Skip to content

Commit

Permalink
Merge pull request #232 from lf-lang/mutation/sidetask/refactor-canco…
Browse files Browse the repository at this point in the history
…nnect

Refactor Reactor::{connect, connectMulti, canConnect}
  • Loading branch information
lhstrh authored Oct 12, 2024
2 parents 09d301a + d140fdc commit 8eee9e2
Show file tree
Hide file tree
Showing 13 changed files with 358 additions and 159 deletions.
5 changes: 3 additions & 2 deletions __tests__/HierarchicalSingleEvent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import {
Parameter,
OutPort,
InPort,
TimeValue
TimeValue,
CanConnectResult
} from "../src/core/internal";

import {SingleEvent} from "../src/share/SingleEvent";
Expand Down Expand Up @@ -93,7 +94,7 @@ describe("HierarchicalSingleEvent", function () {
seTest.seContainer.child.o,
seTest.logContainer.child.i
)
).toBe(false);
).toBe(CanConnectResult.NOT_IN_SCOPE);

seTest._start();
});
Expand Down
11 changes: 3 additions & 8 deletions __tests__/InvalidMutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,25 +38,20 @@ class R1 extends Reactor {
[this.in1],
[this.in1, this.in2, this.out1, this.out2],
function (this, __in1, __in2, __out1, __out2) {
test("expect error on creating creating direct feed through", () => {
expect(() => {
this.connect(__in2, __out2);
}).toThrowError("New connection introduces direct feed through.");
});
test("expect error when creating connection outside container", () => {
expect(() => {
this.connect(__out2, __in2);
this.connect(__out2.asConnectable(), __in2.asConnectable());
}).toThrowError("New connection is outside of container.");
});
const R2 = new R1(this.getReactor());
test("expect error on mutation creating race condition on an output port", () => {
expect(() => {
this.connect(R2.out1, __out1);
this.connect(R2.out1.asConnectable(), __out1.asConnectable());
}).toThrowError("Destination port is already occupied.");
});
test("expect error on spawning and creating loop within a reactor", () => {
expect(() => {
this.connect(R2.out1, R2.in1);
this.connect(R2.out1.asConnectable(), R2.in1.asConnectable());
}).toThrowError("New connection introduces cycle.");
});
}
Expand Down
2 changes: 1 addition & 1 deletion __tests__/SimpleMutation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class R2 extends Reactor {
function (this, __in, __out) {
test("expect error to be thrown", () => {
expect(() => {
this.connect(__out, __in);
this.connect(__out.asConnectable(), __in.asConnectable());
}).toThrowError("New connection is outside of container.");
});
}
Expand Down
8 changes: 2 additions & 6 deletions __tests__/SingleEvent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,8 @@ describe("SingleEvent", function () {
expect(expect(seTest.singleEvent).toBeInstanceOf(SingleEvent));
expect(expect(seTest.logger).toBeInstanceOf(Logger));

expect(function () {
seTest.canConnect(seTest.singleEvent.o, seTest.logger.i);
}).toThrow(new Error("Destination port is already occupied."));
expect(seTest.canConnect(seTest.logger.i, seTest.singleEvent.o)).toBe(
false
);
expect(seTest.canConnect(seTest.singleEvent.o, seTest.logger.i)).toBeTruthy();
expect(seTest.canConnect(seTest.logger.i, seTest.singleEvent.o)).toBeTruthy();

seTest._start();
});
Expand Down
99 changes: 99 additions & 0 deletions __tests__/connection.bonus.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import {App, InPort, OutPort, Reactor} from "../src/core/internal";

// Readers might wonder why this test case exist;
// This is mainly because in the past, we assume direct feedthrough is forbidden, and will not establish the connection if we try to do so. However, it is possible that such a thing happen without introducing any causality issue.
describe("Direct feedthrough without causality issue", () => {
class BigReactor extends App {
public children: SmallReactor;

constructor() {
super(undefined, false, false);
this.children = new SmallReactor(this);
}
}

class SmallReactor extends Reactor {
public inp: InPort<number>;
public outp: OutPort<number>;

constructor(parent: Reactor) {
super(parent);
this.inp = new InPort(this);
this.outp = new OutPort(this);
this.addMutation(
[this.startup],
[this.inp, this.outp],
function (this, inp, outp) {
it("test", () => {
expect(this.getReactor().canConnect(inp, outp)).toBeFalsy();
});
}
);
}
}

const root = new BigReactor();
root._start();
});

describe("Causality loop that can't be detected by only checking local graph", () => {
class FeedThrougher extends Reactor {
public inp = new InPort(this);
public outp = new OutPort(this);

constructor(parent: Reactor) {
super(parent);
this.addReaction(
[this.inp],
[this.inp, this.writable(this.outp)],
function (this, inp, outp) {
// nop troll
}
);
}
}

class Looper extends Reactor {
public leftChild = new FeedThrougher(this);
public rightChild = new FeedThrougher(this);

public inp = new InPort(this);
public outp = new OutPort(this);

constructor(parent: Reactor) {
super(parent);
this.addMutation(
[this.startup],
[
this.inp.asConnectable(),
this.outp.asConnectable(),
this.leftChild.inp.asConnectable(),
this.leftChild.outp.asConnectable(),
this.rightChild.inp.asConnectable(),
this.rightChild.outp.asConnectable()
],
function (this, inp, outp, inp_lc, outp_lc, inp_rc, outp_rc) {
this.connect(inp, inp_lc);
this.connect(outp_rc, outp);
}
);
}
}

class TestApp extends App {
public child = new Looper(this);

constructor() {
super(undefined, undefined, undefined, () => {});
this._connect(this.child.outp, this.child.inp);
it("Test a connection that would create zero delay loop cannot be made", () => {
expect(
this.canConnect(this.child.leftChild.outp, this.child.rightChild.inp)
).toBeTruthy();
});
}
}

const app = new TestApp();
app._start();
});
9 changes: 5 additions & 4 deletions __tests__/connection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import {
OutPort,
InPort,
TimeUnit,
TimeValue
TimeValue,
CanConnectResult
} from "../src/core/internal";

describe("Check canConnect", () => {
Expand All @@ -30,19 +31,19 @@ describe("Check canConnect", () => {

it("canConnect success out->in", () => {
expect(this.canConnect(this.source.out, this.destination.in)).toBe(
true
CanConnectResult.SUCCESS
);
});

it("canConnect success out->out", () => {
expect(this.canConnect(this.source.out, this.destination.out)).toBe(
true
CanConnectResult.SUCCESS
);
});

it("canConnect failure", () => {
expect(this.canConnect(this.destination.in, this.source.out)).toBe(
false
CanConnectResult.NOT_IN_SCOPE
);
});
}
Expand Down
6 changes: 3 additions & 3 deletions __tests__/disconnect.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ class R1 extends Reactor {
const R2 = new R1(this.getReactor());
test("expect that disconnecting an existing connection will not result in an error being thrown", () => {
expect(() => {
this.connect(R2.out2, R2.in2);
this.connect(R2.out2.asConnectable(), R2.in2.asConnectable());
this.disconnect(R2.out2, R2.in2);
this.connect(R2.out2, R2.in2);
this.connect(R2.out2.asConnectable(), R2.in2.asConnectable());
this.disconnect(R2.out2);
this.connect(R2.out2, R2.in2);
this.connect(R2.out2.asConnectable(), R2.in2.asConnectable());
}).not.toThrow();
});
}
Expand Down
64 changes: 28 additions & 36 deletions __tests__/hierarchy.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Reactor, App, InPort, OutPort} from "../src/core/internal";
import {Reactor, App, InPort, OutPort, CanConnectResult} from "../src/core/internal";

class InOut extends Reactor {
a = new InPort<string>(this);
Expand Down Expand Up @@ -36,65 +36,57 @@ describe("Container to Contained", () => {
it("testing canConnect", () => {
expect(
app.container.canConnect(app.container.a, app.container.contained.a)
).toBe(true);
).toBe(CanConnectResult.SUCCESS);
expect(
app.container.canConnect(app.container.contained.a, app.container.a)
).toBe(false);
).toBe(CanConnectResult.NOT_IN_SCOPE);
expect(
app.container.canConnect(
app.container.a,
app.container.b
)
).toBe(true);
).toBe(CanConnectResult.SUCCESS);
expect(
app.container.canConnect(
app.container.contained.a,
app.container.contained.b
)
).toBe(false);
).toBeTruthy();
expect(
app.container.canConnect(
app.container.contained.b,
app.container.contained.a
)
).toBe(true);
).toBeFalsy();

expect(
app.container.canConnect(app.container.a, app.container.contained.b)
).toBe(false);
).toBeTruthy();
expect(
app.container.canConnect(app.container.contained.b, app.container.a)
).toBe(false);
).toBeTruthy();

expect(
app.container.canConnect(app.container.b, app.container.contained.a)
).toBe(false);
).toBeTruthy();
expect(
app.container.canConnect(app.container.contained.a, app.container.b)
).toBe(false);
).toBeTruthy();

