Skip to content

Commit

Permalink
Map property tests + map int limit tests (#1343)
Browse files Browse the repository at this point in the history
  • Loading branch information
anton-trunov authored Jan 15, 2025
1 parent 7fbe9a2 commit 5aaa18d
Show file tree
Hide file tree
Showing 11 changed files with 1,174 additions and 8 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ dist/
coverage/
**/output/
src/test/**/output/
src/test/e2e-emulated/map-tests/build
src/func/funcfiftlib.js
**/grammar.ohm*.ts
**/grammar.ohm*.js
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ output/
**/grammar.ohm-bundle.d.ts
src/func/funcfiftlib.wasm.js
src/test/contracts/pretty-printer-output
src/test/e2e-emulated/map-tests/build

17 changes: 10 additions & 7 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@
"Brujin",
"cleanall",
"codegen",
"comptime",
"Compilables",
"compilables",
"Compilables",
"comptime",
"Daniil",
"decompilation",
"decompile",
Expand All @@ -44,10 +44,10 @@
"elseifnot",
"forall",
"formedness",
"frontmatter",
"funcfiftlib",
"funcid",
"funs",
"frontmatter",
"Georgiy",
"getsimpleforwardfee",
"gettest",
Expand All @@ -56,6 +56,7 @@
"infixl",
"infixr",
"initof",
"Ints",
"ipfs",
"ipld",
"Jesús",
Expand Down Expand Up @@ -109,28 +110,28 @@
"stdlib",
"stmts",
"Ston",
"storer",
"struct",
"structs",
"styleguide",
"subtyping",
"supertypes",
"Tarjan",
"testdata",
"Topup",
"Toncoin",
"Toncoins",
"tonstudio",
"Topup",
"Trunov",
"typechecker",
"uintptr",
"uints",
"unboc",
"uninit",
"unixfs",
"untypable",
"varuint",
"varint",
"storer",
"Ints",
"varuint",
"workchain",
"xffff",
"привет"
Expand Down Expand Up @@ -171,6 +172,8 @@
"src/test/compilation-fail/fail-const-eval.spec.ts",
"src/test/e2e-emulated/getter-names-conflict.spec.ts",
"src/test/exit-codes/contracts/compute-phase-errors.tact",
"src/test/e2e-emulated/map-tests/build",
"src/test/e2e-emulated/map-tests/map-properties-key-value-types.ts",
"stdlib/stdlib.fc",
"/docs"
]
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"gen:pack": "ts-node ./scripts/pack.ts",
"gen:contracts:examples": "ts-node examples/contracts.build.ts",
"gen:contracts:test": "ts-node src/test/contracts.build.ts",
"gen": "yarn gen:grammar && yarn gen:pack && yarn gen:contracts:examples && yarn gen:contracts:test",
"gen:contracts:test:map": "ts-node ./src/test/e2e-emulated/map-tests/generate.ts",
"gen": "yarn gen:grammar && yarn gen:pack && yarn gen:contracts:examples && yarn gen:contracts:test && yarn gen:contracts:test:map",
"clean": "rm -fr dist",
"cleanall": "rm -fr dist node_modules",
"build": "tsc && node --no-warnings=ExperimentalWarning -r ts-node/register ./scripts/copy-files",
Expand Down
146 changes: 146 additions & 0 deletions src/test/e2e-emulated/map-tests/generate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import { run } from "../../../node";
import { keyTypes, valTypes } from "./map-properties-key-value-types";
import { mkdir, writeFile } from "fs/promises";
import path from "path";
import { exit } from "node:process";
import {
descriptionToString,
intKeyFormats,
intValFormats,
maxInt,
minInt,
} from "./map-int-limits-key-value-types";
import { readFile } from "node:fs/promises";

type TestKind = "map-properties" | "map-int-limits";

// template with substitutions that make it an instance after application
type TemplateWithSubst = {
template: string;
subst: Map<string, string>;
};

const pwd = (fileName: string): string => path.join(__dirname, fileName);

const testDirectory = (kind: TestKind, testName: string): string =>
pwd(`./build/${kind}_${testName}`.replaceAll(" ", "-"));

const testContractFileName = "test.tact";

const applySubstitutions = ({ template, subst }: TemplateWithSubst): string => {
return Array.from(subst).reduce(
(partialTemplate, [placeholder, concreteValue]) => {
return partialTemplate.replaceAll(placeholder, concreteValue);
},
template,
);
};

const instantiateContractAndSpecTemplates = async (
testKind: TestKind,
testName: string,
templateTact: TemplateWithSubst,
templateSpec: TemplateWithSubst,
): Promise<string> => {
const testDir = testDirectory(testKind, testName);
const tactSourceCode = applySubstitutions(templateTact);
const specSourceCode = applySubstitutions(templateSpec);
await mkdir(testDir, { recursive: true });
const tactFilePath = path.join(testDir, testContractFileName);
await writeFile(tactFilePath, tactSourceCode);
const specFilePath = path.join(testDir, `${testKind}.spec.ts`);
await writeFile(specFilePath, specSourceCode);
return tactFilePath;
};

const compileAndExitOnError = async (tactFilePath: string) => {
const compilationResult = await run({
fileName: tactFilePath,
suppressLog: true,
});
if (!compilationResult.ok) {
console.error(compilationResult.error);
exit(1);
}
};

