123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- if (S.subclock) {
- describe("S.subclock()", () => {
- it("runs computations to completion before they are visible to surrounding code", function () {
- S.root(() => {
- var d = S.data(1),
- f = S.subclock()(() => {
- var out = S.data(2);
- S(() => d() >= 10 || out(d()));
- return out;
- }),
- c = S.on(f, c => c + 1, 0);
- expect(f()).toBe(1);
- expect(c()).toBe(1);
- d(2);
- expect(f()).toBe(2);
- expect(c()).toBe(2);
- // setting d() to > 10 does not update f()
- d(12);
- expect(f()).toBe(2);
- expect(c()).toBe(2);
- // going back < 10 does update f()
- d(8);
- expect(f()).toBe(8);
- expect(c()).toBe(3);
- });
- });
- it("descendant computations see all states", () => {
- S.root(() => {
- var { d, f } = S.subclock(() => {
- var d = S.data(5),
- f = S.subclock(() =>
- S.on(d, c => c + 1, 0)
- );
- S(() => d() >= 5 || d(d() + 1));
- return { d, f };
- });
-
- expect(f()).toBe(1);
- d(1);
- expect(f()).toBe(6);
- });
- });
-
- it("ancestor computations only see final state", function () {
- S.root(function () {
- var { d, f } = S.subclock(() => {
- var d = S.subclock(() => {
- var d = S.data(5);
- S(() => d() >= 5 || d(d() + 1));
- return d;
- }),
- f = S.on(d, c => c + 1, 0);
- return { d, f };
- });
-
- expect(d()).toBe(5);
- expect(f()).toBe(1);
- d(1);
- expect(d()).toBe(5);
- expect(f()).toBe(2);
- });
- });
- it("sibling processes and nodes only see final state", function () {
- S.root(()=>{
- var d = S.data("abcdefgh"),
- c1 = S.subclock(()=>{
- var d1 = S.data(""),
- n1 = S(() => d1(d())),
- n2 = S(() => d1().length < 6 || d1(d1().substr(1)));
- return { d1, n1, n2 };
- }),
- c2 = S.subclock(()=>{
- var d1 = S.data(""),
- n1 = S(() => d1(c1.d1())),
- n2 = S(() => d1().length < 4 || d1(d1().substr(0, d1().length - 1)));
- return { d1, n1, n2 };
- }),
- n3 = S(() => c2.d1()),
- n3c = S.on(n3, c => c + 1, 0);
-
- expect(d()).toBe("abcdefgh");
- expect(c1.d1()).toBe("defgh");
- expect(c2.d1()).toBe("def");
- expect(n3()).toBe("def");
- expect(n3c()).toBe(1);
- d("1234567");
- expect(c1.d1()).toBe("34567");
- expect(c2.d1()).toBe("345");
- expect(n3()).toBe("345");
- expect(n3c()).toBe(2);
- });
- });
- it("can reference computions in a higher clock", function () {
- S.root(() => {
- var a = S.data(1),
- b = S(() => a()),
- c = S.subclock(() =>
- S(() => a() + b())
- );
- a(2);
- expect(c()).toBe(4);
- });
- });
- it("allows mutations from inside and outside a subclock", function () {
- S.root(() => {
- var sub = S.subclock(() => {
- var a = S.data(""),
- b = S(() => (a().length % 3) === 0 || a(a() + "i"));
- return { a };
- }),
- c = S(() => (sub.a().length % 5) === 0 || sub.a(sub.a() + "o"));
- expect(sub.a()).toBe("");
- sub.a("g");
- // doesn't stabilize until a().length is divisible by both 3 and 5, i.e. length of 15
- expect(sub.a()).toBe("giioiioiioiioii");
- });
- });
- it("throw an exception when there's a circular dependency between a computation and a clock", function () {
- S.root(() => {
- var a = S.data(true),
- b = S(() => a() || sub.c()), // b() depends on sub when a() is false
- sub = S.subclock(() => ({
- c: S(() => a()),
- d: S(() => b()) // sub depends on b()
- }));
- expect(() => a(false)).toThrowError(/circular/);
- });
- });
- it("throw an exception when there's a circular dependency between two clocks", function () {
- S.root(() => {
- var a = S.data(true),
- sub1 = S.subclock(() => ({
- b: S(() => a() || sub2.c()) // sub1 depends on sub2 when a() is false
- })),
- sub2 = S.subclock(() => ({
- c: S(() => sub1.b()) // sub2 depends on sub1
- }));
- expect(() => a(false)).toThrowError(/circular/);
- });
- });
- it("runs any changes in outer clocks when created at top level", function () {
- S.root(() => {
- var outer = S.data(1);
- S.subclock(() => {
- outer(2);
- });
- expect(outer()).toBe(2);
- outer(3);
- });
- });
- it("runs any changes in sibling clocks when created at top level", function () {
- S.root(() => {
- var outer = S.subclock(() => S.data(1));
- S.subclock(() => {
- outer(2);
- });
- expect(outer()).toBe(2);
- outer(3);
- expect(outer()).toBe(3);
- });
- });
- it("handles whiteboard case", function () {
- S.root(function () {
- var t = "",
- d = S.data(1),
- p1 = S.subclock()(function () {
- var c1 = S(() => {
- t += 'p1c1,';
- return d() + (d() > 1 ? c2() : 0);
- });
- return { c1 };
- }),
- p2 = S.subclock()(function () {
- var d1 = S.data(1),
- c1 = S(() => {
- t += 'p2c1,';
- return d1(d());
- });
- return { d1, c1 };
- }),
- p3 = S.subclock()(function () {
- var d1 = S.data(1),
- c1 = S(() => {
- t += 'p3c1,';
- d1(p2.d1());
- });
- return { d1, c1 };
- }),
- c1 = S(() => {
- t += 'c1,';
- return p3.d1();
- }),
- c2 = S(() => {
- t += 'c2,';
- return c1();
- });
-
- expect(p1.c1()).toBe(1);
- t = '';
- d(2);
- expect(p1.c1()).toBe(4);
- expect(t).toBe('p1c1,p2c1,p3c1,c2,c1,');
- t = '';
- d(1);
- expect(p1.c1()).toBe(1);
- expect(t).toBe('p2c1,p3c1,p1c1,c1,c2,');
- t = '';
- d(3);
- expect(p1.c1()).toBe(6);
- expect(t).toBe('p1c1,p2c1,p3c1,c2,c1,');
- t = '';
- d(2);
- expect(p1.c1()).toBe(4);
- expect(t).toBe('p2c1,p3c1,p1c1,c2,c1,');
- t = '';
- d(2);
- expect(p1.c1()).toBe(4);
- expect(t).toBe('p2c1,p3c1,p1c1,c2,c1,');
- t = '';
- d(5);
- expect(p1.c1()).toBe(10);
- expect(t).toBe('p2c1,p3c1,p1c1,c2,c1,');
- });
- });
- it("handles irisjay test case", function () {
- S.root(() => {
- var A = S.data(0),
- log = S.data();
- S.subclock(() => {
- S(() => log(A() + ' logged'));
- })
- var logs = [];
- S(() => logs.push(log()));
-
- expect(logs).toEqual(["0 logged"]);
- A(1);
- expect(logs).toEqual(["0 logged", "1 logged"]);
- A(8);
- expect(logs).toEqual(["0 logged", "1 logged", "8 logged"]);
- });
- });
- });
- }
|