mirror of
https://github.com/Noratrieb/riverdelta.git
synced 2026-01-14 16:35:03 +01:00
Add basic testsuite using ui_test
This commit is contained in:
parent
1551847d8c
commit
b68d775671
13 changed files with 1524 additions and 79 deletions
|
|
@ -20,12 +20,20 @@
|
|||
{
|
||||
devShells = forAllSystems ({ pkgs }: {
|
||||
default = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
rustup
|
||||
];
|
||||
packages = with pkgs; [
|
||||
nodejs-18_x # Node.js 18, plus npm, npx, and corepack
|
||||
wasmtime
|
||||
wasm-tools
|
||||
binaryen
|
||||
];
|
||||
|
||||
shellHook = ''
|
||||
export PATH=$PATH:''${CARGO_HOME:-~/.cargo}/bin
|
||||
export PATH=$PATH:''${RUSTUP_HOME:-~/.rustup}/toolchains/$RUSTC_VERSION-x86_64-unknown-linux-gnu/bin/
|
||||
'';
|
||||
};
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
"build": "tsc",
|
||||
"fmt": "prettier -w .",
|
||||
"test": "jest",
|
||||
"ui-test": "npm run build && cargo run --manifest-path tests/Cargo.toml --bin tests",
|
||||
"lint": "eslint ."
|
||||
},
|
||||
"author": "",
|
||||
|
|
|
|||
15
src/error.ts
15
src/error.ts
|
|
@ -11,7 +11,10 @@ export function spanMerge(a: Span, b: Span): Span {
|
|||
}
|
||||
|
||||
export const DUMMY_SPAN = { start: 0, end: 0 };
|
||||
export const EOF_SPAN = {start: Number.MAX_SAFE_INTEGER, end: Number.MAX_SAFE_INTEGER};
|
||||
export const EOF_SPAN = {
|
||||
start: Number.MAX_SAFE_INTEGER,
|
||||
end: Number.MAX_SAFE_INTEGER,
|
||||
};
|
||||
|
||||
export class CompilerError extends Error {
|
||||
msg: string;
|
||||
|
|
@ -24,16 +27,18 @@ export class CompilerError extends Error {
|
|||
}
|
||||
}
|
||||
|
||||
export function withErrorPrinter(
|
||||
export function withErrorPrinter<R>(
|
||||
input: string,
|
||||
filename: string,
|
||||
f: () => void
|
||||
): void {
|
||||
f: () => R,
|
||||
afterError: (e: CompilerError) => R
|
||||
): R {
|
||||
try {
|
||||
f();
|
||||
return f();
|
||||
} catch (e) {
|
||||
if (e instanceof CompilerError) {
|
||||
renderError(input, filename, e);
|
||||
return afterError(e);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
|
|
|
|||
212
src/index.ts
212
src/index.ts
|
|
@ -33,13 +33,26 @@ function linkStd() = (
|
|||
);
|
||||
`;
|
||||
|
||||
function main() {
|
||||
type Config = {
|
||||
input: string;
|
||||
filename: string;
|
||||
packageName: string;
|
||||
debug: Set<string>;
|
||||
noOutput: boolean;
|
||||
};
|
||||
|
||||
function parseArgs(): Config {
|
||||
let filename: string;
|
||||
let input: string;
|
||||
let packageName: string;
|
||||
let debug = new Set<string>();
|
||||
let noOutput = false;
|
||||
|
||||
if (process.argv.length > 2) {
|
||||
filename = process.argv[2];
|
||||
if (path.extname(filename) !== ".nil") {
|
||||
console.error(process.argv);
|
||||
|
||||
console.error(
|
||||
`error: filename must have \`.nil\` extension: \`${filename}\``
|
||||
);
|
||||
|
|
@ -48,12 +61,43 @@ function main() {
|
|||
|
||||
input = fs.readFileSync(filename, { encoding: "utf-8" });
|
||||
packageName = path.basename(filename, ".nil");
|
||||
|
||||
const debugArg = process.argv.find((arg) => arg.startsWith("--debug="));
|
||||
if (debugArg !== undefined) {
|
||||
const debugs = debugArg.slice("--debug=".length);
|
||||
debug = new Set(debugs.split(","));
|
||||
}
|
||||
|
||||
if (process.argv.some((arg) => arg === "--no-output")) {
|
||||
noOutput = true;
|
||||
}
|
||||
} else {
|
||||
filename = "<hardcoded>";
|
||||
input = INPUT;
|
||||
packageName = "test";
|
||||
debug = new Set([
|
||||
"tokens",
|
||||
"parsed",
|
||||
"resolved",
|
||||
"typecked",
|
||||
"wat",
|
||||
"wasm-validate",
|
||||
]);
|
||||
}
|
||||
|
||||
return {
|
||||
filename,
|
||||
input,
|
||||
packageName,
|
||||
debug,
|
||||
noOutput,
|
||||
};
|
||||
}
|
||||
|
||||
function main() {
|
||||
const config = parseArgs();
|
||||
const { filename, packageName, input, debug } = config;
|
||||
|
||||
if (!isValidIdent(packageName)) {
|
||||
console.error(
|
||||
`error: package name \`${packageName}\` is not a valid identifer`
|
||||
|
|
@ -61,62 +105,85 @@ function main() {
|
|||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`package name: '${packageName}'`);
|
||||
withErrorPrinter(
|
||||
input,
|
||||
filename,
|
||||
() => {
|
||||
const start = Date.now();
|
||||
|
||||
withErrorPrinter(input, filename, () => {
|
||||
const start = Date.now();
|
||||
|
||||
const tokens = tokenize(input);
|
||||
console.log("-----TOKENS------------");
|
||||
console.log(tokens);
|
||||
|
||||
const ast: Crate<Built> = parse(packageName, tokens, 0);
|
||||
console.log("-----AST---------------");
|
||||
|
||||
console.dir(ast.rootItems, { depth: 50 });
|
||||
|
||||
console.log("-----AST pretty--------");
|
||||
const printed = printAst(ast);
|
||||
console.log(printed);
|
||||
|
||||
console.log("-----AST resolved------");
|
||||
const [resolved, crates] = resolve(ast, loadCrate);
|
||||
const resolvedPrinted = printAst(resolved);
|
||||
console.log(resolvedPrinted);
|
||||
|
||||
console.log("-----AST typecked------");
|
||||
const typecked: Crate<Typecked> = typeck(resolved, crates);
|
||||
const typeckPrinted = printAst(typecked);
|
||||
console.log(typeckPrinted);
|
||||
|
||||
console.log("-----wasm--------------");
|
||||
const wasmModule = lowerToWasm([typecked, ...crates]);
|
||||
const moduleStringColor = writeModuleWatToString(wasmModule, true);
|
||||
const moduleString = writeModuleWatToString(wasmModule);
|
||||
|
||||
console.log(moduleStringColor);
|
||||
|
||||
fs.writeFileSync("out.wat", moduleString);
|
||||
|
||||
console.log("--validate wasm-tools--");
|
||||
|
||||
exec("wasm-tools validate out.wat", (error, stdout, stderr) => {
|
||||
if (error && error.code === 1) {
|
||||
console.log(stderr);
|
||||
} else if (error) {
|
||||
console.error(`failed to spawn wasm-tools: ${error.message}`);
|
||||
} else {
|
||||
if (stderr) {
|
||||
console.log(stderr);
|
||||
}
|
||||
if (stdout) {
|
||||
console.log(stdout);
|
||||
}
|
||||
const tokens = tokenize(input);
|
||||
if (debug.has("tokens")) {
|
||||
console.log("-----TOKENS------------");
|
||||
console.log(tokens);
|
||||
}
|
||||
|
||||
console.log(`finished in ${Date.now() - start}ms`);
|
||||
});
|
||||
});
|
||||
const ast: Crate<Built> = parse(packageName, tokens, 0);
|
||||
if (debug.has("ast")) {
|
||||
console.log("-----AST---------------");
|
||||
|
||||
console.dir(ast.rootItems, { depth: 50 });
|
||||
|
||||
console.log("-----AST pretty--------");
|
||||
const printed = printAst(ast);
|
||||
console.log(printed);
|
||||
}
|
||||
|
||||
if (debug.has("resolved")) {
|
||||
console.log("-----AST resolved------");
|
||||
}
|
||||
const [resolved, crates] = resolve(ast, loadCrate);
|
||||
if (debug.has("resolved")) {
|
||||
const resolvedPrinted = printAst(resolved);
|
||||
console.log(resolvedPrinted);
|
||||
}
|
||||
|
||||
if (debug.has("typecked")) {
|
||||
console.log("-----AST typecked------");
|
||||
}
|
||||
const typecked: Crate<Typecked> = typeck(resolved, crates);
|
||||
if (debug.has("typecked")) {
|
||||
const typeckPrinted = printAst(typecked);
|
||||
console.log(typeckPrinted);
|
||||
}
|
||||
|
||||
if (debug.has("wat")) {
|
||||
console.log("-----wasm--------------");
|
||||
}
|
||||
const wasmModule = lowerToWasm([typecked, ...crates]);
|
||||
const moduleStringColor = writeModuleWatToString(wasmModule, true);
|
||||
const moduleString = writeModuleWatToString(wasmModule);
|
||||
|
||||
if (debug.has("wat")) {
|
||||
console.log(moduleStringColor);
|
||||
}
|
||||
|
||||
if (!config.noOutput) {
|
||||
fs.writeFileSync("out.wat", moduleString);
|
||||
}
|
||||
|
||||
if (debug.has("wasm-validate")) {
|
||||
console.log("--validate wasm-tools--");
|
||||
|
||||
exec("wasm-tools validate out.wat", (error, stdout, stderr) => {
|
||||
if (error && error.code === 1) {
|
||||
console.log(stderr);
|
||||
} else if (error) {
|
||||
console.error(`failed to spawn wasm-tools: ${error.message}`);
|
||||
} else {
|
||||
if (stderr) {
|
||||
console.log(stderr);
|
||||
}
|
||||
if (stdout) {
|
||||
console.log(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`finished in ${Date.now() - start}ms`);
|
||||
});
|
||||
}
|
||||
},
|
||||
() => process.exit(1)
|
||||
);
|
||||
}
|
||||
|
||||
function loadCrate(
|
||||
|
|
@ -134,7 +201,7 @@ function loadCrate(
|
|||
}
|
||||
|
||||
const filename = `${name}.nil`;
|
||||
let input;
|
||||
let input: string;
|
||||
try {
|
||||
input = fs.readFileSync(filename, { encoding: "utf-8" });
|
||||
} catch (e) {
|
||||
|
|
@ -144,24 +211,25 @@ function loadCrate(
|
|||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const tokens = tokenize(input);
|
||||
const ast = parse(name, tokens, crateId.next());
|
||||
const [resolved, crates] = resolve(ast, loadCrate);
|
||||
console.log(resolved);
|
||||
|
||||
return withErrorPrinter(
|
||||
input,
|
||||
filename,
|
||||
() => {
|
||||
const tokens = tokenize(input);
|
||||
const ast = parse(name, tokens, crateId.next());
|
||||
const [resolved, crates] = resolve(ast, loadCrate);
|
||||
console.log(resolved);
|
||||
|
||||
const typecked = typeck(resolved, [...existingCrates, ...crates]);
|
||||
return [typecked, crates];
|
||||
} catch (e) {
|
||||
withErrorPrinter(input, filename, () => {
|
||||
throw e;
|
||||
});
|
||||
throw new CompilerError(
|
||||
`failed to load crate ${name}: crate contains errors`,
|
||||
span
|
||||
);
|
||||
}
|
||||
const typecked = typeck(resolved, [...existingCrates, ...crates]);
|
||||
return [typecked, crates];
|
||||
},
|
||||
() => {
|
||||
throw new CompilerError(
|
||||
`failed to load crate ${name}: crate contains errors`,
|
||||
span
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
main();
|
||||
|
|
|
|||
|
|
@ -101,8 +101,6 @@ function appendData(cx: Context, newData: Uint8Array): number {
|
|||
});
|
||||
return 0;
|
||||
} else {
|
||||
console.log("appending", newData);
|
||||
|
||||
const data = datas[0];
|
||||
const idx = data.init.length;
|
||||
const init = new Uint8Array(data.init.length + newData.length);
|
||||
|
|
|
|||
1
tests/.gitignore
vendored
Normal file
1
tests/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/target
|
||||
1224
tests/Cargo.lock
generated
Normal file
1224
tests/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
9
tests/Cargo.toml
Normal file
9
tests/Cargo.toml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "tests"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
ui_test = "0.12.1"
|
||||
35
tests/src/bin/nilc-wrapper.rs
Normal file
35
tests/src/bin/nilc-wrapper.rs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
fn main() {
|
||||
let mut args = std::env::args().skip(1).collect::<Vec<_>>();
|
||||
|
||||
let mut after_edition = false;
|
||||
args.retain(|arg| {
|
||||
// drop some stuff we dont care about
|
||||
if arg.starts_with("--crate-type") {
|
||||
return false;
|
||||
}
|
||||
if arg == "--edition" {
|
||||
after_edition = true;
|
||||
return false;
|
||||
}
|
||||
if after_edition {
|
||||
after_edition = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
});
|
||||
|
||||
// We don't want out.wat polluting things.
|
||||
args.push("--no-output".into());
|
||||
|
||||
let result = std::process::Command::new("node")
|
||||
.arg(".")
|
||||
.args(args)
|
||||
.spawn()
|
||||
.unwrap()
|
||||
.wait()
|
||||
.unwrap();
|
||||
|
||||
let code = result.code().unwrap_or(1);
|
||||
std::process::exit(code);
|
||||
}
|
||||
54
tests/src/main.rs
Normal file
54
tests/src/main.rs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
use ui_test::{
|
||||
clap::Parser, default_filter_by_arg, default_per_file_config, status_emitter, Args,
|
||||
CommandBuilder, Config, Mode, OutputConflictHandling,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
std::process::Command::new("cargo")
|
||||
.args(&[
|
||||
"build",
|
||||
"--manifest-path",
|
||||
"tests/Cargo.toml",
|
||||
"--bin",
|
||||
"nilc-wrapper",
|
||||
])
|
||||
.spawn()
|
||||
.unwrap()
|
||||
.wait()
|
||||
.unwrap()
|
||||
.success()
|
||||
.then_some(())
|
||||
.unwrap_or_else(|| std::process::exit(1));
|
||||
|
||||
let mut config = Config::rustc("tests/ui");
|
||||
config.host = Some("wasm :3".into());
|
||||
config.program = CommandBuilder::cmd("tests/target/debug/nilc-wrapper");
|
||||
config.mode = Mode::Fail {
|
||||
require_patterns: false,
|
||||
};
|
||||
|
||||
let args = Args::parse();
|
||||
|
||||
let text = if args.quiet {
|
||||
status_emitter::Text::quiet()
|
||||
} else {
|
||||
status_emitter::Text::verbose()
|
||||
};
|
||||
|
||||
if !args.check && std::env::var_os("GITHUB_ACTIONS").is_none() {
|
||||
config.output_conflict_handling = OutputConflictHandling::Bless;
|
||||
}
|
||||
|
||||
let result = ui_test::run_tests_generic(
|
||||
config,
|
||||
args,
|
||||
|path, args| {
|
||||
path.extension().is_some_and(|ext| ext == "nil") && default_filter_by_arg(path, args)
|
||||
},
|
||||
default_per_file_config,
|
||||
(text, status_emitter::Gha::<true> { name: "ui".into() }),
|
||||
);
|
||||
if let Err(result) = result {
|
||||
println!("{:?}", result);
|
||||
}
|
||||
}
|
||||
19
tests/ui/function_calls_ok.nil
Normal file
19
tests/ui/function_calls_ok.nil
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
//@check-pass
|
||||
|
||||
function main() = (
|
||||
noArgs();
|
||||
singleArg("hi!");
|
||||
manyArgs(1,2,3,4,5,6);
|
||||
|
||||
let a: () = returnNothing();
|
||||
let b: () = returnExplicitUnit();
|
||||
let c: String = returnString();
|
||||
);
|
||||
|
||||
function noArgs() =;
|
||||
function singleArg(a: String) =;
|
||||
function manyArgs(a: Int, b: Int, c: Int, d: Int, e: Int, f: Int) =;
|
||||
|
||||
function returnNothing() =;
|
||||
function returnExplicitUnit(): () =;
|
||||
function returnString(): String = "uwu";
|
||||
2
tests/ui/hello_world.nil
Normal file
2
tests/ui/hello_world.nil
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
//@check-pass
|
||||
function main() = print("Hello, world!\n");
|
||||
21
tests/ui/type_assignments.nil
Normal file
21
tests/ui/type_assignments.nil
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
//@check-pass
|
||||
|
||||
type CustomType = {};
|
||||
|
||||
function main() = ();
|
||||
|
||||
function assigns(
|
||||
a: Int,
|
||||
b: I32,
|
||||
c: String,
|
||||
d: Bool,
|
||||
e: CustomType,
|
||||
f: (Int, I32),
|
||||
) = (
|
||||
let a1: Int = a;
|
||||
let b1: I32 = b;
|
||||
let c1: String = c;
|
||||
let d1: Bool = d;
|
||||
let e1: CustomType = e;
|
||||
let f1: (Int, I32) = f;
|
||||
);
|
||||
Loading…
Add table
Add a link
Reference in a new issue