Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Map property tests + map int limit tests #1343

Merged
merged 24 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
cdf90f7
WIP: test: maps via operation properties
anton-trunov Dec 31, 2024
da06b5c
fix Tact contract template
anton-trunov Jan 2, 2025
12c32b5
feat: generate contracts and test specs from templates
anton-trunov Jan 2, 2025
48ceff9
feat: add Bool and Cell map value types
anton-trunov Jan 3, 2025
e331187
feat: test structs as map values
anton-trunov Jan 4, 2025
8f181a8
feat: add messages as map value types
anton-trunov Jan 4, 2025
dd8533e
cspell ignore
anton-trunov Jan 4, 2025
77661f0
test: add .exists to properties
anton-trunov Jan 5, 2025
a70ab1f
test: isEmpty
anton-trunov Jan 5, 2025
7060c7c
test: deepEquals vs ==
anton-trunov Jan 5, 2025
50d5dbb
test: asCell()
anton-trunov Jan 8, 2025
ac2aa3f
test: .replace() properties
anton-trunov Jan 8, 2025
53e9e8f
format spec.ts.template
anton-trunov Jan 8, 2025
2a573df
test: replaceGet
anton-trunov Jan 8, 2025
dfa0a13
map int key/value limits tests: works but not elegant
anton-trunov Jan 9, 2025
3cbe7ca
harden int limit test against compiler optimizations
anton-trunov Jan 13, 2025
9588346
get rid of `as`
anton-trunov Jan 15, 2025
ea8a472
tests: one `expect` per `it`
anton-trunov Jan 15, 2025
c0de790
refactor package.json scripts
anton-trunov Jan 15, 2025
e19b621
revert knip.json changes
anton-trunov Jan 15, 2025
83da46d
consistent naming of substitutions
anton-trunov Jan 15, 2025
4accc2c
refactor test codegen
anton-trunov Jan 15, 2025
b6957f6
return type for maxInt
anton-trunov Jan 15, 2025
ed1bdca
refactor generate.ts for better readability
anton-trunov Jan 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading