import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; import { extractArrayInfo, forwardArguments, parseTypeAndName, prependArg, safeParseInt } from '../common'; import { parseMethod, parseProperty } from '../loader'; describe('Parsing Variable Declarations', () => { const tests: { name: string, input: string, out: { type: string, name: string } }[] = [ {name: 'Simple', input: 'int x', out: {type: 'int', name: 'x'}}, {name: 'Pointer', input: 'int *y', out: {type: 'int *', name: 'y'}}, {name: 'Extra Space', input: ' float a ', out: {type: 'float', name: 'a'}}, {name: 'Ugly Pointer', input: 'int* z', out: {type: 'int*', name: 'z'}}, {name: 'Double Pointer', input: 'int **a', out: {type: 'int **', name: 'a'}}, {name: 'Ugly Double Pointer', input: 'int* *b', out: {type: 'int**', name: 'b'}}, {name: 'Reference', input: 'int &c', out: {type: 'int &', name: 'c'}}, {name: 'Reference-To-Pointer', input: 'int *&d', out: {type: 'int *&', name: 'd'}} ]; for (const test of tests) { it(test.name, () => { const obj = parseTypeAndName(test.input); assert.strictEqual(obj.type, test.out.type); assert.strictEqual(obj.name, test.out.name); }); } it('Invalid', () => { assert.throws(() => { parseTypeAndName('abc'); }); }); }); describe('Prepending Arguments To Functions', () => { const tests: { name: string, input: string, arg: string, out: string }[] = [ {name: 'Empty', input: '()', arg: 'int x', out: '(int x)'}, {name: 'Non-Empty', input: '(int x)', arg: 'int y', out: '(int y, int x)'} ]; for (const test of tests) { it(test.name, () => { assert.strictEqual(prependArg(test.input, test.arg), test.out); }); } }); describe('Parsing Integers', () => { const tests: { name: string, input: string, out?: number }[] = [ {name: 'Zero', input: '0', out: 0}, {name: 'Negative', input: '-5', out: -5}, {name: 'Positive', input: '5', out: 5}, {name: 'Extra Space', input: ' 5 ', out: 5}, {name: 'Hexadecimal', input: '0x10', out: 16}, {name: 'Empty', input: ''}, {name: 'Text', input: 'abc'} ]; for (const test of tests) { it(test.name, () => { if (test.out !== undefined) { assert.strictEqual(safeParseInt(test.input), test.out); } else { assert.throws(() => { safeParseInt(test.input); }); } }); } }); describe('Parsing Properties', () => { const tests: { name: string, input: string, out?: { type: string, name: string, offset: number } }[] = [ {name: 'Basic', input: 'int x = 0x0', out: {type: 'int', name: 'x', offset: 0}}, {name: 'Extra Space', input: 'int z = 0x0', out: {type: 'int', name: 'z', offset: 0}}, {name: 'Pointer', input: 'float *y = 0x4', out: {type: 'float *', name: 'y', offset: 4}}, {name: 'Empty', input: ''}, {name: 'Malformed #1', input: 'int x'}, {name: 'Malformed #2', input: 'int x=2'}, {name: 'Malformed #3', input: 'int x = '} ]; for (const test of tests) { it(test.name, () => { if (test.out) { const property = parseProperty(test.input); assert.strictEqual(property.type, test.out.type); assert.strictEqual(property.name, test.out.name); assert.strictEqual(property.offset, test.out.offset); } else { assert.throws(() => { parseProperty(test.input); }); } }); } }); describe('Parsing Methods', () => { const tests: { name: string, input: string, self: string, isStatic: boolean, out?: { name: string, returnType: string, args: string, address: number } }[] = [ {name: 'Basic', input: 'void func() = 0x10', self: 'Test', isStatic: false, out: {name: 'func', returnType: 'void', args: '(Test *self)', address: 16}}, {name: 'Advanced', input: 'int bar(int x, float y, std::vector *arr) = 0x20', self: 'Foo', isStatic: false, out: {name: 'bar', returnType: 'int', args: '(Foo *self, int x, float y, std::vector *arr)', address: 32}}, {name: 'Extra Space', input: 'int bar (int x) = 0x20', self: 'Foo', isStatic: false, out: {name: 'bar', returnType: 'int', args: '(Foo *self, int x)', address: 32}}, {name: 'Static', input: 'int thing(float x) = 0x30', self: 'Crazy', isStatic: true, out: {name: 'thing', returnType: 'int', args: '(float x)', address: 48}}, {name: 'Empty', input: '', self: 'Test', isStatic: false}, {name: 'Malformed #1', input: 'int broken', self: 'Test', isStatic: false}, {name: 'Malformed #2', input: 'int broken = 0x10', self: 'Test', isStatic: false}, {name: 'Malformed #3', input: 'int broken()', self: 'Test', isStatic: false}, {name: 'Malformed #4', input: 'int broken()=0x0', self: 'Test', isStatic: false}, {name: 'Malformed #5', input: 'int broken() = ', self: 'Test', isStatic: false}, {name: 'Malformed #6', input: 'abc', self: 'Test', isStatic: false} ]; for (const test of tests) { it(test.name, () => { if (test.out) { const method = parseMethod(test.input, test.self, !test.isStatic, false); assert.strictEqual(method.shortName, test.out.name); assert.strictEqual(method.returnType, test.out.returnType); assert.strictEqual(method.getArgs(), test.out.args); assert.strictEqual(method.address, test.out.address); } else { assert.throws(() => { parseMethod(test.input, test.self, !test.isStatic, false); }); } }); } }); describe('Extracting Array Information', () => { const tests: { name: string, input: string, out: { name: string, arrayInfo: string } }[] = [ {name: 'Basic', input: 'x', out: {name: 'x', arrayInfo: ''}}, {name: 'Array', input: 'x[10]', out: {name: 'x', arrayInfo: '[10]'}}, {name: 'Multi-Dimensional Array', input: 'x[10][10]', out: {name: 'x', arrayInfo: '[10][10]'}} ]; for (const test of tests) { it(test.name, () => { const out = extractArrayInfo(test.input); assert.strictEqual(out.name, test.out.name); assert.strictEqual(out.arrayInfo, test.out.arrayInfo); }); } }); describe('Forwarding Arguments', () => { const tests: { name: string, input: string, extra?: string[], out?: string }[] = [ {name: 'No Arguments', input: '()', out: '()'}, {name: 'One Argument', input: '(int x)', out: '(x)'}, {name: 'Two Argument', input: '(int x, float y)', out: '(x, y)'}, {name: 'Arrays', input: '(int one[10], float two[20])', out: '(one, two)'}, {name: 'Generics #1', input: '(Obj x, Obj2 *y)', out: '(x, y)'}, {name: 'Generics #2', input: '(function &callback, void *data)', out: '(callback, data)'}, {name: 'Extra Arguments', input: '(int x)', extra: ['this'], out: '(this, x)'}, {name: 'Malformed #1', input: '(int)'}, {name: 'Malformed #2', input: '('}, {name: 'Malformed #3', input: ')'}, {name: 'Malformed #4', input: '(Obj { if (test.out) { const out = forwardArguments(test.input, test.extra ?? []); assert.strictEqual(out, test.out); } else { assert.throws(() => { forwardArguments(test.input, test.extra ?? []); }); } }); } });