if (!(name in this._input)) {
throw new NotFoundError(`No input with key ${name} on form ${this.name}`);
}
- Trace.add(`${this.name}-input-${name}`);
+ Trace.add(`${this.name} input: ${name}`);
return this._input[name];
}
// the one the Line is in, erase |form|'s type with |as any| to
// keep TypeScript happy.
constructor(form: FormClass<F>, line: L, description?: string, fallback?: T) {
- super(description || `Reference F${form.name}.L${line}`);
+ super(description || `Reference ${form.name}-${line}`);
this._form = form;
this._line = line;
this._fallback = fallback;
private _line: L;
constructor(form: FormClass<F>, line: L, description?: string) {
- super(description || `Accumulator F${form.name}.L${line}`);
+ super(description || `Accumulator ${form.name}-${line}`);
this._form = form;
this._line = line;
}
--- /dev/null
+// Copyright 2020 Blue Static <https://www.bluestatic.org>
+// This program is free software licensed under the GNU General Public License,
+// version 3.0. The full text of the license can be found in LICENSE.txt.
+// SPDX-License-Identifier: GPL-3.0-only
+
+import Form from './Form';
+import TaxReturn from './TaxReturn';
+import { ComputedLine, InputLine, ReferenceLine } from './Line';
+import Trace, { Edge, getLastTraceList } from './Trace';
+
+class TestTaxReturn extends TaxReturn {
+ get year() { return 2019; }
+
+ get includeJointPersonForms() { return false; }
+};
+
+interface Input {
+ name: string;
+ value: number;
+};
+
+class TestForm extends Form<TestForm['_lines'], Input> {
+ readonly name = 'TF';
+
+ readonly _lines = {
+ 'i1': new InputLine<Input>('name'),
+ 'i2': new InputLine<Input>('value'),
+ 'c1': new ComputedLine((tr): string => {
+ return `Hello ${this.getInput('name')}`;
+ }),
+ 'c2': new ComputedLine((tr): number => {
+ return this.getValue(tr, 'i2') * 0.20;
+ }),
+ 'r2': new ReferenceLine(TestForm as any, 'c2'),
+ };
+};
+
+describe('tracing', () => {
+ const tr = new TestTaxReturn();
+ const f = new TestForm({
+ name: 'ABC',
+ value: 100
+ });
+ tr.addForm(f);
+
+ test('input line', () => {
+ f.getValue(tr, 'i1');
+ const trace = getLastTraceList();
+ expect(trace).toStrictEqual([ [ 'TF-i1 (Input from name)', 'TF input: name' ] ]);
+ });
+
+ test('computed line via input', () => {
+ f.getValue(tr, 'c1');
+ const trace = getLastTraceList();
+ expect(trace).toStrictEqual([ [ 'TF-c1', 'TF input: name' ] ]);
+ });
+
+ test('computed line via input line', () => {
+ f.getValue(tr, 'c2');
+ const trace = getLastTraceList();
+ expect(trace).toStrictEqual([
+ [ 'TF-c2', 'TF-i2 (Input from value)' ],
+ [ 'TF-i2 (Input from value)', 'TF input: value' ]
+ ]);
+ });
+
+ test('reference line', () => {
+ f.getValue(tr, 'r2');
+ const trace = getLastTraceList();
+ expect(trace).toStrictEqual([
+ [ 'TF-r2 (Reference TestForm-c2)', 'TF-c2' ],
+ [ 'TF-c2', 'TF-i2 (Input from value)' ],
+ [ 'TF-i2 (Input from value)', 'TF input: value' ]
+ ]);
+ });
+});
+
var traces: Trace[] = [];
+export type Edge = [string, string];
+
export default class Trace {
- private _stack: Set<string>[] = [];
- private _index = 0;
+ private _edges: { [key: string]: Edge } = {};
+ private _stack: string[] = [];
private _name: string;
constructor(line: Line<any>) {
this._name = this._formatLine(line);
- const s = new Set<string>();
-
- s.add(`Start: ${this._name}`);
- if (current === null) {
+ if (current === null)
current = this;
- } else {
- ++current._index;
+
+ if (current._stack.length != 0) {
+ current._addEdge([ current._previousEdge(), this._name ]);
}
- current._stack.push(s);
+ current._stack.push(this._name);
}
static add(id: string) {
if (current === null)
return;
- current._stack[current._index].add(id);
+ current._addEdge([ current._previousEdge(), id ]);
}
end() {
- --current._index;
+ current._stack.pop();
if (current === this) {
current = null;
traces.push(this);
}
}
- get traceList(): readonly string[][] {
- return this._stack.map(s => [...s.values()]);
+ get traceList(): readonly Edge[] {
+ return Object.values(this._edges);
+ }
+
+ private _addEdge(e: Edge) {
+ this._edges[`${e[0]}|${e[1]}`] = e;
+ }
+
+ private _previousEdge(): string {
+ return this._stack[this._stack.length - 1];
}
private _formatLine(line: Line<any>): string {
+ const description = line.description ? ` (${line.description})` : '';
if (line.form === undefined)
- return `${line.constructor.name} (${line.description})`;
- return `${line.form.name}-${line.id} (${line.description})`;
+ return `${line.constructor.name}${description}`;
+ return `${line.form.name}-${line.id}${description}`;
}
};
-export function getLastTraceList(): readonly string[][] {
+export function getLastTraceList(): readonly Edge[] {
if (traces.length == 0)
return null;
return traces[traces.length - 1].traceList;