const generatePropertyTests = async () => {
const templateTactSourceCodeProperties: string = (
await readFile(pwd("map-properties.tact.template"))
).toString();
const templateSpecSourceCodeProperties: string = (
await readFile(pwd("map-properties.spec.ts.template"))
).toString();
for (const key of keyTypes) {
for (const val of valTypes) {
const testName = `${key.type}_${val.type}`;
const tactFilePath = await instantiateContractAndSpecTemplates(
"map-properties",
testName,
{
template: templateTactSourceCodeProperties,
subst: new Map([
["KEY_TYPE_PLACEHOLDER", key.type],
["VAL_TYPE_PLACEHOLDER", val.type],
]),
},
{
template: templateSpecSourceCodeProperties,
subst: new Map([
["KEY_1_PLACEHOLDER", key._1],
["KEY_2_PLACEHOLDER", key._2],
["VAL_1_PLACEHOLDER", val._1],
["VAL_2_PLACEHOLDER", val._2],
]),
},
);
await compileAndExitOnError(tactFilePath);
}
}
};

const generateIntLimitsTests = async () => {
const templateTactSourceCodeLimits: string = (
await readFile(pwd("map-int-limits.tact.template"))
).toString();
const templateSpecSourceCodeLimits: string = (
await readFile(pwd("map-int-limits.spec.ts.template"))
).toString();
for (const key of intKeyFormats) {
for (const val of intValFormats) {
const testName = `${descriptionToString(key)}_${descriptionToString(val)}`;
const tactFilePath = await instantiateContractAndSpecTemplates(
"map-int-limits",
testName,
{
template: templateTactSourceCodeLimits,
subst: new Map([
["KEY_FORMAT_PLACEHOLDER", descriptionToString(key)],
["VAL_FORMAT_PLACEHOLDER", descriptionToString(val)],
["KEY_MIN_PLACEHOLDER", minInt(key).toString()],
["KEY_MAX_PLACEHOLDER", maxInt(key).toString()],
["VAL_MIN_PLACEHOLDER", minInt(val).toString()],
["VAL_MAX_PLACEHOLDER", maxInt(val).toString()],
]),
},
{
template: templateSpecSourceCodeLimits,
subst: new Map(),
},
);
await compileAndExitOnError(tactFilePath);
}
}
};

const main = async () => {
try {
await generatePropertyTests();
await generateIntLimitsTests();
} catch (e) {
console.error(e);
process.exit(1);
}
};

void main();
113 changes: 113 additions & 0 deletions src/test/e2e-emulated/map-tests/map-int-limits-key-value-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
type FixedWidthFormat = "int" | "uint";
type VarWidthFormat = "varint" | "varuint";

type MapIntKeyDescription =
| {
format: FixedWidthFormat;
size: number;
}
| { format: null };

type MapIntValDescription =
| MapIntKeyDescription
| {
format: VarWidthFormat;
size: 16 | 32;
}
| { format: "coins" };

const minSignedInt = (nBits: number): bigint => -(2n ** (BigInt(nBits) - 1n));

const maxSignedInt = (nBits: number): bigint => 2n ** (BigInt(nBits) - 1n) - 1n;

const minUnsignedInt = (_nBits: number): bigint => 0n;

const maxUnsignedInt = (nBits: number): bigint => 2n ** BigInt(nBits) - 1n;

const minVarInt = (size: number): bigint => minSignedInt(8 * (size - 1));

const maxVarInt = (size: number): bigint => maxSignedInt(8 * (size - 1));

const minVarUInt = (_size: number): bigint => 0n;

const maxVarUInt = (size: number): bigint => maxUnsignedInt(8 * (size - 1));

export const minInt = (descr: MapIntValDescription): bigint => {
switch (descr.format) {
case null:
return minSignedInt(257);
case "int":
return minSignedInt(descr.size);
case "uint":
return minUnsignedInt(descr.size);
case "varint":
return minVarInt(descr.size);
case "varuint":
return minVarUInt(descr.size);
case "coins":
return minVarUInt(16);
}
};

export const maxInt = (descr: MapIntValDescription): bigint => {
switch (descr.format) {
case null:
return maxSignedInt(257);
case "int":
return maxSignedInt(descr.size);
case "uint":
return maxUnsignedInt(descr.size);
case "varint":
return maxVarInt(descr.size);
case "varuint":
return maxVarUInt(descr.size);
case "coins":
return maxVarUInt(16);
}
};

export const descriptionToString = (descr: MapIntValDescription): string => {
switch (descr.format) {
case null:
return "Int";
case "int":
case "uint":
case "varint":
case "varuint":
return `Int as ${descr.format}${descr.size}`;
case "coins":
return "Int as coins";
}
};

const signedIntFormats: MapIntKeyDescription[] = [
{ format: "int", size: 2 },
{ format: "int", size: 10 },
{ format: "int", size: 37 },
{ format: "int", size: 256 },
{ format: "int", size: 257 },
{ format: null },
];

const unsignedIntFormats: MapIntKeyDescription[] = [
{ format: "uint", size: 2 },
{ format: "uint", size: 8 },
{ format: "uint", size: 32 },
{ format: "uint", size: 256 },
];

const varIntFormats: MapIntValDescription[] = [
{ format: "varint", size: 16 },
{ format: "varint", size: 32 },
{ format: "varuint", size: 16 },
{ format: "varuint", size: 32 },
{ format: "coins" },
];

const fixedWidthInts: MapIntKeyDescription[] =
signedIntFormats.concat(unsignedIntFormats);

export const intKeyFormats: MapIntKeyDescription[] = fixedWidthInts;

export const intValFormats: MapIntValDescription[] =
varIntFormats.concat(fixedWidthInts);
Loading

0 comments on commit 5aaa18d

Please sign in to comment.