S.subclock.spec.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. if (S.subclock) {
  2. describe("S.subclock()", () => {
  3. it("runs computations to completion before they are visible to surrounding code", function () {
  4. S.root(() => {
  5. var d = S.data(1),
  6. f = S.subclock()(() => {
  7. var out = S.data(2);
  8. S(() => d() >= 10 || out(d()));
  9. return out;
  10. }),
  11. c = S.on(f, c => c + 1, 0);
  12. expect(f()).toBe(1);
  13. expect(c()).toBe(1);
  14. d(2);
  15. expect(f()).toBe(2);
  16. expect(c()).toBe(2);
  17. // setting d() to > 10 does not update f()
  18. d(12);
  19. expect(f()).toBe(2);
  20. expect(c()).toBe(2);
  21. // going back < 10 does update f()
  22. d(8);
  23. expect(f()).toBe(8);
  24. expect(c()).toBe(3);
  25. });
  26. });
  27. it("descendant computations see all states", () => {
  28. S.root(() => {
  29. var { d, f } = S.subclock(() => {
  30. var d = S.data(5),
  31. f = S.subclock(() =>
  32. S.on(d, c => c + 1, 0)
  33. );
  34. S(() => d() >= 5 || d(d() + 1));
  35. return { d, f };
  36. });
  37. expect(f()).toBe(1);
  38. d(1);
  39. expect(f()).toBe(6);
  40. });
  41. });
  42. it("ancestor computations only see final state", function () {
  43. S.root(function () {
  44. var { d, f } = S.subclock(() => {
  45. var d = S.subclock(() => {
  46. var d = S.data(5);
  47. S(() => d() >= 5 || d(d() + 1));
  48. return d;
  49. }),
  50. f = S.on(d, c => c + 1, 0);
  51. return { d, f };
  52. });
  53. expect(d()).toBe(5);
  54. expect(f()).toBe(1);
  55. d(1);
  56. expect(d()).toBe(5);
  57. expect(f()).toBe(2);
  58. });
  59. });
  60. it("sibling processes and nodes only see final state", function () {
  61. S.root(()=>{
  62. var d = S.data("abcdefgh"),
  63. c1 = S.subclock(()=>{
  64. var d1 = S.data(""),
  65. n1 = S(() => d1(d())),
  66. n2 = S(() => d1().length < 6 || d1(d1().substr(1)));
  67. return { d1, n1, n2 };
  68. }),
  69. c2 = S.subclock(()=>{
  70. var d1 = S.data(""),
  71. n1 = S(() => d1(c1.d1())),
  72. n2 = S(() => d1().length < 4 || d1(d1().substr(0, d1().length - 1)));
  73. return { d1, n1, n2 };
  74. }),
  75. n3 = S(() => c2.d1()),
  76. n3c = S.on(n3, c => c + 1, 0);
  77. expect(d()).toBe("abcdefgh");
  78. expect(c1.d1()).toBe("defgh");
  79. expect(c2.d1()).toBe("def");
  80. expect(n3()).toBe("def");
  81. expect(n3c()).toBe(1);
  82. d("1234567");
  83. expect(c1.d1()).toBe("34567");
  84. expect(c2.d1()).toBe("345");
  85. expect(n3()).toBe("345");
  86. expect(n3c()).toBe(2);
  87. });
  88. });
  89. it("can reference computions in a higher clock", function () {
  90. S.root(() => {
  91. var a = S.data(1),
  92. b = S(() => a()),
  93. c = S.subclock(() =>
  94. S(() => a() + b())
  95. );
  96. a(2);
  97. expect(c()).toBe(4);
  98. });
  99. });
  100. it("allows mutations from inside and outside a subclock", function () {
  101. S.root(() => {
  102. var sub = S.subclock(() => {
  103. var a = S.data(""),
  104. b = S(() => (a().length % 3) === 0 || a(a() + "i"));
  105. return { a };
  106. }),
  107. c = S(() => (sub.a().length % 5) === 0 || sub.a(sub.a() + "o"));
  108. expect(sub.a()).toBe("");
  109. sub.a("g");
  110. // doesn't stabilize until a().length is divisible by both 3 and 5, i.e. length of 15
  111. expect(sub.a()).toBe("giioiioiioiioii");
  112. });
  113. });
  114. it("throw an exception when there's a circular dependency between a computation and a clock", function () {
  115. S.root(() => {
  116. var a = S.data(true),
  117. b = S(() => a() || sub.c()), // b() depends on sub when a() is false
  118. sub = S.subclock(() => ({
  119. c: S(() => a()),
  120. d: S(() => b()) // sub depends on b()
  121. }));
  122. expect(() => a(false)).toThrowError(/circular/);
  123. });
  124. });
  125. it("throw an exception when there's a circular dependency between two clocks", function () {
  126. S.root(() => {
  127. var a = S.data(true),
  128. sub1 = S.subclock(() => ({
  129. b: S(() => a() || sub2.c()) // sub1 depends on sub2 when a() is false
  130. })),
  131. sub2 = S.subclock(() => ({
  132. c: S(() => sub1.b()) // sub2 depends on sub1
  133. }));
  134. expect(() => a(false)).toThrowError(/circular/);
  135. });
  136. });
  137. it("runs any changes in outer clocks when created at top level", function () {
  138. S.root(() => {
  139. var outer = S.data(1);
  140. S.subclock(() => {
  141. outer(2);
  142. });
  143. expect(outer()).toBe(2);
  144. outer(3);
  145. });
  146. });
  147. it("runs any changes in sibling clocks when created at top level", function () {
  148. S.root(() => {
  149. var outer = S.subclock(() => S.data(1));
  150. S.subclock(() => {
  151. outer(2);
  152. });
  153. expect(outer()).toBe(2);
  154. outer(3);
  155. expect(outer()).toBe(3);
  156. });
  157. });
  158. it("handles whiteboard case", function () {
  159. S.root(function () {
  160. var t = "",
  161. d = S.data(1),
  162. p1 = S.subclock()(function () {
  163. var c1 = S(() => {
  164. t += 'p1c1,';
  165. return d() + (d() > 1 ? c2() : 0);
  166. });
  167. return { c1 };
  168. }),
  169. p2 = S.subclock()(function () {
  170. var d1 = S.data(1),
  171. c1 = S(() => {
  172. t += 'p2c1,';
  173. return d1(d());
  174. });
  175. return { d1, c1 };
  176. }),
  177. p3 = S.subclock()(function () {
  178. var d1 = S.data(1),
  179. c1 = S(() => {
  180. t += 'p3c1,';
  181. d1(p2.d1());
  182. });
  183. return { d1, c1 };
  184. }),
  185. c1 = S(() => {
  186. t += 'c1,';
  187. return p3.d1();
  188. }),
  189. c2 = S(() => {
  190. t += 'c2,';
  191. return c1();
  192. });
  193. expect(p1.c1()).toBe(1);
  194. t = '';
  195. d(2);
  196. expect(p1.c1()).toBe(4);
  197. expect(t).toBe('p1c1,p2c1,p3c1,c2,c1,');
  198. t = '';
  199. d(1);
  200. expect(p1.c1()).toBe(1);
  201. expect(t).toBe('p2c1,p3c1,p1c1,c1,c2,');
  202. t = '';
  203. d(3);
  204. expect(p1.c1()).toBe(6);
  205. expect(t).toBe('p1c1,p2c1,p3c1,c2,c1,');
  206. t = '';
  207. d(2);
  208. expect(p1.c1()).toBe(4);
  209. expect(t).toBe('p2c1,p3c1,p1c1,c2,c1,');
  210. t = '';
  211. d(2);
  212. expect(p1.c1()).toBe(4);
  213. expect(t).toBe('p2c1,p3c1,p1c1,c2,c1,');
  214. t = '';
  215. d(5);
  216. expect(p1.c1()).toBe(10);
  217. expect(t).toBe('p2c1,p3c1,p1c1,c2,c1,');
  218. });
  219. });
  220. it("handles irisjay test case", function () {
  221. S.root(() => {
  222. var A = S.data(0),
  223. log = S.data();
  224. S.subclock(() => {
  225. S(() => log(A() + ' logged'));
  226. })
  227. var logs = [];
  228. S(() => logs.push(log()));
  229. expect(logs).toEqual(["0 logged"]);
  230. A(1);
  231. expect(logs).toEqual(["0 logged", "1 logged"]);
  232. A(8);
  233. expect(logs).toEqual(["0 logged", "1 logged", "8 logged"]);
  234. });
  235. });
  236. });
  237. }