123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- /* global S, describe, it, expect, beforeEach, jasmine */
- describe("S()", function () {
- describe("creation", function () {
- it("throws if no function passed in", function () {
- S.root(function () {
- expect(function() { S(); }).toThrow();
- });
- });
- it("throws if arg is not a function", function () {
- S.root(function () {
- expect(function() { S(1); }).toThrow();
- });
- });
- it("generates a function", function () {
- S.root(function () {
- var f = S(function () { return 1; });
- expect(f).toEqual(jasmine.any(Function));
- });
- });
- it("returns initial value of wrapped function", function () {
- S.root(function () {
- var f = S(function () { return 1; });
- expect(f()).toBe(1);
- });
- });
- });
- describe("evaluation", function () {
- it("occurs once intitially", function () {
- S.root(function () {
- var spy = jasmine.createSpy(),
- f = S(spy);
- expect(spy.calls.count()).toBe(1);
- });
- });
- it("does not re-occur when read", function () {
- S.root(function () {
- var spy = jasmine.createSpy(),
- f = S(spy);
- f(); f(); f();
- expect(spy.calls.count()).toBe(1);
- });
- });
- });
- describe("with a dependency on an S.data", function () {
- it("updates when S.data is set", function () {
- S.root(function () {
- var d = S.data(1),
- fevals = 0,
- f = S(function () { fevals++; return d(); });
- fevals = 0;
- d(1);
- expect(fevals).toBe(1);
- });
- });
- it("does not update when S.data is read", function () {
- S.root(function () {
- var d = S.data(1),
- fevals = 0,
- f = S(function () { fevals++; return d(); });
- fevals = 0;
- d();
- expect(fevals).toBe(0);
- });
- });
- it("updates return value", function () {
- S.root(function () {
- var d = S.data(1),
- fevals = 0,
- f = S(function () { fevals++; return d(); });
- fevals = 0;
- d(2);
- expect(f()).toBe(2);
- });
- });
- });
- describe("with changing dependencies", function () {
- var i, t, e, fevals, f;
- function init() {
- i = S.data(true);
- t = S.data(1);
- e = S.data(2);
- fevals = 0;
- f = S(function () { fevals++; return i() ? t() : e(); });
- fevals = 0;
- }
- it("updates on active dependencies", function () {
- S.root(function () {
- init();
- t(5);
- expect(fevals).toBe(1);
- expect(f()).toBe(5);
- });
- });
- it("does not update on inactive dependencies", function () {
- S.root(function () {
- init();
- e(5);
- expect(fevals).toBe(0);
- expect(f()).toBe(1);
- });
- });
- it("deactivates obsolete dependencies", function () {
- S.root(function () {
- init();
- i(false);
- fevals = 0;
- t(5);
- expect(fevals).toBe(0);
- });
- });
- it("activates new dependencies", function () {
- S.root(function () {
- init();
- i(false);
- fevals = 0;
- e(5);
- expect(fevals).toBe(1);
- });
- });
- it("insures that new dependencies are updated before dependee", function () {
- S.root(function () {
- var order = "",
- a = S.data(0),
- b = S(function () { order += "b"; return a() + 1; }),
- c = S(function () { order += "c"; return b() || d(); }),
- d = S(function () { order += "d"; return a() + 10; });
- expect(order).toBe("bcd");
- order = "";
- a(-1);
- expect(order).toBe("bcd");
- expect(c()).toBe(9);
- order = "";
- a(0);
- expect(order).toBe("bcd");
- expect(c()).toBe(1);
- });
- });
- });
- describe("that creates an S.data", function () {
- it("does not register a dependency", function () {
- S.root(function () {
- var fevals = 0,
- f = S(function () { fevals++; d = S.data(1); });
- fevals = 0;
- d(2);
- expect(fevals).toBe(0);
- });
- });
- });
- describe("from a function with no return value", function () {
- it("reads as undefined", function () {
- S.root(function () {
- var f = S(function () { });
- expect(f()).not.toBeDefined();
- });
- });
- });
- describe("with a seed", function () {
- it("reduces seed value", function () {
- S.root(function () {
- var a = S.data(5),
- f = S(function (v) { return v + a(); }, 5);
- expect(f()).toBe(10);
- a(6);
- expect(f()).toBe(16);
- });
- });
- });
- describe("with a dependency on a computation", function () {
- var d, fcount, f, gcount, g;
- function init() {
- d = S.data(1),
- fcount = 0,
- f = S(function () { fcount++; return d(); }),
- gcount = 0,
- g = S(function () { gcount++; return f(); });
- }
- it("does not cause re-evaluation", function () {
- S.root(function () {
- init();
- expect(fcount).toBe(1);
- });
- });
- it("does not occur from a read", function () {
- S.root(function () {
- init();
- f();
- expect(gcount).toBe(1);
- });
- });
- it("does not occur from a read of the watcher", function () {
- S.root(function () {
- init();
- g();
- expect(gcount).toBe(1);
- });
- });
- it("occurs when computation updates", function () {
- S.root(function () {
- init();
- d(2);
- expect(fcount).toBe(2);
- expect(gcount).toBe(2);
- expect(g()).toBe(2);
- });
- });
- });
- describe("with unending changes", function () {
- it("throws when continually setting a direct dependency", function () {
- S.root(function () {
- var d = S.data(1);
- expect(function () {
- S(function () { d(); d(2); });
- }).toThrow();
- });
- });
- it("throws when continually setting an indirect dependency", function () {
- S.root(function () {
- var d = S.data(1),
- f1 = S(function () { return d(); }),
- f2 = S(function () { return f1(); }),
- f3 = S(function () { return f2(); });
- expect(function () {
- S(function () { f3(); d(2); });
- }).toThrow();
- });
- });
- });
-
- describe("with circular dependencies", function () {
- it("throws when cycle created by modifying a branch", function () {
- S.root(function () {
- var d = S.data(1),
- f = S(function () { return f ? f() : d(); });
- expect(function () { d(0); }).toThrow();
- });
- });
- });
- describe("with converging dependencies", function () {
- it("propagates in topological order", function () {
- S.root(function () {
- //
- // c1
- // / \
- // / \
- // b1 b2
- // \ /
- // \ /
- // a1
- //
- var seq = "",
- a1 = S.data(true),
- b1 = S(function () { a1(); seq += "b1"; }),
- b2 = S(function () { a1(); seq += "b2"; }),
- c1 = S(function () { b1(), b2(); seq += "c1"; });
-
- seq = "";
- a1(true);
-
- expect(seq).toBe("b1b2c1");
- });
- });
- it("only propagates once with linear convergences", function () {
- S.root(function () {
- // d
- // |
- // +---+---+---+---+
- // v v v v v
- // f1 f2 f3 f4 f5
- // | | | | |
- // +---+---+---+---+
- // v
- // g
- var d = S.data(0),
- f1 = S(function () { return d(); }),
- f2 = S(function () { return d(); }),
- f3 = S(function () { return d(); }),
- f4 = S(function () { return d(); }),
- f5 = S(function () { return d(); }),
- gcount = 0,
- g = S(function () { gcount++; return f1() + f2() + f3() + f4() + f5(); });
- gcount = 0;
- d(0);
- expect(gcount).toBe(1);
- });
- });
- it("only propagates once with exponential convergence", function () {
- S.root(function () {
- // d
- // |
- // +---+---+
- // v v v
- // f1 f2 f3
- // \ | /
- // O
- // / | \
- // v v v
- // g1 g2 g3
- // +---+---+
- // v
- // h
- var d = S.data(0),
- f1 = S(function () { return d(); }),
- f2 = S(function () { return d(); }),
- f3 = S(function () { return d(); }),
-
- g1 = S(function () { return f1() + f2() + f3(); }),
- g2 = S(function () { return f1() + f2() + f3(); }),
- g3 = S(function () { return f1() + f2() + f3(); }),
-
- hcount = 0,
- h = S(function () { hcount++; return g1() + g2() + g3(); });
- hcount = 0;
- d(0);
- expect(hcount).toBe(1);
- });
- });
- });
- });
|