Files
8bitworkshop/test/cli/testcompat.js
T

188 lines
8.0 KiB
JavaScript

var assert = require('assert');
var fs = require('fs');
var os = require('os');
var path = require('path');
var { execFileSync } = require('child_process');
const compatcheck = require('gen/tools/compatcheck.js');
// The release whose examples the compatibility check builds.
const TAG = '3.12.1';
const BASELINE = path.join(__dirname, '../compat/3.12.1.json');
// Is the tag in this checkout? A shallow clone (CI) may not have it.
function haveTag() {
try {
execFileSync('git', ['rev-parse', '--verify', TAG], { stdio: 'ignore' });
return true;
} catch (e) {
return false;
}
}
// A fake release tree, to check the example list parser without a checkout.
function makeTree(files) {
var dir = fs.mkdtempSync(path.join(os.tmpdir(), '8bws-compat-test-'));
fs.mkdirSync(path.join(dir, 'src/platform'), { recursive: true });
fs.mkdirSync(path.join(dir, 'presets/demo'), { recursive: true });
for (var name of Object.keys(files)) {
fs.writeFileSync(path.join(dir, 'src/platform', name), files[name]);
}
return dir;
}
describe('compat check', function () {
describe('the example list of an old release', function () {
it('should read the presets of a platform class', function () {
var tree = makeTree({
'demo.ts': `
const DEMO_PRESETS = [
{id: 'hello.c', name: 'Hello World'},
{id: 'bye.c', name: 'Bye'},
];
class DemoPlatform {
getPresets() { return DEMO_PRESETS; }
}
PLATFORMS['demo'] = DemoPlatform;
`,
});
var r = compatcheck.parseOldExamples(tree);
assert.equal(r.problems.length, 0);
assert.deepEqual(r.examples.map((e) => e.id).sort(), ['bye.c', 'hello.c']);
assert.equal(r.examples[0].folder, 'demo');
fs.rmSync(tree, { recursive: true, force: true });
});
it('should read a platform written as a function', function () {
var tree = makeTree({
'demo.ts': `
var DEMO_PRESETS = [ {id: 'hello.c', name: 'Hello'} ];
var DemoPlatform = function(mainElement) {
this.getPresets = function() {
return DEMO_PRESETS;
}
}
PLATFORMS['demo'] = DemoPlatform;
`,
});
var r = compatcheck.parseOldExamples(tree);
assert.deepEqual(r.examples.map((e) => e.id), ['hello.c']);
fs.rmSync(tree, { recursive: true, force: true });
});
it('should follow a class that inherits its presets', function () {
var tree = makeTree({
'demo.ts': `
const DEMO_PRESETS = [ {id: 'hello.c', name: 'Hello'} ];
class BaseDemoPlatform {
getPresets() { return DEMO_PRESETS; }
}
class DemoPlatform extends BaseDemoPlatform {
}
PLATFORMS['demo'] = DemoPlatform;
`,
});
var r = compatcheck.parseOldExamples(tree);
assert.deepEqual(r.examples.map((e) => e.id), ['hello.c']);
fs.rmSync(tree, { recursive: true, force: true });
});
it('should ignore a preset that is commented out', function () {
var tree = makeTree({
'demo.ts': `
const DEMO_PRESETS = [
{id: 'hello.c', name: 'Hello'},
// {id: 'old.dasm', name: 'Old'},
];
class DemoPlatform {
getPresets() { return DEMO_PRESETS; }
}
PLATFORMS['demo'] = DemoPlatform;
`,
});
var r = compatcheck.parseOldExamples(tree);
assert.deepEqual(r.examples.map((e) => e.id), ['hello.c']);
fs.rmSync(tree, { recursive: true, force: true });
});
it('should fold in the presets of an array it extends', function () {
var tree = makeTree({
'demo.ts': `
var DEMO_PRESETS = [ {id: 'hello.c', name: 'Hello'} ];
var DEMO2_PRESETS = DEMO_PRESETS.concat([ {id: 'more.c', name: 'More'} ]);
class DemoPlatform {
getPresets() { return DEMO2_PRESETS; }
}
PLATFORMS['demo'] = DemoPlatform;
`,
});
var r = compatcheck.parseOldExamples(tree);
assert.deepEqual(r.examples.map((e) => e.id).sort(), ['hello.c', 'more.c']);
fs.rmSync(tree, { recursive: true, force: true });
});
});
describe('the release we track', function () {
it('should have a baseline', function () {
assert.ok(fs.existsSync(BASELINE), 'missing ' + BASELINE);
var baseline = JSON.parse(fs.readFileSync(BASELINE, 'utf8'));
assert.ok(baseline.length > 300, 'baseline has only ' + baseline.length + ' entries');
for (var entry of baseline) {
assert.ok(entry.preset, 'a baseline entry has no preset');
assert.ok(typeof entry.ok === 'boolean', entry.preset + ' has no result');
}
});
it('should list the same examples as the baseline', function () {
if (!haveTag()) return this.skip(); // no tag in this checkout
var tree = fs.mkdtempSync(path.join(os.tmpdir(), '8bws-compat-test-'));
try {
execFileSync('git', ['worktree', 'add', '--detach', path.join(tree, 'wt'), TAG],
{ stdio: 'ignore' });
var old = compatcheck.parseOldExamples(path.join(tree, 'wt'));
var baseline = JSON.parse(fs.readFileSync(BASELINE, 'utf8'));
var listed = new Set(old.examples.map((e) => e.folder + '/' + e.id));
var missing = baseline
.map((e) => e.preset)
.filter((p) => !listed.has(p) && !p.includes('/skeleton.'));
assert.deepEqual(missing, [], 'the baseline has examples the release does not');
} finally {
try {
execFileSync('git', ['worktree', 'remove', '--force', path.join(tree, 'wt')],
{ stdio: 'ignore' });
} catch (e) { /* the temp dir goes away anyway */ }
fs.rmSync(tree, { recursive: true, force: true });
}
});
it('should build an old example with the current toolchain', async function () {
if (!haveTag()) return this.skip(); // no tag in this checkout
var tree = fs.mkdtempSync(path.join(os.tmpdir(), '8bws-compat-test-'));
try {
execFileSync('git', ['worktree', 'add', '--detach', path.join(tree, 'wt'), TAG],
{ stdio: 'ignore' });
var corpus = {
dir: path.join(tree, 'wt', 'presets'),
tree: path.join(tree, 'wt'),
label: TAG,
};
var listing = await compatcheck.listCorpus(corpus);
var entry = listing.entries.find((e) => e.preset === 'c64/helloc.c');
assert.ok(entry, 'c64/helloc.c is not in the corpus');
var { compileSourceFile } = require('gen/tools/testlib.js');
var result = await compileSourceFile(entry.tool, entry.platform, entry.abs);
assert.ok(result.success, JSON.stringify(result.errors));
} finally {
try {
execFileSync('git', ['worktree', 'remove', '--force', path.join(tree, 'wt')],
{ stdio: 'ignore' });
} catch (e) { /* the temp dir goes away anyway */ }
fs.rmSync(tree, { recursive: true, force: true });
}
});
});
});