mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2026-10-25 15:05:25 +00:00
181 lines
7.9 KiB
TypeScript
181 lines
7.9 KiB
TypeScript
|
|
import * as assert from 'assert';
|
|
import * as path from 'path';
|
|
import { Builder, ProjectFileProvider } from '../src/buildcore';
|
|
import { findRootDir, isSourceFile } from '../src/projectinfo';
|
|
|
|
// Mocha runs from extension/, not the repo root, so this also checks that
|
|
// the toolchain assets load from the configured root rather than the cwd.
|
|
const ROOT = findRootDir(__dirname);
|
|
|
|
function project(files: { [path: string]: string }) {
|
|
var enc = new TextEncoder();
|
|
return async (rel: string) => rel in files ? enc.encode(files[rel]) : null;
|
|
}
|
|
|
|
describe('extension buildcore', function () {
|
|
var builder = new Builder(ROOT);
|
|
|
|
it('finds the repo as the asset root', function () {
|
|
assert.equal(ROOT, path.resolve(__dirname, '../../..'));
|
|
});
|
|
|
|
it('recognizes source files', function () {
|
|
assert.ok(isSourceFile('main.c'));
|
|
assert.ok(isSourceFile('game.dasm'));
|
|
assert.ok(!isSourceFile('settings.json'));
|
|
});
|
|
|
|
// Files the project reads at build time must survive as bytes when they
|
|
// aren't text, even if their extension isn't on the binary list (.hgr).
|
|
it('keeps binary files binary and decodes text', async function () {
|
|
var raw: { [path: string]: Uint8Array } = {
|
|
'logo.hgr': new Uint8Array([0x25, 0x55, 0xaa, 0xd5]), // invalid UTF-8
|
|
'main.s': new TextEncoder().encode('\t.byte 1\n'),
|
|
};
|
|
var files = new ProjectFileProvider(async rel => raw[rel] ?? null, ROOT, 'apple2');
|
|
var hgr = await files.readFile('logo.hgr') as Uint8Array;
|
|
assert.ok(hgr instanceof Uint8Array);
|
|
assert.deepEqual(Array.from(hgr), [0x25, 0x55, 0xaa, 0xd5]);
|
|
assert.equal(await files.readFile('main.s'), '\t.byte 1\n');
|
|
});
|
|
|
|
it('builds a C file with a local header and a preset header', async function () {
|
|
var files = project({
|
|
'main.c': '#include "neslib.h"\n#include "util.h"\nvoid main(void) { ppu_on_all(); while (1) { x = 1; } }\n',
|
|
'util.h': 'static int x;\n',
|
|
});
|
|
var mainText = new TextDecoder().decode(await files('main.c'));
|
|
var r = await builder.build({
|
|
platform: 'nes', mainPath: 'main.c', mainText,
|
|
files: new ProjectFileProvider(files, ROOT, 'nes'),
|
|
});
|
|
assert.deepEqual(r.diagnostics, []);
|
|
assert.ok(r.success);
|
|
assert.equal(r.tool, 'cc65');
|
|
assert.ok(r.output.length > 0);
|
|
assert.ok(r.paths.indexOf('util.h') >= 0);
|
|
assert.ok(r.listings);
|
|
});
|
|
|
|
it('reports errors against the file that has them', async function () {
|
|
var files = project({
|
|
'main.c': '#include "util.h"\nvoid main(void) { }\n',
|
|
'util.h': 'int x;\nthis is not C;\n',
|
|
});
|
|
var mainText = new TextDecoder().decode(await files('main.c'));
|
|
var r = await builder.build({
|
|
platform: 'nes', mainPath: 'main.c', mainText,
|
|
files: new ProjectFileProvider(files, ROOT, 'nes'),
|
|
});
|
|
assert.ok(!r.success);
|
|
assert.ok(r.diagnostics.length > 0);
|
|
assert.ok(r.diagnostics.every(d => d.path === 'util.h'), JSON.stringify(r.diagnostics));
|
|
assert.equal(r.diagnostics[0].line, 2);
|
|
});
|
|
|
|
it('reports assembler errors in the main file', async function () {
|
|
var src = '\tprocessor 6502\n\torg $f000\nStart\n\tlda #1\n\tbogus\n';
|
|
var r = await builder.build({
|
|
platform: 'vcs', mainPath: 'game.a', mainText: src,
|
|
files: new ProjectFileProvider(project({ 'game.a': src }), ROOT, 'vcs'),
|
|
});
|
|
assert.ok(!r.success);
|
|
assert.equal(r.tool, 'dasm');
|
|
assert.equal(r.diagnostics[0].path, 'game.a');
|
|
assert.equal(r.diagnostics[0].line, 5);
|
|
});
|
|
|
|
// Run builds first, so running the same file twice gets an unchanged build
|
|
it('reports warnings of a successful build, and again when unchanged', async function () {
|
|
var src = 'void main(void) {\n const const int x = 3;\n while (1) ;\n}\n';
|
|
var req = () => ({
|
|
platform: 'nes', mainPath: 'warn.c', mainText: src,
|
|
files: new ProjectFileProvider(project({ 'warn.c': src }), ROOT, 'nes'),
|
|
});
|
|
var first = await builder.build(req());
|
|
assert.ok(first.success, JSON.stringify(first.diagnostics));
|
|
assert.ok(first.diagnostics.length > 0);
|
|
assert.ok(first.diagnostics.every(d => d.severity === 'warning' && d.path === 'warn.c'), JSON.stringify(first.diagnostics));
|
|
assert.equal(first.diagnostics[0].line, 2);
|
|
var second = await builder.build(req());
|
|
assert.ok(second.unchanged);
|
|
assert.deepEqual(second.diagnostics, first.diagnostics);
|
|
});
|
|
|
|
it('returns the previous output when nothing changed', async function () {
|
|
var src = '#include "neslib.h"\nvoid main(void) { ppu_on_all(); while (1) ; }\n';
|
|
var req = () => ({
|
|
platform: 'nes', mainPath: 'same.c', mainText: src,
|
|
files: new ProjectFileProvider(project({ 'same.c': src }), ROOT, 'nes'),
|
|
});
|
|
var first = await builder.build(req());
|
|
assert.ok(first.output);
|
|
var second = await builder.build(req());
|
|
assert.ok(second.unchanged);
|
|
assert.deepEqual(second.output, first.output);
|
|
assert.ok(second.listings);
|
|
});
|
|
|
|
// The linker reads the config file from the source's `//#tooldef ld
|
|
// cfgfile=` (or legacy `#define CFGFILE`) directives. Those have to be
|
|
// applied even when the compiler step is skipped as unchanged, or the
|
|
// second build silently links against the platform default config instead.
|
|
it('keeps cfgfile source directives on an unchanged rebuild', async function () {
|
|
// atari8-800 defaults to atari-cart.cfg; atari.cfg is the disk/XEX layout
|
|
var src = '//#tooldef ld cfgfile=atari.cfg\nvoid main(void) { *(unsigned char*)0x02C8 = 1; }\n';
|
|
var req = () => ({
|
|
platform: 'atari8-800', mainPath: 'cfgdirective.c', mainText: src,
|
|
files: new ProjectFileProvider(project({ 'cfgdirective.c': src }), ROOT, 'atari8-800'),
|
|
});
|
|
var first = await builder.build(req());
|
|
assert.ok(first.success, JSON.stringify(first.diagnostics));
|
|
var second = await builder.build(req());
|
|
assert.ok(second.success, JSON.stringify(second.diagnostics));
|
|
assert.ok(second.unchanged);
|
|
// the cart config has a completely different segment layout
|
|
assert.deepEqual(second.segments, first.segments);
|
|
assert.deepEqual(second.output, first.output);
|
|
});
|
|
|
|
// A failed build must not drop the last success: fixing the file back to
|
|
// what built makes the worker say "unchanged", and Run still needs the ROM.
|
|
it('keeps the last output across a failed build', async function () {
|
|
var good = '#include "neslib.h"\nvoid main(void) { ppu_on_all(); while (1) ; }\n';
|
|
var bad = '#include "neslib.h"\nvoid main(void) { ppu_on_all() while (1) ; }\n';
|
|
var build = (src: string) => builder.build({
|
|
platform: 'nes', mainPath: 'revert.c', mainText: src,
|
|
files: new ProjectFileProvider(project({ 'revert.c': src }), ROOT, 'nes'),
|
|
});
|
|
var first = await build(good);
|
|
assert.ok(first.output);
|
|
var failed = await build(bad);
|
|
assert.ok(!failed.success);
|
|
assert.ok(failed.diagnostics.length > 0);
|
|
var reverted = await build(good);
|
|
assert.ok(reverted.success);
|
|
assert.ok(reverted.unchanged);
|
|
assert.deepEqual(reverted.diagnostics, []);
|
|
assert.deepEqual(reverted.output, first.output);
|
|
});
|
|
|
|
// sms-sms-libcv/common.c is a symlink to coleco/common.c: identical source
|
|
// built with different defines must not reuse the other platform's .rel.
|
|
it('rebuilds shared sources when the platform changes', async function () {
|
|
var fs = require('fs');
|
|
var build = async (platform: string, dir: string) => {
|
|
var read = async (rel: string) => {
|
|
var p = path.join(ROOT, 'presets', dir, rel);
|
|
return fs.existsSync(p) ? new Uint8Array(fs.readFileSync(p)) : null;
|
|
};
|
|
var mainText = new TextDecoder().decode(await read('hello.c'));
|
|
return builder.build({ platform, mainPath: 'hello.c', mainText, files: new ProjectFileProvider(read, ROOT, platform) });
|
|
};
|
|
var sms = await build('sms-sms-libcv', 'sms-sms-libcv');
|
|
assert.deepEqual(sms.diagnostics, []);
|
|
var coleco = await build('coleco', 'coleco');
|
|
assert.deepEqual(coleco.diagnostics, []);
|
|
assert.ok(coleco.success);
|
|
});
|
|
});
|