summaryrefslogtreecommitdiffstats
path: root/llparse-builder/test/span-allocator-test.ts
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--llparse-builder/test/span-allocator-test.ts146
1 files changed, 146 insertions, 0 deletions
diff --git a/llparse-builder/test/span-allocator-test.ts b/llparse-builder/test/span-allocator-test.ts
new file mode 100644
index 0000000..bc8f656
--- /dev/null
+++ b/llparse-builder/test/span-allocator-test.ts
@@ -0,0 +1,146 @@
+import * as assert from 'assert';
+
+import { Builder, SpanAllocator } from '../src/builder';
+
+describe('LLParse/LoopChecker', () => {
+ let b: Builder;
+ let sa: SpanAllocator;
+ beforeEach(() => {
+ b = new Builder();
+ sa = new SpanAllocator();
+ });
+
+ it('should allocate single span', () => {
+ const span = b.span(b.code.span('span'));
+ const start = b.node('start');
+ const body = b.node('body');
+
+ start
+ .otherwise(span.start(body));
+
+ body
+ .skipTo(span.end(start));
+
+ const res = sa.allocate(start);
+
+ assert.strictEqual(res.max, 0);
+
+ assert.strictEqual(res.concurrency.length, 1);
+ assert.ok(res.concurrency[0].includes(span));
+
+ assert.strictEqual(res.colors.size, 1);
+ assert.strictEqual(res.colors.get(span), 0);
+ });
+
+ it('should allocate overlapping spans', () => {
+ const span1 = b.span(b.code.span('span1'));
+ const span2 = b.span(b.code.span('span2'));
+
+ const start = b.node('start');
+ const body1 = b.node('body1');
+ const body2 = b.node('body2');
+
+ start
+ .otherwise(span1.start(body1));
+
+ body1
+ .otherwise(span2.start(body2));
+
+ body2
+ .skipTo(span2.end(span1.end(start)));
+
+ const res = sa.allocate(start);
+
+ assert.strictEqual(res.max, 1);
+
+ assert.strictEqual(res.concurrency.length, 2);
+ assert.ok(res.concurrency[0].includes(span1));
+ assert.ok(res.concurrency[1].includes(span2));
+
+ assert.strictEqual(res.colors.size, 2);
+ assert.strictEqual(res.colors.get(span1), 0);
+ assert.strictEqual(res.colors.get(span2), 1);
+ });
+
+ it('should allocate non-overlapping spans', () => {
+ const span1 = b.span(b.code.span('span1'));
+ const span2 = b.span(b.code.span('span2'));
+
+ const start = b.node('start');
+ const body1 = b.node('body1');
+ const body2 = b.node('body2');
+
+ start
+ .match('a', span1.start(body1))
+ .otherwise(span2.start(body2));
+
+ body1
+ .skipTo(span1.end(start));
+
+ body2
+ .skipTo(span2.end(start));
+
+ const res = sa.allocate(start);
+
+ assert.strictEqual(res.max, 0);
+
+ assert.strictEqual(res.concurrency.length, 1);
+ assert.ok(res.concurrency[0].includes(span1));
+ assert.ok(res.concurrency[0].includes(span2));
+
+ assert.strictEqual(res.colors.size, 2);
+ assert.strictEqual(res.colors.get(span1), 0);
+ assert.strictEqual(res.colors.get(span2), 0);
+ });
+
+ it('should throw on loops', () => {
+ const span = b.span(b.code.span('span_name'));
+
+ const start = b.node('start');
+
+ start
+ .otherwise(span.start(start));
+
+ assert.throws(() => {
+ sa.allocate(start);
+ }, /loop.*span_name/);
+ });
+
+ it('should throw on unmatched ends', () => {
+ const start = b.node('start');
+ const span = b.span(b.code.span('on_data'));
+
+ start.otherwise(span.end().skipTo(start));
+
+ assert.throws(() => sa.allocate(start), /unmatched.*on_data/i);
+ });
+
+ it('should throw on branched unmatched ends', () => {
+ const start = b.node('start');
+ const end = b.node('end');
+ const span = b.span(b.code.span('on_data'));
+
+ start
+ .match('a', end)
+ .match('b', span.start(end))
+ .otherwise(b.error(1, 'error'));
+
+ end
+ .otherwise(span.end(start));
+
+ assert.throws(() => sa.allocate(start), /unmatched.*on_data/i);
+ });
+
+ it('should propagate through the Invoke map', () => {
+ const start = b.node('start');
+ const span = b.span(b.code.span('llparse__on_data'));
+
+ b.property('i8', 'custom');
+
+ start.otherwise(b.invoke(b.code.load('custom'), {
+ 0: span.end().skipTo(start),
+ }, span.end().skipTo(start)));
+
+ assert.doesNotThrow(() => sa.allocate(span.start(start)));
+ });
+});