expect(
app.container.canConnect(app.container.b, app.container.contained.b)
).toBe(false);
).toBeTruthy();
expect(
app.container.canConnect(app.container.contained.b, app.container.b)
).toBe(true);

expect(app.container.canConnect(app.container.contained.a, app.foo.a)).toBe(
false
);
expect(app.container.canConnect(app.container.contained.a, app.foo.b)).toBe(
false
);
expect(app.container.canConnect(app.foo.a, app.container.contained.a)).toBe(
false
);
expect(app.container.canConnect(app.foo.a, app.container.contained.a)).toBe(
false
);

expect(app.container.canConnect(app.foo.a, app.container.b)).toBe(false);
expect(app.container.canConnect(app.foo.a, app.container.a)).toBe(false);
).toBeFalsy();

expect(app.container.canConnect(app.container.contained.a, app.foo.a)).toBeTruthy();
expect(app.container.canConnect(app.container.contained.a, app.foo.b)).toBeTruthy();
expect(app.container.canConnect(app.foo.a, app.container.contained.a)).toBeTruthy();
expect(app.container.canConnect(app.foo.a, app.container.contained.a)).toBeTruthy();

expect(app.container.canConnect(app.foo.a, app.container.b)).toBeTruthy();
expect(app.container.canConnect(app.foo.a, app.container.a)).toBeTruthy();

