Files
8bitworkshop/test/unit/testtoolmeta.ts
T

236 lines
13 KiB
TypeScript

import assert from "assert";
import { describe, it } from "mocha";
import {
TOOL_META, getToolMeta, getToolMetaForFilename, getPreloadFSName, getSkeletonName,
getIncludePatterns, getLinkPatterns, matchDependencyPatterns,
getSystemIncludePatterns, SYSTEM_INCLUDE_PATTERNS, getSharedFileSystemName, getToolHelpURL,
getPlatformToolHelpURL, getToolVersionLabel, SDCC_DEFAULT_VERSION
} from "../../src/common/toolmeta";
// Node-friendly worker entry point (re-exports TOOLS without Worker wiring)
import { TOOLS } from "../../src/worker/workerlib";
describe('Tool metadata registry', function () {
it('every registry key matches its tool id', function () {
for (let id in TOOL_META) {
assert.strictEqual(TOOL_META[id].id, id, `TOOL_META['${id}'].id mismatch`);
}
});
it('every worker build tool is registered (and vice versa)', function () {
// worker TOOLS -> must exist in registry
for (let id of Object.keys(TOOLS)) {
assert.ok(TOOL_META[id], `worker TOOLS['${id}'] missing from TOOL_META`);
}
// registry -> must have a worker build fn, unless explicitly deferred
// or built on a remote server (llvm-mos)
for (let id in TOOL_META) {
let meta = TOOL_META[id];
if (meta.noWorkerBuild || meta.remote) continue;
assert.ok(TOOLS[id], `TOOL_META['${id}'] missing from worker TOOLS`);
}
});
it('getToolMeta strips the remote: transport prefix', function () {
assert.strictEqual(getToolMeta('remote:llvm-mos'), TOOL_META['llvm-mos']);
assert.strictEqual(getToolMeta('llvm-mos'), TOOL_META['llvm-mos']);
assert.strictEqual(getToolMeta('bogus'), undefined);
});
it('getToolMetaForFilename finds tools by extension', function () {
assert.ok(getToolMetaForFilename('foo.c').some(m => m.id === 'cc65'));
assert.ok(getToolMetaForFilename('foo.s').some(m => m.id === 'ca65'));
assert.ok(getToolMetaForFilename('foo.wiz').some(m => m.id === 'wiz'));
assert.ok(getToolMetaForFilename('foo.ecs').some(m => m.id === 'ecs'));
assert.ok(getToolMetaForFilename('foo.dg').some(m => m.id === 'dialog'));
// unknown extension -> no matches
assert.deepStrictEqual(getToolMetaForFilename('foo.xyz'), []);
});
it('getPreloadFSName resolves per-platform filesystems', function () {
assert.strictEqual(getPreloadFSName('cc65', 'nes'), undefined); // WASI zip, not preloadFS
assert.strictEqual(getSharedFileSystemName('cc65', 'nes'), 'wasi:cc65-fs-nes.zip');
assert.strictEqual(getSharedFileSystemName('cc65', 'c64'), 'wasi:cc65-fs-c64.zip');
assert.strictEqual(getSharedFileSystemName('cc65', 'nonexistent'), undefined);
// the 'default' config leads with whichever SDCC the project defaults to
// (getSharedFileSystemName prefers preloadFS); platforms whose libraries
// were built by 3.x always keep the 3.6.5 preloadFS.
const defaultPreload = SDCC_DEFAULT_VERSION === 3 ? 'sdcc' : undefined;
const defaultShared = SDCC_DEFAULT_VERSION === 3 ? 'sdcc' : 'wasi:sdcc-fs.zip';
assert.strictEqual(getPreloadFSName('sdcc'), defaultPreload);
assert.strictEqual(getSharedFileSystemName('sdcc', 'mw8080bw'), defaultShared);
assert.strictEqual(getPreloadFSName('sdcc', 'gb'), 'sdcc');
assert.strictEqual(getPreloadFSName('sdcc', 'msx-libcv'), 'sdcc');
assert.strictEqual(getSharedFileSystemName('sdcc', 'msx'), defaultShared);
assert.strictEqual(getPreloadFSName('dasm'), undefined);
});
it('getSharedFileSystemName resolves suffixed platform ids to their root base', function () {
// atari8 runs as 'atari8-800' / 'atari8-5200' / *.xlmame variants
assert.strictEqual(getSharedFileSystemName('cc65', 'atari8'), 'wasi:cc65-fs-atari8.zip');
assert.strictEqual(getSharedFileSystemName('cc65', 'atari8-800'), 'wasi:cc65-fs-atari8.zip');
assert.strictEqual(getSharedFileSystemName('cc65', 'atari8-800.xlmame'), 'wasi:cc65-fs-atari8.zip');
assert.strictEqual(getSharedFileSystemName('cc65', 'atari8-5200'), 'wasi:cc65-fs-atari8.zip');
assert.strictEqual(getSharedFileSystemName('fastbasic', 'atari8-800'), 'wasi:cc65-fs-atari8.zip');
assert.strictEqual(getSharedFileSystemName('ecs', 'nes'), 'wasi:cc65-fs-nes.zip');
});
it('getSkeletonName defaults to the tool id', function () {
assert.strictEqual(getSkeletonName('dasm'), 'dasm');
assert.strictEqual(getSkeletonName('remote:llvm-mos'), 'llvm-mos');
});
it('getToolVersionLabel shows an alternate version when there is one', function () {
// both SDCC releases are selectable, so show both (default first)
let sdcc = SDCC_DEFAULT_VERSION === 3 ? '3.6.5 (default) / 4.6.0' : '4.6.0 (default) / 3.6.5';
assert.strictEqual(getToolVersionLabel(TOOL_META['sdcc']), sdcc);
// single-version tools show just their version, unknown ones a dash
assert.strictEqual(getToolVersionLabel(TOOL_META['dasm']), '2.20.17');
assert.strictEqual(getToolVersionLabel(TOOL_META['basic']), '—');
assert.strictEqual(getToolVersionLabel(undefined), '—');
});
it('covers the previously-scattered editor styles and help URLs', function () {
// spot-check ids that used to live in TOOL_TO_SOURCE_STYLE / TOOL_TO_HELPURL
for (let id of ['dasm', 'acme', 'cc65', 'ca65', 'sdcc', 'cmoc', 'zmac',
'nesasm', 'bataribasic', 'fastbasic', 'wiz', 'silice', 'ecs', 'dialog']) {
assert.ok(TOOL_META[id], `expected tool '${id}' in registry`);
}
assert.strictEqual(TOOL_META['cc65'].editorStyle, 'text/x-csrc');
assert.strictEqual(TOOL_META['ca65'].editorStyle, '6502');
assert.ok(TOOL_META['dasm'].helpURL);
});
it('include patterns exist for tools that do dependency parsing', function () {
assert.ok(TOOL_META['cc65'].includePatterns.length > 0);
assert.ok(TOOL_META['verilator'].includePatterns.length > 0);
assert.ok(TOOL_META['acme'].includePatterns.length > 0);
assert.ok(TOOL_META['dialog'].includePatterns.length > 0);
assert.ok(TOOL_META['wiz'].includePatterns.length > 0);
});
it('matches include directives for the tool that builds the file', function () {
let deps = (text: string, tool?: string, platform?: string) =>
matchDependencyPatterns(text, getIncludePatterns(tool, platform));
assert.deepStrictEqual(deps('#include "foo.h"\n', 'cc65'), ['foo.h']);
assert.deepStrictEqual(deps('\t.include "foo.inc"\n', 'ca65'), ['foo.inc']);
assert.deepStrictEqual(deps('//#resource "foo.bin"\n', 'dasm'), ['foo.bin']);
// dasm also accepts unquoted include/incbin names
assert.deepStrictEqual(deps('\tINCLUDE VCS.h\n\tINCLUDE macro.h\n', 'dasm'),
['VCS.h', 'macro.h']);
assert.deepStrictEqual(deps(' .include foo.inc\n incbin table.bin\n', 'dasm'),
['foo.inc', 'table.bin']);
// quoted names stay matched by the shared pattern only (no duplicates)
assert.deepStrictEqual(deps('\tinclude "vcs.h"\n', 'dasm'), ['vcs.h']);
assert.deepStrictEqual(deps('!src "foo.acme"\n', 'acme'), ['foo.acme']);
assert.deepStrictEqual(deps('%% #include "foo.dg"\n', 'dialog'), ['foo.dg']);
assert.deepStrictEqual(deps(' USE foo.xasm\n', 'xasm6809'), ['foo.xasm']);
// ecs uses both its own import and the shared .include directive
assert.deepStrictEqual(deps('.include "vcs.h"\nimport "lib.ecs"\n', 'ecs'),
['vcs.h', 'lib.ecs']);
// wiz: import gets an implicit .wiz extension, embed does not
assert.deepStrictEqual(deps('import "nes";\nembed "map.bin";\n', 'wiz'),
['nes.wiz', 'map.bin']);
// verilog: .arch names a .json file
assert.deepStrictEqual(deps('`include "hvsync.v"\n.arch mynes\n', 'verilator'),
['hvsync.v', 'mynes.json']);
// unknown tool falls back to the platform
assert.deepStrictEqual(deps('`include "hvsync.v"\n', null, 'verilog'), ['hvsync.v']);
assert.deepStrictEqual(deps('#include "foo.h"\n', null, 'c64'), ['foo.h']);
// system includes (<foo.h>) are NOT build dependencies
assert.deepStrictEqual(deps('#include <stdio.h>\n', 'cc65'), []);
assert.deepStrictEqual(deps('#include <stdio.h>\n', 'sdcc'), []);
});
it('matches system include patterns for UI links only', function () {
// tools with a bundled filesystem get system-include link patterns
assert.deepStrictEqual(getSystemIncludePatterns('cc65'), SYSTEM_INCLUDE_PATTERNS);
assert.deepStrictEqual(getSystemIncludePatterns('sdcc'), SYSTEM_INCLUDE_PATTERNS);
// tools without one do not (dasm has no includeDirs)
assert.deepStrictEqual(getSystemIncludePatterns('dasm'), []);
assert.deepStrictEqual(getSystemIncludePatterns('bogus'), []);
// ...and they extract the filename from <...>
let deps = (text: string, tool?: string) =>
matchDependencyPatterns(text, getSystemIncludePatterns(tool));
assert.deepStrictEqual(deps('#include <stdio.h>\n', 'cc65'), ['stdio.h']);
assert.deepStrictEqual(deps(' # include <atari7800.h>\n', 'cc65'), ['atari7800.h']);
// quoted includes are not matched by the system patterns
assert.deepStrictEqual(deps('#include "foo.h"\n', 'cc65'), []);
});
it('resolves shared filesystem names for all tools with includeDirs', function () {
assert.equal(getSharedFileSystemName('cc65', 'nes'), 'wasi:cc65-fs-nes.zip');
// preloadFS-backed tools
assert.equal(getSharedFileSystemName('sdcc', 'coleco'), 'sdcc');
assert.equal(getSharedFileSystemName('bataribasic', 'vcs'), 'wasi:bb-fs.zip');
assert.equal(getSharedFileSystemName('wiz'), 'wiz');
// WASI zip-backed tools (cc2600/cc7800/armtcc/oscar64/dialog)
assert.equal(getSharedFileSystemName('cc2600'), 'wasi:cc2600-fs.zip');
assert.equal(getSharedFileSystemName('cc7800'), 'wasi:cc7800-fs.zip');
assert.equal(getSharedFileSystemName('armtcc'), 'wasi:arm32-fs.zip');
assert.equal(getSharedFileSystemName('oscar64'), 'wasi:oscar64-fs.zip');
assert.equal(getSharedFileSystemName('dialog'), 'wasi:dialog-fs.zip');
// tools without a bundled filesystem
assert.equal(getSharedFileSystemName('cmoc'), undefined);
assert.equal(getSharedFileSystemName('smlrc'), undefined);
assert.equal(getSharedFileSystemName('dasm'), undefined);
});
it('matches link directives only for tools that link', function () {
let deps = (text: string, tool?: string, platform?: string) =>
matchDependencyPatterns(text, getLinkPatterns(tool, platform));
assert.deepStrictEqual(deps('//#link "foo.c"\n', 'cc65'), ['foo.c']);
assert.deepStrictEqual(deps(';#link "foo.s"\n', 'ca65'), ['foo.s']);
assert.deepStrictEqual(deps('//#link "foo.v"\n', 'verilator'), []); // hdl tools don't link
assert.deepStrictEqual(deps('//#link "foo.v"\n', null, 'verilog'), []);
});
it('parses verilog directives in .asm files built by jsasm', function () {
// presets/verilog/test2.asm: .include "*.v" plus ".arch femto16"
let text = '.include "cpu16.v"\n.arch femto16\n';
let files = matchDependencyPatterns(text, getIncludePatterns('jsasm', 'verilog'));
assert.deepStrictEqual(files, ['cpu16.v', 'femto16.json']);
// and no shared-pattern duplicate of the .include
assert.strictEqual(files.filter(f => f == 'cpu16.v').length, 1);
});
it('falls back to the platform when a tool declares no patterns', function () {
// 'basic' has no includePatterns of its own -> platform default
assert.deepStrictEqual(
matchDependencyPatterns('#include "foo.h"\n', getIncludePatterns('basic', 'c64')),
['foo.h']);
// an explicit empty list means "this tool has no such directives"
assert.deepStrictEqual(getLinkPatterns('jsasm', 'verilog'), []);
});
it('resolves platform-specific tool help pages', function () {
// cc65 publishes per-platform manuals; those win over the generic page
assert.strictEqual(getToolHelpURL('cc65', 'atari8'), 'https://cc65.github.io/doc/atari.html');
assert.strictEqual(getToolHelpURL('cc65', 'c64'), 'https://cc65.github.io/doc/c64.html');
// suffixed platform ids resolve through their root base
assert.strictEqual(getToolHelpURL('cc65', 'atari8-800.xlmame'), 'https://cc65.github.io/doc/atari.html');
// no platform-specific page -> the tool's generic helpURL
assert.strictEqual(getToolHelpURL('cc65', 'vector'), TOOL_META['cc65'].helpURL);
assert.strictEqual(getToolHelpURL('dasm', 'atari8'), TOOL_META['dasm'].helpURL);
// tools with no docs at all, or unknown tools
assert.strictEqual(getToolHelpURL('ld65', 'atari8'), undefined);
assert.strictEqual(getToolHelpURL('bogus', 'atari8'), undefined);
});
it('getPlatformToolHelpURL only returns platform-specific pages', function () {
assert.strictEqual(getPlatformToolHelpURL('cc65', 'atari8'), 'https://cc65.github.io/doc/atari.html');
assert.strictEqual(getPlatformToolHelpURL('cc65', 'atari8-800.xlmame'), 'https://cc65.github.io/doc/atari.html');
// generic-only tools have no platform-specific page
assert.strictEqual(getPlatformToolHelpURL('dasm', 'atari8'), undefined);
assert.strictEqual(getPlatformToolHelpURL('cc65', 'vector'), undefined);
assert.strictEqual(getPlatformToolHelpURL('bogus', 'atari8'), undefined);
});
it('rewinds shared global patterns between files', function () {
// patterns are shared between tools and have the /g flag, so a stale
// lastIndex would make the second scan miss the directive
let pats = getIncludePatterns('cc65');
assert.deepStrictEqual(matchDependencyPatterns('#include "a.h"\n', pats), ['a.h']);
assert.deepStrictEqual(matchDependencyPatterns('#include "b.h"\n', pats), ['b.h']);
});
});