diff --git a/__tests__/InvalidMutations.ts b/__tests__/InvalidMutations.ts index d4964d4c1..5bab815ba 100644 --- a/__tests__/InvalidMutations.ts +++ b/__tests__/InvalidMutations.ts @@ -43,23 +43,49 @@ class R1 extends Reactor { this.addMutation( new Triggers(this.in1), new Args(this.in1, this.out2), - function(this, __in, __out) { - // test('expect error to be thrown on mutation creating loop', () => { + function(this, __in1, __out2) { + test('expect error to be thrown on mutation creating loop', () => { + expect(() => { + this.connect(__out2, __in1) + }).toThrowError("New connection introduces cycle.") + expect(() => { + this.connect(__in1, __out2) + }).toThrowError("New connection introduces direct feed through.") // dist port already in use + + // let R2 = new R1(this.getReactor()) + // expect(() => { + // // this.connect(R2.out1, R2.in1) + // // this.connect(R2.in1, R2.out1) + // // this.connect(R2.in1, R2.out1) + // this.connect(R2.out1, R2.in1) + // }).toThrowError("New connection introduces cycle.") + }) + // console.log("TEST START!") + // test('expect error on mutation creating race condition', () => { // expect(() => { - // this.connect(__out, __in) - // }).toThrowError("ERROR connecting " + __out + " to " + __in) + // //this.canConnect(__in1) + // // console.log("Can Connect: ", this.getReactor().canConnect(__in1, __out2)) + + // this.connect(__in1, __out2) + // }).toThrowError("New connection introduces direct feed through.") + // }) + // let R2 = new R1(this.getReactor()) + // test('expect error on spawning and creating loop within a reactor', () => { + // expect(() => { + // this.connect(__out2, __in1) + // }).toThrowError("New connection introduces cycle.") // }) test('expect error on mutation creating race condition', () => { expect(() => { - this.connect(__in, __out) - }).toThrowError("ERROR connecting " + __out + " to " + __in) + this.connect(__in1, __out2) + }).toThrowError("New connection introduces direct feed through.") }) let R2 = new R1(this.getReactor()) test('expect error on spawning and creating loop within a reactor', () => { expect(() => { this.connect(R2.in1, R2.out1) this.connect(R2.out1, R2.in1) - }).toThrowError("ERROR connecting " + R2.out1 + " to " + R2.in1) + }).toThrowError("New connection introduces cycle.") }) } ) @@ -82,4 +108,3 @@ class testApp extends App { var app = new testApp() app._start() - diff --git a/src/core/graph.ts b/src/core/graph.ts index 3f0d8ab3b..2eb81495e 100644 --- a/src/core/graph.ts +++ b/src/core/graph.ts @@ -184,27 +184,14 @@ export class DependencyGraph { * @param current The current node being visited. */ function search(current: T) { + visited.add(current) + if (origins.has(current)) reachable.add(current) for (let next of self.getEdges(current)) { - if (!visited.has(current)) { - // Do not visit a node twice. - if (origins.has(current)) { - // If the current node is among the origins searched - // for, add it to the reachable set. - reachable.add(current) - } - // Continue search, depth first. - if (reachable.size == origins.size) { - search(next) - } else { - // Preempt search of all origins are reachable. - return - } - } + if (!visited.has(next)) search(next) } } search(effect) - return reachable } diff --git a/src/core/reactor.ts b/src/core/reactor.ts index 0ae17034b..0eeace5a4 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -944,7 +944,7 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { (src: IOPort, dst: IOPort) { // Immediate rule out trivial self loops. if (src === dst) { - return false + throw Error("Source port and destination port are the same.") } if (this._runtime.isRunning() == false) { @@ -956,15 +956,15 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { // Rule out write conflicts. // - (between reactors) if (this._dependencyGraph.getBackEdges(dst).size > 0) { - return false; + throw Error("Destination port is already occupied.") } - // - between reactors and reactions (NOTE: check also needs to happen - // in addReaction) - var deps = this._dependencyGraph.getEdges(dst) // FIXME this will change with multiplex ports - if (deps != undefined && deps.size > 0) { - return false; - } + // // - between reactors and reactions (NOTE: check also needs to happen + // // in addReaction) + // var deps = this._dependencyGraph.getEdges(dst) // FIXME this will change with multiplex ports + // if (deps != undefined && deps.size > 0) { + // return false; + // } return this._isInScope(src, dst) @@ -983,25 +983,57 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { } // Add the new edge. + // Add edge when we are able to add graph + // if(isAbleToAdd(dst, src)) graph.addEdge(dst, src) graph.addEdge(dst, src) // 1) check for loops + let hasCycle = false if (graph.hasCycle()) { - return false + hasCycle = true + // throw Error("New connection introduces cycle.") } // 2) check for direct feed through. + let hasDirectFeedThrough = false let inputs = this._findOwnInputs() - for (let output of this._findOwnOutputs()) { + let outputs = this._findOwnOutputs() + + + for (let output of outputs) { let newReachable = graph.reachableOrigins(output, inputs) let oldReachable = this._causalityGraph.reachableOrigins(output, inputs) + // Print newReachable and oldReachable origin + for (let new_ori of newReachable) { + if (new_ori instanceof Port) { + console.log("New Origin: ", new_ori._getFullyQualifiedName()) + } + } + for (let old_ori of oldReachable) { + if (old_ori instanceof Port) { + console.log("Old Origin: ", old_ori._getFullyQualifiedName()) + } + } + console.log("!!!") + for (let origin of newReachable) { if (origin instanceof Port && !oldReachable.has(origin)) { - return false + hasDirectFeedThrough = true + // throw Error("New connection introduces direct feed through.") } } } + + // Throw error three cases: 1. cycle / 2. direct feed through / 3. both + if (hasDirectFeedThrough && hasCycle) { + throw Error("New connection introduces direct feed through and cycle.") + } else if (hasCycle) { + throw Error("New connection introduces cycle.") + } else if (hasDirectFeedThrough) { + throw Error("New connection introduces direct feed through.") + } + return true } } @@ -1078,11 +1110,9 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { if (dst === undefined || dst === null) { throw new Error("Cannot connect unspecified destination"); } - if (this.canConnect(src, dst)) { - this._uncheckedConnect(src, dst); - } else { - throw new Error("ERROR connecting " + src + " to " + dst); - } + this.canConnect(src, dst); + console.log("Performing connect.") + this._uncheckedConnect(src, dst); } protected _connectMulti( @@ -1251,7 +1281,7 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { private _findOwnOutputs() { let outputs = new Set>() for (let component of this._keyChain.keys()) { - if (component instanceof InPort) { + if (component instanceof OutPort) { outputs.add(component) } }