// expect(app.container.contained).toBeDefined();

Expand All @@ -104,49 +96,49 @@ describe("Container to Contained", () => {
app.container.contained.containedAgain.a,
app.container.contained.a
)
).toBe(false);
).toBeTruthy();
expect(
app.container.contained.canConnect(
app.container.contained.containedAgain.b,
app.container.contained.b
)
).toBe(true);
).toBeFalsy();
expect(
app.container.contained.canConnect(
app.container.contained.containedAgain.a,
app.container.a
)
).toBe(false);
).toBeTruthy();
expect(
app.container.contained.canConnect(
app.container.contained.containedAgain.b,
app.container.b
)
).toBe(false);
).toBeTruthy();
expect(
app.container.contained.canConnect(
app.container.contained.containedAgain.a,
app.foo.a
)
).toBe(false);
).toBeTruthy();
expect(
app.container.contained.canConnect(
app.container.contained.containedAgain.b,
app.foo.b
)
).toBe(false);
).toBeTruthy();
expect(
app.container.contained.canConnect(
app.container.contained.containedAgain.a,
app.foo.a
)
).toBe(false);
).toBeTruthy();
expect(
app.container.contained.canConnect(
app.container.contained.containedAgain.b,
app.foo.b
)
).toBe(false);
).toBeTruthy();
// }
});
});
Loading

0 comments on commit 8eee9e2

Please sign in to comment.