Files
nand2tetris/web-ide-main/cli/src/grading.ts
T
2026-04-09 14:14:56 +02:00

80 lines
2.4 KiB
TypeScript

import { FileSystem } from "@davidsouther/jiffies/lib/esm/fs.js";
import { NodeFileSystemAdapter } from "@davidsouther/jiffies/lib/esm/fs_node.js";
import type { Assignment } from "@nand2tetris/projects/base.js";
import { Assignments } from "@nand2tetris/projects/full.js";
import { JavaRunner } from "@nand2tetris/runner/index.js";
import {
AssignmentFiles,
hasTest,
runTests,
} from "@nand2tetris/simulator/projects/runner.js";
import { join, parse } from "path";
/**
* Given a FileSystem wrapper, curry a function that loads the necessary files for running an HDL test.
* For grading, tests come from the built-in assignment master test list.
*/
const loadAssignment = (fs: FileSystem) =>
async function (file: Assignment): Promise<AssignmentFiles> {
const hdl = await fs.readFile(file.base);
const tst = Assignments[
`${file.name}.tst` as keyof typeof Assignments
] as string;
const cmp = Assignments[
`${file.name}.cmp` as keyof typeof Assignments
] as string;
return { ...file, hdl, tst, cmp };
};
/**
* Run the grader using a NodeJS file system.
*
* Report results using a simple `{Name} passed/failed`, and if given a java_id, the same for shadow mode results.
*
* Returns 1 if at least one test was failed or no tests were found to run. Returns 0 otherwise.
*/
export async function main(folder = process.cwd(), java_ide = "") {
const fs = new FileSystem(new NodeFileSystemAdapter());
fs.cd(folder);
const directory = [...(await fs.readdir("."))];
const runFiles = directory.filter((file) => file.endsWith(".hdl"));
const files = runFiles
.map((f) => join(folder, f))
.map(parse)
.filter(hasTest);
const ideRunner = await JavaRunner.try_init(java_ide);
const tests = await runTests(files, loadAssignment(fs), fs, ideRunner);
if (!tests.length) {
console.log("No tests have run!");
return 1;
}
let failsCount = 0;
for (const test of tests) {
if (!test.pass) {
failsCount++;
}
console.log(
`Test ${test.name}: ${test.pass ? `Passed` : `Failed (${test.out})`}`,
);
if (test.shadow) {
if (test.shadow.code !== 0) {
failsCount++;
}
console.log(
`\tShadow: ${
test.shadow.code === 0
? `Passed`
: `Errored (${test.shadow.stderr.trim()})`
}`,
);
} else {
console.log("\tNo shadow");
}
}
return failsCount > 0 ? 1 : 0;
}