New tests and bug fixes for patch and mux

This commit is contained in:
Tony Garnock-Jones 2016-02-02 15:52:48 -05:00
parent d1b3ffdf81
commit f22e228cc0
5 changed files with 222 additions and 14 deletions

View File

@ -42,16 +42,17 @@ Mux.prototype.updateStream = function (pid, unclampedPatch) {
deltaAggregate: deltaAggregate };
};
function computePatches(oldMux, newMux, updateStreamResult) {
function computeEvents(oldMux, newMux, updateStreamResult) {
var actingPid = updateStreamResult.pid;
var delta = updateStreamResult.delta;
var deltaAggregate = updateStreamResult.deltaAggregate;
var oldRoutingTable = oldMux.routingTable;
var newRoutingTable = newMux.routingTable;
var affectedPids = computeAffectedPids(oldRoutingTable, delta).remove("meta");
var affectedPids = computeAffectedPids(oldRoutingTable, delta).add(actingPid).remove("meta");
return {
eventMap: Immutable.Map().withMutations(function (result) {
affectedPids.forEach(function (pid) {
var patchForPid;
if (pid === actingPid) {
var part1 = new Patch.Patch(Patch.biasedIntersection(newRoutingTable, delta.added),
Patch.biasedIntersection(oldRoutingTable, delta.removed));
@ -59,9 +60,12 @@ function computePatches(oldMux, newMux, updateStreamResult) {
newMux.interestsOf(pid)),
Patch.biasedIntersection(deltaAggregate.removed,
oldMux.interestsOf(pid)));
results.set(pid, part1.unsafeUnion(part2));
patchForPid = part1.unsafeUnion(part2);
} else {
result.set(pid, updateStreamResult.deltaAggregate.viewFrom(oldMux.interestsOf(pid)));
patchForPid = updateStreamResult.deltaAggregate.viewFrom(oldMux.interestsOf(pid));
}
if (patchForPid.isNonEmpty()) {
result.set(pid, patchForPid);
}
});
}),
@ -97,5 +101,5 @@ Mux.prototype.interestsOf = function (pid) {
///////////////////////////////////////////////////////////////////////////
module.exports.Mux = Mux;
module.exports.computePatches = computePatches;
module.exports.computeEvents = computeEvents;
module.exports.computeAffectedPids = computeAffectedPids;

View File

@ -63,6 +63,11 @@ function unpub(p, metaLevel) {
///////////////////////////////////////////////////////////////////////////
Patch.prototype.equals = function (other) {
if (!(other instanceof Patch)) return false;
return Immutable.is(this.added, other.added) && Immutable.is(this.removed, other.removed);
};
Patch.prototype.isEmpty = function () {
return this.added === Route.emptyTrie && this.removed === Route.emptyTrie;
};

View File

@ -656,7 +656,12 @@ function trieStep(m, key) {
if (is_emptyTrie(m)) return emptyTrie;
if (m instanceof $WildcardSequence) return (is_keyClose(key) ? m.trie : m);
if (m instanceof $Success) return emptyTrie;
return rlookupWild(m, key) || rlookup(m, __);
var result = rlookupWild(m, key);
if (result) return result;
var wildEdge = rlookup(m, __);
if (is_keyOpen(key)) return rwildseq(wildEdge);
if (is_keyClose(key)) return (wildEdge instanceof $WildcardSequence) ? wildEdge.trie : emptyTrie;
return wildEdge;
}
function relabel(m, f) {
@ -1025,7 +1030,7 @@ function prettyTrie(m, initialIndent) {
module.exports.__ = __;
module.exports.SOA = SOA;
module.exports.EOA = SOA;
module.exports.EOA = EOA;
module.exports.$Capture = $Capture;
module.exports.$Special = $Special;
module.exports._$ = _$;
@ -1050,3 +1055,11 @@ module.exports.trieKeys = trieKeys;
module.exports.trieKeysToObjects = trieKeysToObjects;
module.exports.projectObjects = projectObjects;
module.exports.prettyTrie = prettyTrie;
// For testing
module.exports._testing = {
rsuccess: rsuccess,
rseq: rseq,
rwild: rwild,
rwildseq: rwildseq
};

View File

@ -19,14 +19,14 @@ function checkPrettyPatch(p, expectedAdded, expectedRemoved) {
'>>>>>>>>\n'));
}
describe('mux stream', function () {
function getM() {
var m = new Mux.Mux();
expect(m.addStream(Patch.assert(1).andThen(Patch.assert(2))).pid).to.equal(0);
expect(m.addStream(Patch.assert(3).andThen(Patch.assert(2))).pid).to.equal(1);
return m;
}
function getM() {
var m = new Mux.Mux();
expect(m.addStream(Patch.assert(1).andThen(Patch.assert(2))).pid).to.equal(0);
expect(m.addStream(Patch.assert(3).andThen(Patch.assert(2))).pid).to.equal(1);
return m;
}
describe('mux stream', function () {
describe('addition', function () {
it('should union interests appropriately', function () {
var m = getM();
@ -75,5 +75,183 @@ describe('mux stream', function () {
[' 3 >{[1]}']);
});
});
describe('removal', function () {
it('should remove streams properly', function () {
var m = getM();
var updateStreamResult = m.removeStream(1);
expect(updateStreamResult.pid).to.equal(1);
checkPrettyPatch(updateStreamResult.delta,
['::: nothing'],
[' 2 >{[1]}',
' 3 >{[1]}']);
checkPrettyTrie(m.routingTable, [' 1 >{[0]}',
' 2 >{[0]}']);
checkPrettyTrie(m.interestsOf(0), [' 1 >{[0]}',
' 2 >{[0]}']);
checkPrettyTrie(m.interestsOf(1), ['::: nothing']);
checkPrettyPatch(updateStreamResult.deltaAggregate,
['::: nothing'],
[' 3 >{[1]}']);
});
});
});
describe('computeEvents', function () {
describe('for new assertions and existing specific interest', function () {
var oldM = new Mux.Mux();
oldM.addStream(Patch.sub(1));
var newM = oldM.shallowCopy();
var updateStreamResult = newM.addStream(Patch.assert(1).andThen(Patch.assert(2)));
var events = Mux.computeEvents(oldM, newM, updateStreamResult);
it('should yield just the assertion of interest', function () {
expect(events.eventMap.size).to.be(1);
expect(events.metaEvents.size).to.be(1);
expect(events.eventMap.get(0).strip().equals(Patch.assert(1))).to.be(true);
expect(events.metaEvents.get(0).equals(Patch.emptyPatch)).to.be(true);
});
});
describe('for removed assertions', function () {
var oldM = new Mux.Mux();
oldM.addStream(Patch.sub([__]));
var newM = oldM.shallowCopy();
newM.addStream(Patch.assert([1]).andThen(Patch.assert([2])));
var updateStreamResult = newM.updateStream(1, Patch.retract([1]));
var events = Mux.computeEvents(oldM, newM, updateStreamResult);
it('should yield just the specific retraction', function () {
expect(events.eventMap.size).to.be(1);
expect(events.metaEvents.size).to.be(1);
expect(events.eventMap.get(0).strip().equals(Patch.retract([1]))).to.be(true);
expect(events.metaEvents.get(0).equals(Patch.emptyPatch)).to.be(true);
});
});
describe('for new assertions and existing general interest', function () {
var oldM = new Mux.Mux();
oldM.addStream(Patch.sub([__]));
var newM = oldM.shallowCopy();
var updateStreamResult = newM.addStream(Patch.assert([1]).andThen(Patch.assert([2])));
var events = Mux.computeEvents(oldM, newM, updateStreamResult);
it('should yield both new assertions', function () {
expect(events.eventMap.size).to.be(1);
expect(events.metaEvents.size).to.be(1);
expect(events.eventMap.get(0).strip().equals(Patch.assert([1]).andThen(Patch.assert([2]))))
.to.be(true);
expect(events.metaEvents.get(0).equals(Patch.emptyPatch)).to.be(true);
});
})
describe('for new specific interest and existing assertion', function () {
var oldM = new Mux.Mux();
oldM.addStream(Patch.assert(1).andThen(Patch.assert(2)));
var newM = oldM.shallowCopy();
var updateStreamResult = newM.addStream(Patch.sub(1));
var events = Mux.computeEvents(oldM, newM, updateStreamResult);
it('should yield just the assertion of interest', function () {
expect(events.eventMap.size).to.be(1);
expect(events.metaEvents.size).to.be(1);
expect(events.eventMap.get(1).strip().equals(Patch.assert(1))).to.be(true);
expect(events.metaEvents.get(0).equals(Patch.emptyPatch)).to.be(true);
});
});
describe('for new general interest and existing assertion', function () {
var oldM = new Mux.Mux();
oldM.addStream(Patch.assert([1]).andThen(Patch.assert([2])));
var newM = oldM.shallowCopy();
var updateStreamResult = newM.addStream(Patch.sub([__]));
var events = Mux.computeEvents(oldM, newM, updateStreamResult);
it('should yield just the assertion of interest', function () {
expect(events.eventMap.size).to.be(1);
expect(events.metaEvents.size).to.be(1);
expect(events.eventMap.get(1).strip().equals(Patch.assert([1]).andThen(Patch.assert([2]))))
.to.be(true);
expect(events.metaEvents.get(0).equals(Patch.emptyPatch)).to.be(true);
});
});
describe('for removed general interest', function () {
var oldM = new Mux.Mux();
oldM.addStream(Patch.assert([1]).andThen(Patch.assert([2])));
oldM.addStream(Patch.sub([__]));
var newM = oldM.shallowCopy();
var updateStreamResult = newM.updateStream(1, Patch.unsub([__]));
var events = Mux.computeEvents(oldM, newM, updateStreamResult);
it('should yield both retractions', function () {
expect(events.eventMap.size).to.be(1);
expect(events.metaEvents.size).to.be(1);
expect(events.eventMap.get(1).strip().equals(Patch.retract([1]).andThen(Patch.retract([2]))))
.to.be(true);
expect(events.metaEvents.get(0).equals(Patch.emptyPatch)).to.be(true);
});
});
describe('for removed specific interest', function () {
it('should yield three appropriate events for three intercessions', function () {
var oldM = new Mux.Mux();
oldM.addStream(Patch.assert([1]).andThen(Patch.assert([2])));
oldM.addStream(Patch.sub([__]));
var newM = oldM.shallowCopy();
var updateStreamResult = newM.updateStream(1, Patch.unsub([1]));
var events = Mux.computeEvents(oldM, newM, updateStreamResult);
expect(events.eventMap.size).to.be(1);
expect(events.metaEvents.size).to.be(1);
expect(events.eventMap.get(1).strip().equals(Patch.retract([1]))).to.be(true);
expect(events.metaEvents.get(0).equals(Patch.emptyPatch)).to.be(true);
oldM = newM;
newM = oldM.shallowCopy();
events = Mux.computeEvents(oldM, newM, newM.updateStream(1, Patch.unsub([2])));
expect(events.eventMap.size).to.be(1);
expect(events.metaEvents.size).to.be(1);
expect(events.eventMap.get(1).strip().equals(Patch.retract([2])))
.to.be(true);
expect(events.metaEvents.get(0).equals(Patch.emptyPatch)).to.be(true);
oldM = newM;
newM = oldM.shallowCopy();
events = Mux.computeEvents(oldM, newM, newM.updateStream(0, Patch.assert([3])));
expect(events.eventMap.size).to.be(1);
expect(events.metaEvents.size).to.be(1);
expect(events.eventMap.get(1).strip().equals(Patch.assert([3]))).to.be(true);
expect(events.metaEvents.get(0).equals(Patch.emptyPatch)).to.be(true);
});
});
describe('for new meta assertion', function () {
var oldM = new Mux.Mux();
var newM = oldM.shallowCopy();
var updateStreamResult = newM.addStream(Patch.assert(Patch.atMeta(1)).andThen(Patch.assert(2)));
var events = Mux.computeEvents(oldM, newM, updateStreamResult);
it('should yield no local events and one meta event', function () {
expect(events.eventMap.size).to.be(0);
expect(events.metaEvents.size).to.be(1);
expect(events.metaEvents.get(0).strip().equals(Patch.assert(1))).to.be(true);
});
});
describe('for adding supply and demand simultaneously', function () {
var oldM = new Mux.Mux();
var newM = oldM.shallowCopy();
var updateStreamResult = newM.addStream(Patch.assert(1).andThen(Patch.sub(1)));
var events = Mux.computeEvents(oldM, newM, updateStreamResult);
it('should send a patch back', function () {
expect(events.eventMap.size).to.be(1);
expect(events.metaEvents.size).to.be(1);
expect(events.eventMap.get(0).strip().equals(Patch.assert(1))).to.be(true);
expect(events.metaEvents.get(0).equals(Patch.emptyPatch)).to.be(true);
});
});
});

View File

@ -408,3 +408,11 @@ describe("Projection with no captures", function () {
[' "X" ★ >{["A"]}']);
});
});
describe('trieStep', function () {
it('should expand wildcard when given SOA', function () {
expect(Immutable.is(r.trieStep(r.compilePattern(true, r.__), r.SOA),
r._testing.rwildseq(r._testing.rseq(r.EOA, r._testing.rsuccess(true)))))
.to.be(true);
});
});