Better testing infrastructure; Initial exports/imports/re-exports

This commit is contained in:
dcodeIO
2017-12-02 01:14:15 +01:00
parent 00303fdf30
commit ef859937a8
34 changed files with 713 additions and 248 deletions

29
tests/README.md Normal file
View File

@@ -0,0 +1,29 @@
Parser
------
Tests consist of a test case that is first parsed and then serialized again. The output is then compared to its respective fixture.
```
$> npm run test:parser [case name]
```
To recreate the fixtures:
```
$>npm run test:parser -- --create
```
Compiler
--------
Tests consist of a test case that is compiled to a module, converted to text format and then compared to its respective fixture.
```
$> npm run test:compiler [case name]
```
To recreate the fixtures:
```
$>npm run test:compiler -- --create
```

View File

@@ -1,6 +1,7 @@
/// <reference path="../src/binaryen.d.ts" />
/// <reference path="../src/glue/binaryen.d.ts" />
import "../src/glue/js";
import { Type, Module, MemorySegment, BinaryOp } from "../src/binaryen";
import { NativeType, Module, MemorySegment, BinaryOp } from "../src/module";
import { Target } from "../src/compiler";
import { U64 } from "../src/util";
@@ -11,33 +12,33 @@ mod.setMemory(1, Module.MAX_MEMORY_WASM32, [
MemorySegment.create(new Uint8Array(4), U64.fromI32(12))
], Target.WASM32, "memory");
const add = mod.addFunctionType("iii", Type.I32, [ Type.I32, Type.I32 ]);
const add = mod.addFunctionType("iii", NativeType.I32, [ NativeType.I32, NativeType.I32 ]);
mod.addFunction("add", add, [], mod.createReturn(
mod.createBinary(BinaryOp.AddI32,
mod.createGetLocal(0, Type.I32),
mod.createGetLocal(1, Type.I32)
mod.createGetLocal(0, NativeType.I32),
mod.createGetLocal(1, NativeType.I32)
)
));
mod.addExport("add", "add");
const lit = mod.addFunctionType("I", Type.I64, []);
const lit = mod.addFunctionType("I", NativeType.I64, []);
mod.addFunction("lit", lit, [], mod.createReturn(
mod.createI64(0, 0x80000000) // I64_MIN
));
mod.addExport("lit", "lit");
mod.addGlobal("42", Type.I32, false, mod.createI32(42));
mod.addGlobal("42", NativeType.I32, false, mod.createI32(42));
const aSwitch = mod.addFunctionType("ii", Type.I32, [ Type.I32 ]);
const aSwitch = mod.addFunctionType("ii", NativeType.I32, [ NativeType.I32 ]);
const rl = mod.createRelooper();
const b0 = rl.addBlockWithSwitch(mod.createNop(), mod.createGetLocal(0, Type.I32));
const b0 = rl.addBlockWithSwitch(mod.createNop(), mod.createGetLocal(0, NativeType.I32));
let b1, b2, b3;
rl.addBranchForSwitch(b0, b2 = rl.addBlock(mod.createReturn(mod.createI32(1))), [1]); // indexed branch
rl.addBranchForSwitch(b0, b3 = rl.addBlock(mod.createReturn(mod.createI32(2))), [2]); // indexed branch
rl.addBranch(b0, b1 = rl.addBlock(mod.createDrop(mod.createI32(0)))); // default branch
rl.addBranch(b1, b2);
mod.addFunction("aSwitch", aSwitch, [ Type.I32 ], mod.createBlock(null, [
mod.addFunction("aSwitch", aSwitch, [ NativeType.I32 ], mod.createBlock(null, [
rl.renderAndDispose(b0, 1),
mod.createUnreachable()
]));

View File

@@ -1,72 +1,74 @@
/// <reference path="../src/glue/binaryen.d.ts" />
import * as fs from "fs";
import * as path from "path";
import * as chalk from "chalk";
import * as glob from "glob";
import "../src/glue/js";
import { Compiler } from "../src/compiler";
import { Module } from "../src/module";
import { Parser } from "../src/parser";
import { diff } from "./util/diff";
/* const files: Map<string,string> = new Map([
["main", `import { Test as TestAlias } from "./a"; export { TestAlias } from "./d"; if (1) {} export const a: i32 = 123;`],
["a", `export { Test } from "./b";`],
["b", `export { Test } from "./c";`],
["c", `export enum Test { ONE = 1, TWO = 1 + 1 }`],
["d", `export { Test as TestAlias } from "./b";`]
]); */
// TODO: implement properly in module.ts
import * as binaryen from "binaryen";
Module.prototype.toText = function(): string {
let old: any = (<any>binaryen)["print"];
let ret: string = "";
(<any>binaryen)["print"] = function(x: string): void { ret += x + "\n" };
_BinaryenModulePrint(this.ref);
(<any>binaryen)["print"] = old;
return ret;
}
const files: Map<string,string> = new Map([
["main",
`
function add(a: i32, b: i32): i32 { return a + b; };
export { add };
export { sub as notadd } from "../other";
2+3;
export function switchMe(n: i32): i32 {
switch (n) {
case 0:
return 0;
default:
return 2;
case 1:
return 1;
case -1:
break;
const isCreate = process.argv[2] === "--create";
const filter = process.argv.length > 2 && !isCreate ? "*" + process.argv[2] + "*.ts" : "*.ts";
glob.sync(filter, { cwd: __dirname + "/compiler" }).forEach(filename => {
if (filename.charAt(0) == "_" || filename.endsWith(".fixture.ts"))
return;
console.log("Testing compiler/" + filename);
const parser = new Parser();
const sourceText = fs.readFileSync(__dirname + "/compiler/" + filename, { encoding: "utf8" });
parser.parseFile(sourceText, filename, true);
let nextFile;
while ((nextFile = parser.nextFile()) !== null) {
let nextSourceText: string;
try {
nextSourceText = fs.readFileSync(path.join(__dirname, "compiler", nextFile + ".ts"), { encoding: "utf8" });
} catch (e) {
nextSourceText = fs.readFileSync(path.join(__dirname, "compiler", nextFile, "index.ts"), { encoding: "utf8" });
}
return -1;
parser.parseFile(nextSourceText, nextFile, false);
}
import { sub } from "../other";
export function doCall(): void {
sub(1,2);
const program = parser.finish();
const module = Compiler.compile(program);
const actual = module.toText() + "(;\n[program.elements]\n " + iterate(program.elements.keys()).join("\n ") + "\n[program.exports]\n " + iterate(program.exports.keys()).join("\n ") + "\n;)\n";
const fixture = path.basename(filename, ".ts") + ".wast";
if (isCreate) {
fs.writeFileSync(__dirname + "/compiler/" + fixture, actual, { encoding: "utf8" });
console.log("Created\n");
} else {
const expected = fs.readFileSync(__dirname + "/compiler/" + fixture, { encoding: "utf8" });
const diffs = diff("compiler/" + fixture, expected, actual);
if (diffs !== null) {
process.exitCode = 1;
console.log(diffs);
} else {
console.log("No changes\n");
}
}
export function doNaN(value: f32): bool {
return isNaN<f32>(0.3);
});
function iterate<T>(it: IterableIterator<T>): T[] {
let current: IteratorResult<T>;
var arr: T[] = [];
while ((current = it.next()) && !current.done) {
arr.push(current.value);
}
export function doRotl(value: u16): u8 {
return rotl<i32>(value, 2);
}
`],
["../other",
`
export function sub(a: i32, b: i32): i32 { return a - b + c; };
let c: i32 = 42 >> 31;
1+2;
`]
]);
const parser = new Parser();
parser.parseFile(<string>files.get("main"), "main", true);
do {
let nextFile = parser.nextFile();
if (!nextFile)
break;
if (!files.has(nextFile))
throw new Error("file not found: " + nextFile);
parser.parseFile(<string>files.get(nextFile), nextFile, false);
} while(true);
const program = parser.finish();
const compiler = new Compiler(program);
const module = compiler.compile();
// console.log(program.elements.keys());
// module.optimize();
module.validate();
if (!module.noEmit)
_BinaryenModulePrint(module.ref);
return arr;
}

15
tests/compiler/export.ts Normal file
View File

@@ -0,0 +1,15 @@
export function add(a: i32, b: i32): i32 {
return a + b;
}
function sub(a: i32, b: i32): i32 {
return a - b;
}
export { sub as renamed_sub };
export let a: i32 = 1;
let b: i32 = 2;
export { b as renamed_b };

View File

@@ -0,0 +1,52 @@
(module
(type $iii (func (param i32 i32) (result i32)))
(global $export/a i32 (i32.const 1))
(global $export/b i32 (i32.const 2))
(memory $0 1)
(data (i32.const 4) "\08\00\00\00")
(export "memory" (memory $0))
(func $export/add (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(return
(i32.add
(get_local $0)
(get_local $1)
)
)
)
(func $export/sub (; 1 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(return
(i32.sub
(get_local $0)
(get_local $1)
)
)
)
)
(;
[program.elements]
clz
ctz
popcnt
rotl
rotr
abs
ceil
copysign
floor
max
min
nearest
sqrt
trunc
isNaN
isFinite
export/add
export/sub
export/a
export/b
[program.exports]
export/add
export/renamed_sub
export/a
export/renamed_b
;)

3
tests/compiler/import.ts Normal file
View File

@@ -0,0 +1,3 @@
import { add, renamed_sub as sub, a, renamed_b as b } from "./export";
add(a, b) + sub(b, a);

View File

@@ -0,0 +1,72 @@
(module
(type $iii (func (param i32 i32) (result i32)))
(type $v (func))
(global $export/a i32 (i32.const 1))
(global $export/b i32 (i32.const 2))
(memory $0 1)
(data (i32.const 4) "\08\00\00\00")
(export "memory" (memory $0))
(start $start)
(func $export/add (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(return
(i32.add
(get_local $0)
(get_local $1)
)
)
)
(func $export/sub (; 1 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(return
(i32.sub
(get_local $0)
(get_local $1)
)
)
)
(func $start (; 2 ;) (type $v)
(drop
(i32.add
(call $export/add
(get_global $export/a)
(get_global $export/b)
)
(call $export/sub
(get_global $export/b)
(get_global $export/a)
)
)
)
)
)
(;
[program.elements]
clz
ctz
popcnt
rotl
rotr
abs
ceil
copysign
floor
max
min
nearest
sqrt
trunc
isNaN
isFinite
export/add
export/sub
export/a
export/b
import/add
import/sub
import/a
import/b
[program.exports]
export/add
export/renamed_sub
export/a
export/renamed_b
;)

View File

@@ -0,0 +1,7 @@
export { add, renamed_sub } from "./export";
import { add as imported_add, renamed_sub as imported_sub } from "./export";
export { imported_add as renamed_add, imported_sub as rerenamed_sub };
imported_add(1, 2) + imported_sub(3, 4);

View File

@@ -0,0 +1,72 @@
(module
(type $iii (func (param i32 i32) (result i32)))
(type $v (func))
(memory $0 1)
(data (i32.const 4) "\08\00\00\00")
(export "memory" (memory $0))
(start $start)
(func $export/add (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(return
(i32.add
(get_local $0)
(get_local $1)
)
)
)
(func $export/sub (; 1 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(return
(i32.sub
(get_local $0)
(get_local $1)
)
)
)
(func $start (; 2 ;) (type $v)
(drop
(i32.add
(call $export/add
(i32.const 1)
(i32.const 2)
)
(call $export/sub
(i32.const 3)
(i32.const 4)
)
)
)
)
)
(;
[program.elements]
clz
ctz
popcnt
rotl
rotr
abs
ceil
copysign
floor
max
min
nearest
sqrt
trunc
isNaN
isFinite
export/add
export/sub
export/a
export/b
reexport/imported_add
reexport/imported_sub
[program.exports]
export/add
export/renamed_sub
export/a
export/renamed_b
reexport/add
reexport/renamed_sub
reexport/renamed_add
reexport/rerenamed_sub
;)

40
tests/parser.ts Normal file
View File

@@ -0,0 +1,40 @@
import * as fs from "fs";
import * as chalk from "chalk";
import * as glob from "glob";
import "../src/glue/js";
import { Parser } from "../src/parser";
import { diff } from "./util/diff";
const isCreate = process.argv[2] === "--create";
const filter = process.argv.length > 2 && !isCreate ? "*" + process.argv[2] + "*.ts" : "**.ts";
glob.sync(filter, { cwd: __dirname + "/parser" }).forEach(filename => {
if (filename.charAt(0) == "_" || filename.endsWith(".fixture.ts"))
return;
console.log("Testing parser/" + filename);
const parser = new Parser();
const sourceText = fs.readFileSync(__dirname + "/parser/" + filename, { encoding: "utf8" }).replace(/\r?\n/g, "\n").replace(/^\/\/.*\r?\n/mg, "");
parser.parseFile(sourceText, filename, true);
var sb: string[] = [];
parser.program.sources[0].serialize(sb);
const actual = sb.join("");
const fixture = filename + ".fixture.ts";
if (isCreate) {
fs.writeFileSync(__dirname + "/parser/" + fixture, actual, { encoding: "utf8" });
console.log("Created\n");
} else {
const expected = fs.readFileSync(__dirname + "/parser/" + fixture, { encoding: "utf8" });
const diffs = diff("parser/" + fixture, expected, actual);
if (diffs !== null) {
process.exitCode = 1;
console.log(diffs);
} else {
console.log("No changes\n");
}
}
});

View File

@@ -0,0 +1,12 @@
export class Test<T> {
instanceFunction(): void {
}
static staticFunction(): void {
}
get instanceGetter(): i32 {
}
static set staticSetter(v: i32): i32 {
}
instanceField: i32;
static staticField: i32;
}

View File

@@ -0,0 +1,3 @@
do {
;
} while (a != b);

View File

@@ -0,0 +1,10 @@
export const enum A {
B = 1,
C,
D = 3
}
enum E {
F,
G = 1 + 2,
H = 3 * 4
}

View File

@@ -1,42 +0,0 @@
0;
1;
2;
3;
4;
5;
6;
7;
8;
9;
// 0x0;
// 0x1;
// 0x2;
// 0x3;
// 0x4;
// 0x5;
// 0x6;
// 0x7;
// 0x8;
// 0x9;
// 0xA;
// 0xB;
// 0xC;
// 0xD;
// 0xE;
// 0xF;
// 0xa;
// 0xb;
// 0xc;
// 0xd;
// 0xe;
// 0xf;
// 0o0;
// 0o1;
// 0o2;
// 0o3;
// 0o4;
// 0o5;
// 0o6;
// 0o7;
// 0b0;
// 0b1;

View File

@@ -0,0 +1,9 @@
for (let i: i32 = 0; i < 10; ++i) {
;
}
for (i = 0; i < 10; ++i) {
;
}
for (;;) {
;
}

View File

@@ -0,0 +1,7 @@
function simple(): void {
}
function typeparams<T, V extends T>(a: V | null = null): void {
}
@decorator()
function withdecorator(): void {
}

View File

@@ -1,41 +0,0 @@
import * as fs from "fs";
import * as diff from "diff";
import * as chalk from "chalk";
import * as glob from "glob";
import "../../src/glue/js";
import { NodeKind, ExpressionStatement } from "../../src/ast";
import { Parser } from "../../src/parser";
const filter = process.argv.length > 2 ? "*" + process.argv[2] + "*.ts" : "**.ts";
const files = glob.sync(filter, { cwd: __dirname + "/fixtures" });
files.forEach(filename => {
if (filename.charAt(0) == "_") return;
const isTree = filename.indexOf(".tree.") >= 0;
const parser = new Parser();
const text = fs.readFileSync(__dirname + "/fixtures/" + filename, { encoding: "utf8" }).replace(/\r?\n/g, "\n").replace(/^\/\/.*\r?\n/mg, "");
parser.parseFile(text, filename, true);
var sb: string[] = [];
parser.program.sources[0].serialize(sb);
const actual = sb.join("");
const expected = isTree ? text : text.replace(/^\s+/mg, "");
const diffs = diff.diffLines(expected, actual);
let changed = false;
diffs.forEach(part => {
if (part.added || part.removed)
changed = true;
});
if (changed) { // print it
console.log("Differences in " + filename + ":");
diffs.forEach(part => {
if (part.added || part.removed)
changed = true;
process.stderr.write((part.added ? chalk.default.green : part.removed ? chalk.default.red : chalk.default.grey)(part.value));
});
} else {
console.log("No differences in " + filename + ".");
}
// parser.program.initialize();
// console.log(parser.program.names);
});

42
tests/parser/literals.ts Normal file
View File

@@ -0,0 +1,42 @@
0;
1;
2;
3;
4;
5;
6;
7;
8;
9;
0x0;
0x1;
0x2;
0x3;
0x4;
0x5;
0x6;
0x7;
0x8;
0x9;
0xA;
0xB;
0xC;
0xD;
0xE;
0xF;
0xa;
0xb;
0xc;
0xd;
0xe;
0xf;
0o0;
0o1;
0o2;
0o3;
0o4;
0o5;
0o6;
0o7;
0b0;
0b1;

View File

@@ -0,0 +1,42 @@
0;
1;
2;
3;
4;
5;
6;
7;
8;
9;
0;
1;
2;
3;
4;
5;
6;
7;
8;
9;
10;
11;
12;
13;
14;
15;
10;
11;
12;
13;
14;
15;
0;
1;
2;
3;
4;
5;
6;
7;
0;
1;

View File

@@ -0,0 +1,9 @@
while (1) {
;
}
while (true) {
;
}
while ("str") {
;
}

30
tests/util/diff.ts Normal file
View File

@@ -0,0 +1,30 @@
import * as JsDiff from "diff";
import * as chalk from "chalk";
export function diff(filename: string, expected: string, actual: string): string | null {
const diff = JsDiff.structuredPatch(filename, filename, expected, actual, "expected", "actual", { context: 2 });
if (!diff.hunks.length)
return null;
const ret = [];
ret.push('--- ' + diff.oldHeader);
ret.push('+++ ' + diff.newHeader);
for (let i = 0; i < diff.hunks.length; i++) {
const hunk = diff.hunks[i];
ret.push(
'@@ -' + hunk.oldStart + ',' + hunk.oldLines
+ ' +' + hunk.newStart + ',' + hunk.newLines
+ ' @@'
);
ret.push.apply(ret, hunk.lines.map(line =>
line.charAt(0) === "+"
? chalk.default.green(line)
: line.charAt(0) === "-"
? line = chalk.default.red(line)
: line
));
}
return ret.join('\n') + '\n';
}