Files
8bitworkshop/test/unit/testsearchdebuggerhit.ts

150 lines
5.7 KiB
TypeScript

import assert from "assert";
import { describe, it } from "mocha";
import {
resolveDebuggerTarget,
findSegmentAt,
makeDebuggerHit,
shouldOfferDebuggerHit,
} from "../../src/ide/search/debuggerhit";
import { Segment } from "../../src/common/workertypes";
const SYMBOLS = {
main: 0xc000,
JOY_READ: 0xf000,
videoTop: 0x0200,
};
describe('DebuggerHit', function () {
describe('resolveDebuggerTarget', function () {
it('should resolve exact symbol names', function () {
const t = resolveDebuggerTarget('main', SYMBOLS);
assert.ok(t);
assert.strictEqual(t!.addr, 0xc000);
assert.strictEqual(t!.isSymbol, true);
});
it('should resolve symbol names case-insensitively', function () {
const t = resolveDebuggerTarget('joy_read', SYMBOLS);
assert.ok(t);
assert.strictEqual(t!.addr, 0xf000);
assert.strictEqual(t!.isSymbol, true);
});
it('should prefer symbol map over hex parse', function () {
// "add" is valid hex (0xadd) but if a symbol exists, it wins
const t = resolveDebuggerTarget('add', { add: 0x123 });
assert.ok(t);
assert.strictEqual(t!.addr, 0x123);
assert.strictEqual(t!.isSymbol, true);
});
it('should parse $-prefixed hex', function () {
const t = resolveDebuggerTarget('$c000');
assert.ok(t);
assert.strictEqual(t!.addr, 0xc000);
assert.strictEqual(t!.isSymbol, false);
});
it('should parse 0x-prefixed hex', function () {
const t = resolveDebuggerTarget('0xC000');
assert.ok(t);
assert.strictEqual(t!.addr, 0xc000);
assert.strictEqual(t!.isSymbol, false);
});
it('should parse bare hex digits', function () {
const t = resolveDebuggerTarget('C000');
assert.ok(t);
assert.strictEqual(t!.addr, 0xc000);
assert.strictEqual(t!.isSymbol, false);
});
it('should return null for non-hex identifiers', function () {
assert.strictEqual(resolveDebuggerTarget('zzz'), null);
assert.strictEqual(resolveDebuggerTarget('main loop'), null);
assert.strictEqual(resolveDebuggerTarget(''), null);
assert.strictEqual(resolveDebuggerTarget(' '), null);
});
it('should return null for hex-like names when no symbol map exists... or resolve as hex', function () {
// without a symbol map, hex-like needles still resolve (as raw hex)
const t = resolveDebuggerTarget('add');
assert.ok(t);
assert.strictEqual(t!.addr, 0xadd);
assert.strictEqual(t!.isSymbol, false);
});
});
describe('findSegmentAt', function () {
const segs: Segment[] = [
{ name: 'Code', start: 0xc000, size: 0x1000, type: 'rom' },
{ name: 'RAM', start: 0x0200, size: 0x100, type: 'ram' },
{ name: 'IO', start: 0x8000, last: 0x8003, size: 0x4, type: 'io' },
];
it('should find the segment containing an address', function () {
assert.strictEqual(findSegmentAt(0xc123, segs)?.name, 'Code');
assert.strictEqual(findSegmentAt(0x02ff, segs)?.name, 'RAM');
});
it('should respect the last field when present', function () {
assert.strictEqual(findSegmentAt(0x8003, segs)?.name, 'IO');
assert.strictEqual(findSegmentAt(0x8004, segs), null);
});
it('should return null for unmapped addresses', function () {
assert.strictEqual(findSegmentAt(0x0000, segs), null);
assert.strictEqual(findSegmentAt(0x10000, segs), null);
});
it('should handle missing segment lists', function () {
assert.strictEqual(findSegmentAt(0xc000, undefined), null);
assert.strictEqual(findSegmentAt(0xc000, []), null);
});
});
describe('makeDebuggerHit', function () {
it('should build a disassembler hit for code (rom) segments', function () {
const segs: Segment[] = [{ name: 'Code', start: 0xc000, size: 0x1000, type: 'rom' }];
const hit = makeDebuggerHit('main', { addr: 0xc100, isSymbol: true }, segs);
assert.strictEqual(hit.record.source, 'debugger');
assert.strictEqual(hit.record.kind, 'label');
assert.strictEqual(hit.record.brief, 'open in Disassembly');
assert.strictEqual(hit.record.addr, 0xc100);
assert.strictEqual(hit.record.name, 'main');
});
it('should build a memory browser hit for data (ram) segments', function () {
const segs: Segment[] = [{ name: 'RAM', start: 0x0200, size: 0x100, type: 'ram' }];
const hit = makeDebuggerHit('videoTop', { addr: 0x0200, isSymbol: true }, segs);
assert.strictEqual(hit.record.kind, 'var');
assert.strictEqual(hit.record.brief, 'open in Memory Browser');
assert.strictEqual(hit.record.addr, 0x0200);
});
it('should default to the memory browser when no segment matches', function () {
const hit = makeDebuggerHit('$9000', { addr: 0x9000, isSymbol: false }, []);
assert.strictEqual(hit.record.kind, 'var');
assert.strictEqual(hit.record.brief, 'open in Memory Browser');
});
it('should give debugger hits a low score so source hits rank first', function () {
const hit = makeDebuggerHit('main', { addr: 0xc000, isSymbol: true }, []);
assert.ok(hit.score < 1000);
});
});
describe('shouldOfferDebuggerHit', function () {
it('should always offer exact symbol matches', function () {
assert.strictEqual(shouldOfferDebuggerHit({ addr: 0xc000, isSymbol: true }, true), true);
assert.strictEqual(shouldOfferDebuggerHit({ addr: 0xc000, isSymbol: true }, false), true);
});
it('should only offer raw hex when there are no other hits', function () {
assert.strictEqual(shouldOfferDebuggerHit({ addr: 0xadd, isSymbol: false }, false), true);
assert.strictEqual(shouldOfferDebuggerHit({ addr: 0xadd, isSymbol: false }, true), false);
});
});
});