| 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);            });        });    });});
 |