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

90 lines
3.2 KiB
TypeScript

import assert from "assert";
import * as fs from "fs";
import { describe, it } from "mocha";
import { hashState } from "../../src/common/statehash";
import { formatTimestamp } from "../../src/common/timeline";
import { hex } from "../../src/common/util";
import { EmuTarget, loadPlatform } from "../../src/tools/emutarget";
import { RunScript } from "../../src/tools/runscript";
// The run-script commands that move backwards in time: back, seek, rewind,
// rbreak, now. They drive EmuCore's timeline, so going back and forward again
// has to land on the same state.
async function open() {
const t = await loadPlatform('mw8080bw');
await t.start();
await t.loadROM(new Uint8Array(fs.readFileSync('test/roms/mw8080bw/game2.c.rom')));
let out = '';
const script = new RunScript(t, (s: string) => { out += s; });
return { t, script, output: () => { const s = out; out = ''; return s; } };
}
function stateOf(t: EmuTarget) { return hashState(t.machine.saveState()); }
describe('RunScript time travel', function () {
this.timeout(60000);
it('labels output with the frame and step', async function () {
const { script, output } = await open();
script.run('run 3; step 2');
const out = output();
assert.match(out, /^\[3:0\] ran 3 frames$/m);
assert.match(out, /^\[3:2\] PC=\$[0-9A-F]{4}$/m);
});
it('steps back to where it stepped from', async function () {
const { t, script, output } = await open();
script.run('run 5; step 1');
const pc = t.getPC();
const state = stateOf(t);
script.run('step 7; back 7');
assert.strictEqual(t.getPC(), pc);
assert.strictEqual(stateOf(t), state);
assert.match(output(), new RegExp(`PC=\\$${hex(pc, 4)}`));
});
it('seeks to a frame and step, and back to the present', async function () {
const { t, script, output } = await open();
script.run('run 12');
const present = stateOf(t);
script.run('seek 4:10; now');
assert.strictEqual(formatTimestamp(t.now()), '4:10');
assert.match(output(), /at 4:10 \(past; recorded 0:0 to 12:0\)/);
script.run('seek 12');
assert.strictEqual(stateOf(t), present);
assert.ok(!t.isInPast());
});
it('rewinds whole frames, to a frame start', async function () {
const { t, script } = await open();
script.run('run 10; rewind 3');
assert.strictEqual(formatTimestamp(t.now()), '7:0');
script.run('step 5; rewind');
assert.strictEqual(formatTimestamp(t.now()), '7:0');
});
it('runs back to the last time the PC was somewhere', async function () {
const { t, script, output } = await open();
script.run('run 10; step 1');
const pc = t.getPC();
const when = t.now();
script.run('run 5');
output();
script.run(`rbreak $${hex(pc, 4)}`);
assert.match(output(), /HIT/);
assert.strictEqual(t.getPC(), pc);
assert.ok(t.isInPast());
// the last visit, which is no earlier than the one we saw
assert.ok(t.now().frame >= when.frame);
});
it('reports a miss and stays put', async function () {
const { t, script, output } = await open();
script.run('run 5');
script.run('rbreak $FFFF');
assert.match(output(), /MISSED/);
assert.strictEqual(formatTimestamp(t.now()), '5:0');
});
});