Refine Trace to be able to output graphviz-compatible data.
authorRobert Sesek <rsesek@bluestatic.org>
Tue, 17 Mar 2020 02:35:21 +0000 (22:35 -0400)
committerRobert Sesek <rsesek@bluestatic.org>
Tue, 17 Mar 2020 02:35:21 +0000 (22:35 -0400)
src/core/Form.ts
src/core/Line.ts
src/core/Trace.test.ts [new file with mode: 0644]
src/core/Trace.ts

index 65157531a5ffaabc7df9a50d92118bc427a097c5..9b46ab3254bae562604f2cea8c115a97e11c52da 100644 (file)
@@ -54,7 +54,7 @@ export default abstract class Form<L extends { [key: string]: Line<any> },
     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];
   }
 
index ca5dd0033ac674bc1b1560355955311b05a434b0..b4731305bbe439657df5aba03dc3522ec3d40b4a 100644 (file)
@@ -58,7 +58,7 @@ export class ReferenceLine<F extends Form<any>,
   // 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;
@@ -108,7 +108,7 @@ export class AccumulatorLine<F extends Form<any>,
   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;
   }
diff --git a/src/core/Trace.test.ts b/src/core/Trace.test.ts
new file mode 100644 (file)
index 0000000..342e168
--- /dev/null
@@ -0,0 +1,77 @@
+// 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' ]
+    ]);
+  });
+});
+
index b94fac85a0426aad8ac433c2187bb89d2b4a2f90..5db974ab73262cbdfa744720506d9cba28cdc989 100644 (file)
@@ -9,52 +9,61 @@ var current: Trace = null;
 
 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;