Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,25 @@ all relevant ones can be easily checked out from [our test vectors repository](h
Obviously it's also possible to run just single test case or part of the test
cases by altering the glob pattern in the path.

#### Selecting PVM Backend

By default, test vectors are run with both PVM backends (built-in and Ananas).
You can select a specific PVM backend using the `--pvm` option:

```bash
# Run tests with built-in PVM only
$ npm run w3f-davxy:0.7.1 -w @typeberry/test-runner -- --pvm builtin

# Run tests with Ananas PVM only
$ npm run w3f-davxy:0.7.1 -w @typeberry/test-runner -- --pvm ananas

# Run tests with both PVMs (default)
$ npm run w3f-davxy:0.7.1 -w @typeberry/test-runner
```

This option is useful for debugging PVM-specific issues or running faster tests
by testing only one implementation at a time.

### Running JSON RPC E2E tests

To run JSON RPC E2E test-vectors [test-vectors](https://github.com/fluffylabs/test-vectors)
Expand Down
41 changes: 41 additions & 0 deletions bin/test-runner/common.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import assert from "node:assert";
import { describe, it } from "node:test";
import { deepEqual } from "@typeberry/utils";
import { parseArgs, SelectedPvm } from "./common.js";

describe("test runner common", () => {
it("should parse pvm argument", () => {
const args = ["--pvm", "ananas", "file1.json", "file2.json"];

const result = parseArgs(args);

deepEqual(result, {
initialFiles: ["file1.json", "file2.json"],
pvms: [SelectedPvm.Ananas],
});
});

it("should have both pvms by default", () => {
const args = ["file1.json", "file2.json"];

const result = parseArgs(args);

deepEqual(result, {
initialFiles: ["file1.json", "file2.json"],
pvms: [SelectedPvm.Ananas, SelectedPvm.Builtin],
});
});

it("should throw on invalid pvm", () => {
const args = ["--pvm=invalid", "file1.json", "file2.json"];

assert.throws(
() => {
const _result = parseArgs(args);
},
{
message: "Unknown pvm value: invalid. Use one of ananas, builtin.",
},
);
});
});
77 changes: 70 additions & 7 deletions bin/test-runner/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,36 @@ import path from "node:path";
import test, { type TestContext } from "node:test";
import util from "node:util";
import { type Decode, Decoder } from "@typeberry/codec";
import { type ChainSpec, tinyChainSpec } from "@typeberry/config";
import { type ChainSpec, PvmBackend, tinyChainSpec } from "@typeberry/config";
import { initWasm } from "@typeberry/crypto";
import { type FromJson, parseFromJson } from "@typeberry/json-parser";
import { Level, Logger } from "@typeberry/logger";
import { assertNever } from "@typeberry/utils";
import minimist from "minimist";

Logger.configureAll(process.env.JAM_LOG ?? "", Level.LOG);
export const logger = Logger.new(import.meta.filename, "test-runner");

export enum SelectedPvm {
Ananas = "ananas",
Builtin = "builtin",
}
export const ALL_PVMS = [SelectedPvm.Ananas, SelectedPvm.Builtin];
export function selectedPvmToBackend(pvm: SelectedPvm): PvmBackend {
switch (pvm) {
case SelectedPvm.Ananas:
return PvmBackend.Ananas;
case SelectedPvm.Builtin:
return PvmBackend.BuiltIn;
default:
assertNever(pvm);
}
}

export type GlobalsOptions = {
pvms: SelectedPvm[];
};

export class RunnerBuilder<T, V> implements Runner<T, V> {
public readonly parsers: testFile.Kind<T>[] = [];
public readonly variants: V[] = [];
Expand Down Expand Up @@ -112,22 +134,56 @@ export namespace testFile {
};
}

export function parseArgs(argv: string[]) {
const PVM_OPTION = "pvm";
const parsed = minimist(argv);
const pvms = getPvms(parsed[PVM_OPTION]);

return {
initialFiles: parsed._,
pvms,
};

function getPvms(parsed: string | undefined): SelectedPvm[] {
const allPvms = ALL_PVMS.slice();

if (parsed === undefined) {
return allPvms;
}

const opts = parsed.split(",").map((x) => x.trim());
const result: SelectedPvm[] = [];
for (const o of opts) {
const idx = allPvms.indexOf(o as SelectedPvm);
if (idx !== -1) {
result.push(allPvms[idx]);
} else {
throw new Error(`Unknown pvm value: ${o}. Use one of ${allPvms.join(", ")}.`);
}
}
return result;
}
}
Comment thread
tomusdrw marked this conversation as resolved.

export async function main(
runners: Runner<unknown, unknown>[],
initialFiles: string[],
directoryToScan: string,
{
initialFiles,
pvms,
patterns = [testFile.bin, testFile.json],
accepted,
ignored,
}: {
initialFiles: string[];
pvms: SelectedPvm[];
patterns?: (testFile.bin | testFile.json)[];
accepted?: {
[testFile.bin]?: string[];
[testFile.json]?: string[];
};
ignored?: string[];
} = {},
},
) {
await initWasm();
const relPath = `${import.meta.dirname}/../..`;
Expand Down Expand Up @@ -177,7 +233,7 @@ export async function main(
// 3. If the list is defined, we make sure that the path is on that list.
(accepted[testFileContent.kind] ?? []).some((x) => absolutePath.includes(x));

const testVariants = prepareTest(runners, testFileContent, testFilePath, absolutePath);
const testVariants = prepareTest(runners, testFileContent, testFilePath, absolutePath, { pvms });
for (const test of testVariants) {
test.shouldSkip = !isAccepted;
tests.push(test);
Expand Down Expand Up @@ -258,6 +314,7 @@ function prepareTest<T, V>(
testContent: testFile.Content,
fileName: string,
fullPath: string,
globalOptions: GlobalsOptions,
): TestAndRunner<V>[] {
const errors: [string, unknown][] = [];
const handleError = (name: string, e: unknown) => errors.push([name, e]);
Expand Down Expand Up @@ -286,7 +343,7 @@ function prepareTest<T, V>(
if (parser.kind === testFile.bin && testContent.kind === testFile.bin) {
try {
const parsedTest = Decoder.decodeObject(parser.codec, testContent.content, chainSpec);
matchingRunners.push(...createTestDefinitions(path, run, variants, parsedTest, chainSpec));
matchingRunners.push(...createTestDefinitions(path, run, variants, parsedTest, chainSpec, globalOptions));
} catch (e) {
handleError(path, e);
}
Expand All @@ -295,7 +352,7 @@ function prepareTest<T, V>(
if (parser.kind === testFile.json && testContent.kind === testFile.json) {
try {
const parsedTest = parseFromJson(testContent.content, parser.fromJson);
matchingRunners.push(...createTestDefinitions(path, run, variants, parsedTest, chainSpec));
matchingRunners.push(...createTestDefinitions(path, run, variants, parsedTest, chainSpec, globalOptions));
} catch (e) {
handleError(path, e);
}
Expand Down Expand Up @@ -330,9 +387,15 @@ function prepareTest<T, V>(
variants: V[],
parsedTest: T,
chainSpec: ChainSpec,
globalOptions: GlobalsOptions,
) {
const results: TestAndRunner<V>[] = [];
const possibleVariants: V[] = variants.length === 0 ? [noneVariant] : variants;
let possibleVariants: V[] = variants.length === 0 ? [noneVariant] : variants;
// a bit hacky way to detect pvm-variants and filtering.
const idx = ALL_PVMS.indexOf(possibleVariants[0] as SelectedPvm);
if (idx !== -1) {
possibleVariants = possibleVariants.filter((x) => globalOptions.pvms.includes(x as SelectedPvm));
}

for (const variant of possibleVariants) {
results.push({
Expand Down
5 changes: 3 additions & 2 deletions bin/test-runner/jam-conformance-070.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { logger, main } from "./common.js";
import { logger, main, parseArgs } from "./common.js";
import { runners } from "./w3f/runners.js";

main(runners, process.argv.slice(2), "test-vectors/jam-conformance/fuzz-reports/0.7.0/traces", {
main(runners, "test-vectors/jam-conformance/fuzz-reports/0.7.0/traces", {
...parseArgs(process.argv.slice(2)),
patterns: [".json"],
ignored: [
// CORRECT: note [seko] test rejected at block parsing stage, which is considered valid behavior
Expand Down
5 changes: 3 additions & 2 deletions bin/test-runner/jam-conformance-071.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { logger, main } from "./common.js";
import { logger, main, parseArgs } from "./common.js";
import { runners } from "./w3f/runners.js";

main(runners, process.argv.slice(2), "test-vectors/jam-conformance/fuzz-reports/0.7.1/traces", {
main(runners, "test-vectors/jam-conformance/fuzz-reports/0.7.1/traces", {
...parseArgs(process.argv.slice(2)),
patterns: [".json"],
ignored: [
// genesis file is unparsable
Expand Down
7 changes: 4 additions & 3 deletions bin/test-runner/jamduna-067.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { StateTransition } from "@typeberry/state-vectors";
import { logger, main, runner } from "./common.js";
import { logger, main, parseArgs, runner, SelectedPvm } from "./common.js";
import { runStateTransition } from "./state-transition/state-transition.js";

const runners = [
runner("state_transition", runStateTransition)
.fromJson(StateTransition.fromJson)
.fromBin(StateTransition.Codec)
.withVariants(["ananas", "builtin"]),
.withVariants([SelectedPvm.Ananas, SelectedPvm.Builtin]),
].map((x) => x.build());

main(runners, process.argv.slice(2), "test-vectors/jamduna_067", {
main(runners, "test-vectors/jamduna_067", {
...parseArgs(process.argv.slice(2)),
patterns: [".json"],
accepted: {
".json": ["traces/"],
Expand Down
7 changes: 4 additions & 3 deletions bin/test-runner/javajam.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { StateTransition } from "@typeberry/state-vectors";
import { logger, main, runner } from "./common.js";
import { logger, main, parseArgs, runner, SelectedPvm } from "./common.js";
import { runStateTransition } from "./state-transition/state-transition.js";

const runners = [
runner("state_transition", runStateTransition)
.fromJson(StateTransition.fromJson)
.fromBin(StateTransition.Codec)
.withVariants(["ananas", "builtin"]),
.withVariants([SelectedPvm.Ananas, SelectedPvm.Builtin]),
].map((x) => x.build());

main(runners, process.argv.slice(2), "test-vectors/javajam", {
main(runners, "test-vectors/javajam", {
...parseArgs(process.argv.slice(2)),
patterns: [".json"],
accepted: {
".json": ["stf/state_transitions/"],
Expand Down
3 changes: 2 additions & 1 deletion bin/test-runner/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"@typeberry/transition": "*",
"@typeberry/trie": "*",
"@typeberry/utils": "*",
"json-bigint-patch": "0.0.8"
"json-bigint-patch": "0.0.8",
"minimist": "^1.2.8"
},
"scripts": {
"start": "tsx --test-timeout=900000 ./index.ts",
Expand Down
12 changes: 4 additions & 8 deletions bin/test-runner/state-transition/state-transition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import fs from "node:fs";
import path from "node:path";
import { Block, emptyBlock } from "@typeberry/block";
import { Decoder, Encoder } from "@typeberry/codec";
import { ChainSpec, PvmBackend, tinyChainSpec } from "@typeberry/config";
import { ChainSpec, tinyChainSpec } from "@typeberry/config";
import { InMemoryBlocks } from "@typeberry/database";
import { Blake2b, keccak, WithHash } from "@typeberry/hash";
import { tryAsU32 } from "@typeberry/numbers";
Expand All @@ -13,7 +13,7 @@ import { TransitionHasher } from "@typeberry/transition";
import { BlockVerifier } from "@typeberry/transition/block-verifier.js";
import { OnChain } from "@typeberry/transition/chain-stf.js";
import { deepEqual, resultToString } from "@typeberry/utils";
import type { RunOptions } from "../common.js";
import { type RunOptions, type SelectedPvm, selectedPvmToBackend } from "../common.js";
import { loadState } from "./state-loader.js";

const keccakHasher = keccak.KeccakHasher.create();
Expand Down Expand Up @@ -65,12 +65,8 @@ const jamConformance070V0Spec = new ChainSpec({
maxLookupAnchorAge: tryAsU32(14_400),
});

export async function runStateTransition(
testContent: StateTransition,
options: RunOptions,
variant: "ananas" | "builtin",
) {
const pvm = variant === "ananas" ? PvmBackend.Ananas : PvmBackend.BuiltIn;
export async function runStateTransition(testContent: StateTransition, options: RunOptions, variant: SelectedPvm) {
const pvm = selectedPvmToBackend(variant);
const blake2b = await Blake2b.createHasher();
// a bit of a hack, but the new value for `maxLookupAnchorAge` was proposed with V1
// version of the fuzzer, yet these tests were still depending on the older value.
Expand Down
5 changes: 3 additions & 2 deletions bin/test-runner/w3f-davxy-067.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { logger, main } from "./common.js";
import { logger, main, parseArgs } from "./common.js";
import { runners } from "./w3f/runners.js";

main(runners, process.argv.slice(2), "test-vectors/w3f-davxy_067", {
main(runners, "test-vectors/w3f-davxy_067", {
...parseArgs(process.argv.slice(2)),
patterns: [".json"],
accepted: {
".json": ["traces"],
Expand Down
5 changes: 3 additions & 2 deletions bin/test-runner/w3f-davxy-070.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { logger, main } from "./common.js";
import { logger, main, parseArgs } from "./common.js";
import { runners } from "./w3f/runners.js";

main(runners, process.argv.slice(2), "test-vectors/w3f-davxy_070", {
main(runners, "test-vectors/w3f-davxy_070", {
...parseArgs(process.argv.slice(2)),
patterns: [".json"],
accepted: {
".json": ["traces"],
Expand Down
5 changes: 3 additions & 2 deletions bin/test-runner/w3f-davxy-071.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { logger, main } from "./common.js";
import { logger, main, parseArgs } from "./common.js";
import { runners } from "./w3f/runners.js";

main(runners, process.argv.slice(2), "test-vectors/w3f-davxy_071", {
main(runners, "test-vectors/w3f-davxy_071", {
...parseArgs(process.argv.slice(2)),
patterns: [".json"],
accepted: {
".json": ["traces", "codec", "stf"],
Expand Down
5 changes: 3 additions & 2 deletions bin/test-runner/w3f.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { logger, main } from "./common.js";
import { logger, main, parseArgs } from "./common.js";
import { runners } from "./w3f/runners.js";

main(runners, process.argv.slice(2), "test-vectors/w3f-fluffy", {
main(runners, "test-vectors/w3f-fluffy", {
...parseArgs(process.argv.slice(2)),
patterns: [".json"],
ignored: [
"genesis.json",
Expand Down
4 changes: 2 additions & 2 deletions bin/test-runner/w3f/runners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
} from "@typeberry/block-json";
import { fullChainSpec, tinyChainSpec } from "@typeberry/config";
import { StateTransition } from "@typeberry/state-vectors";
import { runner } from "../common.js";
import { runner, SelectedPvm } from "../common.js";
import { runStateTransition } from "../state-transition/state-transition.js";
import { AccumulateTest, runAccumulateTest } from "./accumulate.js";
import { AssurancesTestFull, AssurancesTestTiny, runAssurancesTestFull, runAssurancesTestTiny } from "./assurances.js";
Expand Down Expand Up @@ -46,7 +46,7 @@ import { runShufflingTests, shufflingTestsFromJson } from "./shuffling.js";
import { runStatisticsTestFull, runStatisticsTestTiny, StatisticsTestFull, StatisticsTestTiny } from "./statistics.js";
import { runTrieTest, trieTestSuiteFromJson } from "./trie.js";

const pvms: ("ananas" | "builtin")[] = ["ananas", "builtin"];
const pvms: SelectedPvm[] = [SelectedPvm.Ananas, SelectedPvm.Builtin];
const tiny = [tinyChainSpec];
const full = [fullChainSpec];
const tinyFull = [...tiny, ...full];
Expand Down
3 changes: 2 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading