aboutsummaryrefslogtreecommitdiffgithub
diff options
context:
space:
mode:
authorAustin Adams <git@austinjadams.com>2019-01-26 17:31:30 -0600
committerAustin Adams <git@austinjadams.com>2019-01-26 17:31:30 -0600
commitb178d160633bac3a1fe4415c20d5a0bdc9741dd7 (patch)
tree1df4fab8cbb13324ba4466d08f705888b75eb05d
parent9357daa71d72b969fc958df4ba9e5cd1c5a409bb (diff)
downloadnovice-b178d160633bac3a1fe4415c20d5a0bdc9741dd7.tar.gz
novice-b178d160633bac3a1fe4415c20d5a0bdc9741dd7.tar.xz
Add cli.ts tests
-rw-r--r--novice/assembler/index.ts2
-rw-r--r--novice/cli.test.ts351
2 files changed, 348 insertions, 5 deletions
diff --git a/novice/assembler/index.ts b/novice/assembler/index.ts
index 6b9e6e9..452acc7 100644
--- a/novice/assembler/index.ts
+++ b/novice/assembler/index.ts
@@ -52,5 +52,5 @@ function getConfig(configName: string): AssemblerConfig {
};
}
-export { Assembler, AssemblerConfig, getParser, getGenerator,
+export { Assembler, AssemblerConfig, Serializer, getParser, getGenerator,
getOpSpec, getSerializer, getConfig };
diff --git a/novice/cli.test.ts b/novice/cli.test.ts
index f9257c4..ecfd215 100644
--- a/novice/cli.test.ts
+++ b/novice/cli.test.ts
@@ -1,13 +1,18 @@
import { Readable, Writable } from 'stream';
import { Buffer } from 'buffer';
-import { getIsa } from './isa';
import main from './cli';
// Mocks
jest.mock('fs');
import * as fs from 'fs';
jest.mock('./assembler');
-import { Assembler, AssemblerConfig, getConfig, getParser } from './assembler';
+import { Assembler, AssemblerConfig, Serializer, getConfig, getParser,
+ getSerializer } from './assembler';
+jest.mock('./simulator');
+import { CliDebugger, getSimulatorConfig, Simulator,
+ SimulatorConfig } from './simulator';
+jest.mock('./isa');
+import { getIsa, StreamIO } from './isa';
describe('cli', () => {
let stdin: Readable, stdout: Writable, stderr: Writable;
@@ -39,7 +44,6 @@ describe('cli', () => {
if (callback) callback();
}
});
-
});
afterEach(() => {
@@ -60,6 +64,7 @@ describe('cli', () => {
let mockOutFp: Writable;
let mockAssembleTo: (inFp: Readable, outFp: Writable) => Promise<void>;
let mockConfig: AssemblerConfig;
+ let mockSerializer: Serializer;
beforeEach(() => {
// @ts-ignore
@@ -98,9 +103,14 @@ describe('cli', () => {
};
// @ts-ignore
getConfig.mockReturnValue(mockConfig);
+
+ // @ts-ignore
+ mockSerializer = {fileExt: () => 'tl6'};
+ // @ts-ignore
+ getSerializer.mockReturnValue(mockSerializer);
});
- it('parses asm file', () => {
+ it('assembles asm file', () => {
return main(['asm', '-c', 'bread', 'patrick.asm'],
stdin, stdout, stderr).then(exitCode => {
// @ts-ignore
@@ -110,10 +120,124 @@ describe('cli', () => {
// @ts-ignore
expect(getConfig.mock.calls).toEqual([['bread']]);
// @ts-ignore
+ expect(getSerializer.mock.calls).toEqual([]);
+ // @ts-ignore
+ expect(Assembler.mock.calls).toEqual([[mockConfig]]);
+ // @ts-ignore
+ expect(mockAssembleTo.mock.calls).toEqual([[mockInFp, mockOutFp]]);
+
+ expect(exitCode).toEqual(0);
+ expect(stdoutActual).toEqual('');
+ expect(stderrActual).toEqual('');
+ });
+ });
+
+ it('assembles asm file without extension', () => {
+ return main(['asm', '-c', 'bread', 'chickenworld'],
+ stdin, stdout, stderr).then(exitCode => {
+ // @ts-ignore
+ expect(fs.createReadStream.mock.calls).toEqual([['chickenworld']]);
+ // @ts-ignore
+ expect(fs.createWriteStream.mock.calls).toEqual([['chickenworld.star']]);
+ // @ts-ignore
+ expect(getConfig.mock.calls).toEqual([['bread']]);
+ // @ts-ignore
+ expect(getSerializer.mock.calls).toEqual([]);
+ // @ts-ignore
+ expect(Assembler.mock.calls).toEqual([[mockConfig]]);
+ // @ts-ignore
+ expect(mockAssembleTo.mock.calls).toEqual([[mockInFp, mockOutFp]]);
+
+ expect(exitCode).toEqual(0);
+ expect(stdoutActual).toEqual('');
+ expect(stderrActual).toEqual('');
+ });
+ });
+
+ it('assembles asm file with period in earlier path components', () => {
+ return main(['asm', '-c', 'bread', '/home/travis.adams/chickenworld'],
+ stdin, stdout, stderr).then(exitCode => {
+ // @ts-ignore
+ expect(fs.createReadStream.mock.calls).toEqual([['/home/travis.adams/chickenworld']]);
+ // @ts-ignore
+ expect(fs.createWriteStream.mock.calls).toEqual([['/home/travis.adams/chickenworld.star']]);
+ // @ts-ignore
+ expect(getConfig.mock.calls).toEqual([['bread']]);
+ // @ts-ignore
+ expect(getSerializer.mock.calls).toEqual([]);
+ // @ts-ignore
+ expect(Assembler.mock.calls).toEqual([[mockConfig]]);
+ // @ts-ignore
+ expect(mockAssembleTo.mock.calls).toEqual([[mockInFp, mockOutFp]]);
+
+ expect(exitCode).toEqual(0);
+ expect(stdoutActual).toEqual('');
+ expect(stderrActual).toEqual('');
+ });
+ });
+
+ it('assembles asm file with hardcoded out path', () => {
+ return main(['asm', '-c', 'bread', 'chickenworld.asm', '-o', 'jeff'],
+ stdin, stdout, stderr).then(exitCode => {
+ // @ts-ignore
+ expect(fs.createReadStream.mock.calls).toEqual([['chickenworld.asm']]);
+ // @ts-ignore
+ expect(fs.createWriteStream.mock.calls).toEqual([['jeff']]);
+ // @ts-ignore
+ expect(getConfig.mock.calls).toEqual([['bread']]);
+ // @ts-ignore
+ expect(getSerializer.mock.calls).toEqual([]);
+ // @ts-ignore
+ expect(Assembler.mock.calls).toEqual([[mockConfig]]);
+ // @ts-ignore
+ expect(mockAssembleTo.mock.calls).toEqual([[mockInFp, mockOutFp]]);
+
+ expect(exitCode).toEqual(0);
+ expect(stdoutActual).toEqual('');
+ expect(stderrActual).toEqual('');
+ });
+ });
+
+ it('assembles asm file with lc3 config by default', () => {
+ return main(['asm', 'chickenworld.asm'],
+ stdin, stdout, stderr).then(exitCode => {
+ // @ts-ignore
+ expect(fs.createReadStream.mock.calls).toEqual([['chickenworld.asm']]);
+ // @ts-ignore
+ expect(fs.createWriteStream.mock.calls).toEqual([['chickenworld.star']]);
+ // @ts-ignore
+ expect(getConfig.mock.calls).toEqual([['lc3']]);
+ // @ts-ignore
+ expect(getSerializer.mock.calls).toEqual([]);
+ // @ts-ignore
+ expect(Assembler.mock.calls).toEqual([[mockConfig]]);
+ // @ts-ignore
+ expect(mockAssembleTo.mock.calls).toEqual([[mockInFp, mockOutFp]]);
+
+ expect(exitCode).toEqual(0);
+ expect(stdoutActual).toEqual('');
+ expect(stderrActual).toEqual('');
+ });
+ });
+
+ it('assembles asm file with custom serializer', () => {
+ return main(['asm', 'chickenworld.asm', '-f', 'honker'],
+ stdin, stdout, stderr).then(exitCode => {
+ // @ts-ignore
+ expect(fs.createReadStream.mock.calls).toEqual([['chickenworld.asm']]);
+ // @ts-ignore
+ expect(fs.createWriteStream.mock.calls).toEqual([['chickenworld.tl6']]);
+ // @ts-ignore
+ expect(getConfig.mock.calls).toEqual([['lc3']]);
+ // @ts-ignore
+ expect(getSerializer.mock.calls).toEqual([['honker']]);
+ // @ts-ignore
expect(Assembler.mock.calls).toEqual([[mockConfig]]);
// @ts-ignore
expect(mockAssembleTo.mock.calls).toEqual([[mockInFp, mockOutFp]]);
+ expect(mockConfig.serializer).toBe(mockSerializer);
+
expect(exitCode).toEqual(0);
expect(stdoutActual).toEqual('');
expect(stderrActual).toEqual('');
@@ -140,6 +264,225 @@ describe('cli', () => {
});
});
+ describe('simulation', () => {
+ let mockSimConfig: SimulatorConfig;
+ let mockFp: Readable;
+
+ beforeAll(() => {
+ mockSimConfig = {
+ // @ts-ignore
+ isa: {donkey: 'horse apple'},
+ loader: {
+ load: jest.fn(),
+ },
+ };
+ // @ts-ignore
+ mockFp = {
+ iam: 'fp',
+ // @ts-ignore
+ on(ev, cb) {
+ if (ev === 'readable') cb();
+ }
+ };
+ });
+
+ beforeEach(() => {
+ // @ts-ignore
+ getSimulatorConfig.mockReturnValue(mockSimConfig);
+ // @ts-ignore
+ fs.createReadStream.mockReturnValue(mockFp);
+ });
+
+ afterEach(() => {
+ // @ts-ignore
+ getSimulatorConfig.mockReset();
+ // @ts-ignore
+ fs.createReadStream.mockReset();
+ // @ts-ignore
+ mockSimConfig.loader.load.mockReset();
+ });
+
+ describe('sim subcommand', () => {
+ let mockStdin: Readable;
+ let mockIo: StreamIO;
+ let mockSim: Simulator;
+
+ beforeAll(() => {
+ // @ts-ignore
+ mockStdin = {
+ iam: 'stdin',
+ // @ts-ignore
+ on(ev, cb) {
+ if (ev === 'readable') cb();
+ }
+ };
+ // @ts-ignore
+ mockIo = {timothy: 'aveni'};
+ // @ts-ignore
+ mockSim = {
+ run: jest.fn(),
+ };
+ });
+
+ beforeEach(() => {
+ // @ts-ignore
+ StreamIO.mockImplementation(() => mockIo);
+ // @ts-ignore
+ Simulator.mockImplementation(() => mockSim);
+ });
+
+ afterEach(() => {
+ // @ts-ignore
+ StreamIO.mockReset();
+ // @ts-ignore
+ Simulator.mockReset();
+ // @ts-ignore
+ mockSim.run.mockReset();
+ });
+
+ it('simulates', () => {
+ return main(['sim', 'farzam.obj'],
+ mockStdin, stdout, stderr).then(exitCode => {
+ // @ts-ignore
+ expect(getSimulatorConfig.mock.calls).toEqual([['lc3']]);
+ // @ts-ignore
+ expect(fs.createReadStream.mock.calls).toEqual([['farzam.obj']]);
+ // @ts-ignore
+ expect(StreamIO.mock.calls).toEqual([[mockStdin, stdout]]);
+ // @ts-ignore
+ expect(Simulator.mock.calls).toEqual([[mockSimConfig.isa, mockIo]]);
+ // @ts-ignore
+ expect(mockSim.run.mock.calls).toEqual([[]]);
+
+ expect(exitCode).toEqual(0);
+ expect(stdoutActual).toEqual('');
+ expect(stderrActual).toEqual('');
+ });
+ });
+
+ it('handles nonexistent file', () => {
+ // @ts-ignore
+ fs.createReadStream.mockReset();
+ // @ts-ignore
+ fs.createReadStream.mockReturnValue({
+ // @ts-ignore
+ on(ev, cb) {
+ if (ev === 'error') cb(new Error('oopsie daisy doodle'));
+ }
+ });
+
+ return main(['sim', 'margaret.obj'],
+ mockStdin, stdout, stderr).then(exitCode => {
+ // @ts-ignore
+ expect(fs.createReadStream.mock.calls).toEqual([['margaret.obj']]);
+ // @ts-ignore
+ expect(mockSim.run.mock.calls).toEqual([]);
+
+ expect(exitCode).toEqual(1);
+ expect(stdoutActual).toEqual('');
+ expect(stderrActual).toMatch('oopsie daisy doodle');
+ });
+ });
+ });
+
+ describe('dbg subcommand', () => {
+ let mockDbg: CliDebugger;
+
+ beforeAll(() => {
+ // @ts-ignore
+ mockDbg = {
+ run: jest.fn(),
+ close: jest.fn(),
+ };
+ });
+
+ beforeEach(() => {
+ // @ts-ignore
+ CliDebugger.mockImplementation(() => mockDbg);
+ });
+
+ afterEach(() => {
+ // @ts-ignore
+ CliDebugger.mockReset();
+ // @ts-ignore
+ mockDbg.run.mockReset();
+ // @ts-ignore
+ mockDbg.close.mockReset();
+ });
+
+ it('launches debugger', () => {
+ return main(['dbg', 'brickell.obj'],
+ stdin, stdout, stderr).then(exitCode => {
+ // @ts-ignore
+ expect(getSimulatorConfig.mock.calls).toEqual([['lc3']]);
+ // @ts-ignore
+ expect(fs.createReadStream.mock.calls).toEqual([['brickell.obj']]);
+ // @ts-ignore
+ expect(CliDebugger.mock.calls).toEqual([[mockSimConfig.isa, stdin, stdout]]);
+ // @ts-ignore
+ expect(mockDbg.run.mock.calls).toEqual([[]]);
+ // @ts-ignore
+ expect(mockDbg.close.mock.calls).toEqual([[]]);
+
+ expect(exitCode).toEqual(0);
+ expect(stdoutActual).toEqual('');
+ expect(stderrActual).toEqual('');
+ });
+ });
+
+ it('handles setup error', () => {
+ // @ts-ignore
+ fs.createReadStream.mockReset();
+ // @ts-ignore
+ fs.createReadStream.mockReturnValue({
+ // @ts-ignore
+ on(ev, cb) {
+ if (ev === 'error') cb(new Error('oops i did it again'));
+ }
+ });
+
+ return main(['dbg', 'brickell.obj'],
+ stdin, stdout, stderr).then(exitCode => {
+ // @ts-ignore
+ expect(fs.createReadStream.mock.calls).toEqual([['brickell.obj']]);
+ // @ts-ignore
+ expect(CliDebugger.mock.calls).toEqual([]);
+
+ expect(exitCode).toEqual(1);
+ expect(stdoutActual).toEqual('');
+ expect(stderrActual).toMatch(/setup error.+oops i did it again/);
+ });
+ });
+
+ it('handles simulator error', () => {
+ // @ts-ignore
+ mockDbg.run.mockReset();
+ // @ts-ignore
+ mockDbg.run.mockImplementation(() => {
+ throw new Error('unexpected end-of-file');
+ });
+
+ return main(['dbg', 'daisy.obj'],
+ stdin, stdout, stderr).then(exitCode => {
+ // @ts-ignore
+ expect(getSimulatorConfig.mock.calls).toEqual([['lc3']]);
+ // @ts-ignore
+ expect(fs.createReadStream.mock.calls).toEqual([['daisy.obj']]);
+ // @ts-ignore
+ expect(CliDebugger.mock.calls).toEqual([[mockSimConfig.isa, stdin, stdout]]);
+ // @ts-ignore
+ expect(mockDbg.run.mock.calls).toEqual([[]]);
+ // @ts-ignore
+ expect(mockDbg.close.mock.calls).toEqual([[]]);
+
+ expect(exitCode).toEqual(1);
+ expect(stdoutActual).toEqual('');
+ expect(stderrActual).toMatch('error: unexpected end-of-file');
+ });
+ });
+ });
+ });
+
describe('tablegen subcommand', () => {
it('generates table', () => {
const json = {massive: 'banana'};