Files: fe17252ec249e039c0e509ba0d745ca0da354881 / tests / index.js
31095 bytesRaw
1 | const tape = require('tape') |
2 | const AbstractContainer = require('primea-abstract-container') |
3 | const Message = require('primea-message') |
4 | const Hypervisor = require('../') |
5 | |
6 | const level = require('level') |
7 | const RadixTree = require('dfinity-radix-tree') |
8 | const db = level('./testdb') |
9 | |
10 | class CreationMessage extends Message { |
11 | constructor (params) { |
12 | const buf = [Buffer.from([0x0]), Buffer.from([params.data.type])] |
13 | if (params.data.code) { |
14 | buf.push(params.data.code) |
15 | } |
16 | params.data = Buffer.concat(buf) |
17 | super(params) |
18 | } |
19 | } |
20 | |
21 | class BaseContainer extends AbstractContainer { |
22 | onCreation (message) { |
23 | const port = message.ports[0] |
24 | if (port) { |
25 | return this.kernel.ports.bind('root', port) |
26 | } |
27 | } |
28 | static get typeId () { |
29 | return 9 |
30 | } |
31 | } |
32 | |
33 | tape('basic', async t => { |
34 | t.plan(3) |
35 | let message |
36 | const expectedState = { |
37 | '/': Buffer.from('646e7b89a3a4bb5dd6d43d1a7a29c69e72943bcd', 'hex') |
38 | } |
39 | |
40 | const tree = new RadixTree({ |
41 | db: db |
42 | }) |
43 | |
44 | class testVMContainer extends BaseContainer { |
45 | onMessage (m) { |
46 | t.true(m === message, 'should recive a message') |
47 | } |
48 | } |
49 | |
50 | const hypervisor = new Hypervisor(tree) |
51 | hypervisor.registerContainer(testVMContainer) |
52 | |
53 | const port = hypervisor.creationService.getPort() |
54 | |
55 | let rootContainer = await hypervisor.send(port, new CreationMessage({ |
56 | data: { |
57 | type: testVMContainer.typeId |
58 | } |
59 | })) |
60 | |
61 | rootContainer = await hypervisor.getInstance(rootContainer.id) |
62 | hypervisor.pin(rootContainer) |
63 | |
64 | const [portRef1, portRef2] = rootContainer.ports.createChannel() |
65 | const initMessage = new CreationMessage({ |
66 | data: { |
67 | code: Buffer.from('test code'), |
68 | type: testVMContainer.typeId |
69 | }, |
70 | ports: [portRef2] |
71 | }) |
72 | |
73 | message = rootContainer.createMessage() |
74 | await Promise.all([ |
75 | rootContainer.send(port, initMessage), |
76 | rootContainer.ports.bind('first', portRef1), |
77 | rootContainer.send(portRef1, message) |
78 | ]) |
79 | rootContainer.shutdown() |
80 | |
81 | const stateRoot = await hypervisor.createStateRoot(Infinity) |
82 | t.deepEquals(stateRoot, expectedState, 'expected root!') |
83 | |
84 | t.equals(hypervisor.scheduler.leastNumberOfTicks(), 0) |
85 | }) |
86 | |
87 | tape('basic - do not store containers with no ports bound', async t => { |
88 | t.plan(1) |
89 | |
90 | const tree = new RadixTree({ |
91 | db: db |
92 | }) |
93 | |
94 | const expectedState = { |
95 | '/': Buffer.from('5a316218edbc909f511b41d936517b27bb51cd6c', 'hex') |
96 | } |
97 | |
98 | class testVMContainer extends BaseContainer { |
99 | onCreation () {} |
100 | } |
101 | |
102 | const hypervisor = new Hypervisor(tree) |
103 | hypervisor.registerContainer(testVMContainer) |
104 | |
105 | const creationPort = hypervisor.creationService.getPort() |
106 | let root = await hypervisor.send(creationPort, new CreationMessage({ |
107 | data: { |
108 | type: testVMContainer.typeId |
109 | } |
110 | })) |
111 | |
112 | hypervisor.pin(root) |
113 | |
114 | root = await hypervisor.getInstance(root.id) |
115 | |
116 | const [portRef1, portRef2] = root.ports.createChannel() |
117 | |
118 | await Promise.all([ |
119 | root.ports.bind('one', portRef1), |
120 | root.send(creationPort, new CreationMessage({ |
121 | data: { |
122 | type: testVMContainer.typeId |
123 | }, |
124 | ports: [portRef2] |
125 | })) |
126 | ]) |
127 | |
128 | root.shutdown() |
129 | |
130 | const stateRoot = await hypervisor.createStateRoot(Infinity) |
131 | |
132 | // await hypervisor.graph.tree(stateRoot, Infinity, true) |
133 | // console.log(JSON.stringify(stateRoot, null, 2)) |
134 | t.deepEquals(stateRoot, expectedState, 'expected root!') |
135 | }) |
136 | |
137 | tape('one child contract', async t => { |
138 | t.plan(4) |
139 | |
140 | const tree = new RadixTree({ |
141 | db: db |
142 | }) |
143 | |
144 | let message |
145 | const expectedState = { |
146 | '/': Buffer.from('c91821c303cd07adde06c0d46c40aafe4542dea1', 'hex') |
147 | } |
148 | |
149 | let hasResolved = false |
150 | |
151 | class testVMContainer2 extends BaseContainer { |
152 | onMessage (m) { |
153 | t.true(m === message, 'should recive a message') |
154 | return new Promise((resolve, reject) => { |
155 | setTimeout(() => { |
156 | this.kernel.incrementTicks(1) |
157 | hasResolved = true |
158 | resolve() |
159 | }, 200) |
160 | }) |
161 | } |
162 | |
163 | static get typeId () { |
164 | return 99 |
165 | } |
166 | } |
167 | |
168 | class testVMContainer extends BaseContainer { |
169 | async onMessage (m) { |
170 | const [portRef1, portRef2] = this.kernel.ports.createChannel() |
171 | const port = this.kernel.hypervisor.creationService.getPort() |
172 | |
173 | await Promise.all([ |
174 | this.kernel.send(port, new CreationMessage({ |
175 | data: { |
176 | type: testVMContainer2.typeId |
177 | }, |
178 | ports: [portRef2] |
179 | })), |
180 | this.kernel.send(portRef1, m) |
181 | ]) |
182 | |
183 | this.kernel.incrementTicks(1) |
184 | return this.kernel.ports.bind('child', portRef1) |
185 | } |
186 | } |
187 | |
188 | const hypervisor = new Hypervisor(tree) |
189 | hypervisor.registerContainer(testVMContainer) |
190 | hypervisor.registerContainer(testVMContainer2) |
191 | |
192 | let creationPort = hypervisor.creationService.getPort() |
193 | let root = await hypervisor.send(creationPort, new CreationMessage({ |
194 | data: { |
195 | type: testVMContainer.typeId |
196 | } |
197 | })) |
198 | |
199 | hypervisor.pin(root) |
200 | |
201 | const rootId = root.id |
202 | root = await hypervisor.getInstance(rootId) |
203 | const [portRef1, portRef2] = root.ports.createChannel() |
204 | |
205 | message = root.createMessage() |
206 | await Promise.all([ |
207 | root.send(creationPort, new CreationMessage({ |
208 | data: { |
209 | type: testVMContainer.typeId |
210 | }, |
211 | ports: [portRef2] |
212 | })), |
213 | root.ports.bind('first', portRef1), |
214 | root.send(portRef1, message) |
215 | ]) |
216 | |
217 | root.shutdown() |
218 | |
219 | const stateRoot = await hypervisor.createStateRoot(Infinity) |
220 | t.true(hasResolved, 'should resolve before generating the state root') |
221 | t.deepEquals(stateRoot, expectedState, 'expected state') |
222 | |
223 | // test reviving the state |
224 | class testVMContainer3 extends BaseContainer { |
225 | onMessage (m) { |
226 | const port = this.kernel.ports.get('child') |
227 | this.kernel.send(port, m) |
228 | this.kernel.incrementTicks(1) |
229 | } |
230 | } |
231 | |
232 | hypervisor.registerContainer(testVMContainer3) |
233 | root = await hypervisor.getInstance(rootId) |
234 | const port = root.ports.get('first') |
235 | root.send(port, message) |
236 | }) |
237 | |
238 | tape('traps', async t => { |
239 | t.plan(1) |
240 | |
241 | const tree = new RadixTree({ |
242 | db: db |
243 | }) |
244 | class Root extends BaseContainer { |
245 | async onMessage (m) { |
246 | const [portRef1] = this.kernel.ports.createChannel() |
247 | const [portRef3] = this.kernel.ports.createChannel() |
248 | const [portRef5] = this.kernel.ports.createChannel() |
249 | |
250 | await Promise.all([ |
251 | this.kernel.ports.bind('one', portRef1), |
252 | this.kernel.ports.bind('two', portRef3), |
253 | this.kernel.ports.bind('three', portRef5) |
254 | ]) |
255 | |
256 | throw new Error('it is a trap!!!') |
257 | } |
258 | } |
259 | |
260 | const hypervisor = new Hypervisor(tree) |
261 | |
262 | hypervisor.registerContainer(Root) |
263 | const creationPort = hypervisor.creationService.getPort() |
264 | const root = await hypervisor.send(creationPort, new CreationMessage({ |
265 | data: { |
266 | type: Root.typeId |
267 | } |
268 | })) |
269 | |
270 | hypervisor.pin(root) |
271 | |
272 | await root.message(root.createMessage()) |
273 | const stateRoot = await hypervisor.createStateRoot() |
274 | |
275 | t.deepEquals(stateRoot, { |
276 | '/': Buffer.from('308b10121e2c46102e2d9701cfe11032786ef955', 'hex') |
277 | }, 'should revert the state') |
278 | }) |
279 | |
280 | tape('recieving older messages', async t => { |
281 | t.plan(2) |
282 | |
283 | const tree = new RadixTree({ |
284 | db: db |
285 | }) |
286 | let runs = 0 |
287 | const hypervisor = new Hypervisor(tree) |
288 | const creationPort = hypervisor.creationService.getPort() |
289 | |
290 | class Root extends BaseContainer { |
291 | async onMessage (m) { |
292 | if (!runs) { |
293 | runs++ |
294 | const [portRef1, portRef2] = this.kernel.ports.createChannel() |
295 | const [portRef3, portRef4] = this.kernel.ports.createChannel() |
296 | |
297 | const message1 = new CreationMessage({ |
298 | data: { |
299 | type: First.typeId |
300 | }, |
301 | ports: [portRef2] |
302 | }) |
303 | const message2 = new CreationMessage({ |
304 | data: { |
305 | type: Waiter.typeId |
306 | }, |
307 | ports: [portRef4] |
308 | }) |
309 | |
310 | return Promise.all([ |
311 | this.kernel.send(creationPort, message1), |
312 | this.kernel.send(portRef1, this.kernel.createMessage()), |
313 | this.kernel.send(portRef3, this.kernel.createMessage()), |
314 | this.kernel.ports.bind('one', portRef1), |
315 | this.kernel.ports.bind('two', portRef3), |
316 | this.kernel.send(creationPort, message2) |
317 | ]) |
318 | } else if (runs === 1) { |
319 | runs++ |
320 | t.equals(m.data, 'first', 'should recive the first message') |
321 | } else { |
322 | runs++ |
323 | t.equals(m.data, 'second', 'should recive the second message') |
324 | } |
325 | } |
326 | static get typeId () { |
327 | return 211 |
328 | } |
329 | } |
330 | |
331 | class First extends BaseContainer { |
332 | onMessage (m) { |
333 | this.kernel.incrementTicks(2) |
334 | return this.kernel.send(m.fromPort, this.kernel.createMessage({ |
335 | data: 'second' |
336 | })) |
337 | } |
338 | static get typeId () { |
339 | return 29 |
340 | } |
341 | } |
342 | |
343 | class Waiter extends BaseContainer { |
344 | onMessage (m) { |
345 | return new Promise((resolve, reject) => { |
346 | setTimeout(() => { |
347 | this.kernel.send(m.fromPort, this.kernel.createMessage({ |
348 | data: 'first' |
349 | })).then(resolve) |
350 | }, 200) |
351 | }) |
352 | } |
353 | } |
354 | |
355 | hypervisor.registerContainer(Root) |
356 | hypervisor.registerContainer(First) |
357 | hypervisor.registerContainer(Waiter) |
358 | |
359 | let root = await hypervisor.send(creationPort, new CreationMessage({ |
360 | data: { |
361 | type: Root.typeId |
362 | } |
363 | })) |
364 | |
365 | hypervisor.pin(root) |
366 | |
367 | root = await hypervisor.getInstance(root.id) |
368 | const [portRef1, portRef2] = root.ports.createChannel() |
369 | |
370 | const message = root.createMessage() |
371 | await Promise.all([ |
372 | root.send(portRef1, message), |
373 | root.ports.bind('first', portRef1), |
374 | root.send(creationPort, new CreationMessage({ |
375 | data: { |
376 | type: Root.typeId |
377 | }, |
378 | ports: [portRef2] |
379 | })) |
380 | ]) |
381 | root.shutdown() |
382 | }) |
383 | |
384 | tape('saturation', async t => { |
385 | t.plan(3) |
386 | |
387 | const tree = new RadixTree({ |
388 | db: db |
389 | }) |
390 | let runs = 0 |
391 | |
392 | const hypervisor = new Hypervisor(tree) |
393 | const creationPort = hypervisor.creationService.getPort() |
394 | |
395 | class Root extends BaseContainer { |
396 | onIdle () {} |
397 | async onMessage (m) { |
398 | if (!runs) { |
399 | runs++ |
400 | const [portRef1, portRef2] = this.kernel.ports.createChannel() |
401 | const [portRef3, portRef4] = this.kernel.ports.createChannel() |
402 | |
403 | const message1 = new CreationMessage({ |
404 | data: { |
405 | type: First.typeId |
406 | }, |
407 | ports: [portRef2] |
408 | }) |
409 | |
410 | const message2 = new CreationMessage({ |
411 | data: { |
412 | type: Second.typeId |
413 | }, |
414 | ports: [portRef4] |
415 | }) |
416 | |
417 | this.kernel.incrementTicks(6) |
418 | await Promise.all([ |
419 | this.kernel.send(creationPort, message1), |
420 | this.kernel.send(creationPort, message2), |
421 | this.kernel.send(portRef1, this.kernel.createMessage()), |
422 | this.kernel.send(portRef3, this.kernel.createMessage()), |
423 | this.kernel.ports.bind('one', portRef1), |
424 | this.kernel.ports.bind('two', portRef3) |
425 | ]) |
426 | } else if (runs === 1) { |
427 | runs++ |
428 | t.equals(m.data, 'first', 'should recive the first message') |
429 | } else if (runs === 2) { |
430 | runs++ |
431 | t.equals(m.data, 'second', 'should recive the second message') |
432 | } else { |
433 | runs++ |
434 | t.equals(m.data, 'third', 'should recived the third message') |
435 | } |
436 | } |
437 | static get typeId () { |
438 | return 112 |
439 | } |
440 | } |
441 | |
442 | class First extends BaseContainer { |
443 | onMessage (m) { |
444 | this.kernel.incrementTicks(2) |
445 | return this.kernel.send(m.fromPort, this.kernel.createMessage({ |
446 | data: 'second' |
447 | })) |
448 | } |
449 | static get typeId () { |
450 | return 29 |
451 | } |
452 | } |
453 | |
454 | class Second extends BaseContainer { |
455 | onMessage (m) { |
456 | this.kernel.incrementTicks(3) |
457 | return this.kernel.send(m.fromPort, this.kernel.createMessage({ |
458 | data: 'third' |
459 | })) |
460 | } |
461 | static get typeId () { |
462 | return 2 |
463 | } |
464 | } |
465 | |
466 | class Waiter extends BaseContainer { |
467 | onCreation (m) { |
468 | return new Promise((resolve, reject) => { |
469 | setTimeout(() => { |
470 | this.kernel.send(m.ports[0], this.kernel.createMessage({ |
471 | data: 'first' |
472 | })).then(resolve) |
473 | }, 200) |
474 | }) |
475 | } |
476 | } |
477 | |
478 | hypervisor.registerContainer(Root) |
479 | hypervisor.registerContainer(First) |
480 | hypervisor.registerContainer(Second) |
481 | hypervisor.registerContainer(Waiter) |
482 | |
483 | let root = await hypervisor.send(creationPort, new CreationMessage({ |
484 | data: { |
485 | type: Root.typeId |
486 | } |
487 | })) |
488 | |
489 | hypervisor.pin(root) |
490 | root = await hypervisor.getInstance(root.id) |
491 | |
492 | const [portRef1, portRef2] = root.ports.createChannel() |
493 | const [portRef3, portRef4] = root.ports.createChannel() |
494 | |
495 | const message = root.createMessage() |
496 | await Promise.all([ |
497 | root.send(portRef1, message), |
498 | root.ports.bind('first', portRef1), |
499 | root.send(creationPort, new CreationMessage({ |
500 | data: { |
501 | type: Root.typeId |
502 | }, |
503 | ports: [portRef2] |
504 | })), |
505 | root.ports.bind('sencond', portRef3), |
506 | root.send(creationPort, new CreationMessage({ |
507 | data: { |
508 | type: Waiter.typeId |
509 | }, |
510 | ports: [portRef4] |
511 | })) |
512 | ]) |
513 | |
514 | root.incrementTicks(100) |
515 | await root.send(portRef1, root.createMessage({ |
516 | data: 'testss' |
517 | })) |
518 | root.shutdown() |
519 | }) |
520 | |
521 | tape('send to the same container at the same time', async t => { |
522 | t.plan(2) |
523 | |
524 | const tree = new RadixTree({ |
525 | db: db |
526 | }) |
527 | let runs = 0 |
528 | let instance |
529 | |
530 | const hypervisor = new Hypervisor(tree) |
531 | const creationPort = hypervisor.creationService.getPort() |
532 | |
533 | class Root extends BaseContainer { |
534 | async onMessage (m) { |
535 | let one = this.kernel.ports.get('one') |
536 | if (!one) { |
537 | const [portRef1, portRef2] = this.kernel.ports.createChannel() |
538 | const message1 = new CreationMessage({ |
539 | data: { |
540 | type: First.typeId |
541 | }, |
542 | ports: [portRef2] |
543 | }) |
544 | await this.kernel.send(creationPort, message1) |
545 | return this.kernel.ports.bind('one', portRef1) |
546 | } else { |
547 | return Promise.all([ |
548 | this.kernel.send(one, this.kernel.createMessage()), |
549 | this.kernel.send(one, this.kernel.createMessage()) |
550 | ]) |
551 | } |
552 | } |
553 | static get typeId () { |
554 | return 111 |
555 | } |
556 | } |
557 | |
558 | class First extends BaseContainer { |
559 | onMessage (m) { |
560 | ++runs |
561 | if (runs === 2) { |
562 | t.equals(instance, this, 'should have same instances') |
563 | } else { |
564 | instance = this |
565 | } |
566 | } |
567 | } |
568 | |
569 | hypervisor.registerContainer(Root) |
570 | hypervisor.registerContainer(First) |
571 | |
572 | let root = await hypervisor.send(creationPort, new CreationMessage({ |
573 | data: { |
574 | type: Root.typeId |
575 | } |
576 | })) |
577 | |
578 | hypervisor.pin(root) |
579 | root = await hypervisor.getInstance(root.id) |
580 | |
581 | const [portRef1, portRef2] = root.ports.createChannel() |
582 | await Promise.all([ |
583 | root.ports.bind('first', portRef1), |
584 | root.send(creationPort, new CreationMessage({ |
585 | data: { |
586 | type: Root.typeId |
587 | }, |
588 | ports: [portRef2] |
589 | })) |
590 | ]) |
591 | |
592 | const message = root.createMessage() |
593 | |
594 | await root.send(portRef1, message) |
595 | root.shutdown() |
596 | await hypervisor.createStateRoot() |
597 | await root.send(portRef1, root.createMessage()) |
598 | await hypervisor.createStateRoot() |
599 | t.equals(runs, 2) |
600 | }) |
601 | |
602 | tape('checking ports', async t => { |
603 | t.plan(4) |
604 | |
605 | const tree = new RadixTree({ |
606 | db: db |
607 | }) |
608 | const hypervisor = new Hypervisor(tree) |
609 | const creationPort = hypervisor.creationService.getPort() |
610 | hypervisor.registerContainer(BaseContainer) |
611 | |
612 | let root = await hypervisor.send(creationPort, new CreationMessage({ |
613 | data: { |
614 | type: BaseContainer.typeId |
615 | } |
616 | })) |
617 | |
618 | hypervisor.pin(root) |
619 | root = await hypervisor.getInstance(root.id) |
620 | |
621 | const [portRef1, portRef2] = root.ports.createChannel() |
622 | root.send(creationPort, new CreationMessage({ |
623 | data: { |
624 | type: BaseContainer.typeId |
625 | }, |
626 | ports: [portRef2] |
627 | })) |
628 | await root.ports.bind('test', portRef1) |
629 | |
630 | try { |
631 | root.createMessage({ |
632 | ports: [portRef1] |
633 | }) |
634 | } catch (e) { |
635 | t.pass('should thow if sending a port that is bound') |
636 | } |
637 | |
638 | try { |
639 | await root.ports.bind('test', portRef1) |
640 | } catch (e) { |
641 | t.pass('should thow if binding an already bound port') |
642 | } |
643 | |
644 | try { |
645 | const [portRef3] = root.ports.createChannel() |
646 | await root.ports.bind('test', portRef3) |
647 | } catch (e) { |
648 | t.pass('should thow if binding an already bound name') |
649 | } |
650 | |
651 | await root.ports.unbind('test') |
652 | const message = root.createMessage({ |
653 | ports: [portRef1] |
654 | }) |
655 | t.equals(message.ports[0], portRef1, 'should create a message if the port is unbound') |
656 | }) |
657 | |
658 | tape('port deletion', async t => { |
659 | const expectedSr = { |
660 | '/': Buffer.from('1f0673f23b4eeb86115992621d7edc981a6afade', 'hex') |
661 | } |
662 | |
663 | const tree = new RadixTree({ |
664 | db: db |
665 | }) |
666 | const hypervisor = new Hypervisor(tree) |
667 | const creationPort = hypervisor.creationService.getPort() |
668 | |
669 | class Root extends BaseContainer { |
670 | async onMessage (m) { |
671 | const [portRef1, portRef2] = this.kernel.ports.createChannel() |
672 | const message1 = new CreationMessage({ |
673 | data: { |
674 | type: First.typeId |
675 | }, |
676 | ports: [portRef2] |
677 | }) |
678 | |
679 | await Promise.all([ |
680 | this.kernel.send(creationPort, message1), |
681 | this.kernel.send(portRef1, this.kernel.createMessage()) |
682 | ]) |
683 | this.kernel.incrementTicks(6) |
684 | return this.kernel.ports.bind('one', portRef1) |
685 | } |
686 | } |
687 | |
688 | class First extends BaseContainer { |
689 | onMessage (m) { |
690 | this.kernel.incrementTicks(2) |
691 | return this.kernel.ports.delete('root') |
692 | } |
693 | static get typeId () { |
694 | return 111 |
695 | } |
696 | } |
697 | |
698 | hypervisor.registerContainer(Root) |
699 | hypervisor.registerContainer(First) |
700 | |
701 | let root = await hypervisor.send(creationPort, new CreationMessage({ |
702 | data: { |
703 | type: Root.typeId |
704 | } |
705 | })) |
706 | |
707 | hypervisor.pin(root) |
708 | root = await hypervisor.getInstance(root.id) |
709 | |
710 | const [portRef1, portRef2] = root.ports.createChannel() |
711 | await root.ports.bind('first', portRef1) |
712 | await root.send(creationPort, new CreationMessage({ |
713 | data: { |
714 | type: Root.typeId |
715 | }, |
716 | ports: [portRef2] |
717 | })) |
718 | |
719 | const message = root.createMessage() |
720 | await root.send(portRef1, message) |
721 | |
722 | root.shutdown() |
723 | |
724 | const sr = await hypervisor.createStateRoot() |
725 | t.deepEquals(sr, expectedSr, 'should produce the corret state root') |
726 | |
727 | t.end() |
728 | }) |
729 | |
730 | tape('clear unbounded ports', async t => { |
731 | const expectedSr = { |
732 | '/': Buffer.from('1f0673f23b4eeb86115992621d7edc981a6afade', 'hex') |
733 | } |
734 | |
735 | const tree = new RadixTree({ |
736 | db: db |
737 | }) |
738 | const hypervisor = new Hypervisor(tree) |
739 | const creationPort = hypervisor.creationService.getPort() |
740 | |
741 | class Root extends BaseContainer { |
742 | onMessage (m) { |
743 | return this.kernel.send(creationPort, new CreationMessage({ |
744 | data: { |
745 | type: Root.typeId |
746 | } |
747 | })) |
748 | } |
749 | } |
750 | |
751 | hypervisor.registerContainer(Root) |
752 | |
753 | let root = await hypervisor.send(creationPort, new CreationMessage({ |
754 | data: { |
755 | type: Root.typeId |
756 | } |
757 | })) |
758 | |
759 | root = await hypervisor.getInstance(root.id) |
760 | hypervisor.pin(root) |
761 | |
762 | const [portRef1, portRef2] = root.ports.createChannel() |
763 | await root.ports.bind('first', portRef1) |
764 | await root.send(creationPort, new CreationMessage({ |
765 | data: { |
766 | type: Root.typeId |
767 | }, |
768 | ports: [portRef2] |
769 | })) |
770 | |
771 | const message = root.createMessage() |
772 | await root.send(portRef1, message) |
773 | root.shutdown() |
774 | const sr = await hypervisor.createStateRoot() |
775 | t.deepEquals(sr, expectedSr, 'should produce the corret state root') |
776 | |
777 | t.end() |
778 | }) |
779 | |
780 | tape('should remove subgraphs', async t => { |
781 | const expectedSr = { |
782 | '/': Buffer.from('1f0673f23b4eeb86115992621d7edc981a6afade', 'hex') |
783 | } |
784 | |
785 | const tree = new RadixTree({ |
786 | db: db |
787 | }) |
788 | const hypervisor = new Hypervisor(tree) |
789 | const creationPort = hypervisor.creationService.getPort() |
790 | |
791 | class Root extends BaseContainer { |
792 | onMessage (m) { |
793 | const [, portRef2] = this.kernel.ports.createChannel() |
794 | return this.kernel.send(creationPort, new CreationMessage({ |
795 | data: { |
796 | type: Sub.typeId |
797 | }, |
798 | ports: [portRef2] |
799 | })) |
800 | } |
801 | } |
802 | |
803 | class Sub extends BaseContainer { |
804 | static get typeId () { |
805 | return 199 |
806 | } |
807 | } |
808 | |
809 | hypervisor.registerContainer(Root) |
810 | hypervisor.registerContainer(Sub) |
811 | |
812 | let root = await hypervisor.send(creationPort, new CreationMessage({ |
813 | data: { |
814 | type: Root.typeId |
815 | } |
816 | })) |
817 | |
818 | root = await hypervisor.getInstance(root.id) |
819 | |
820 | hypervisor.pin(root) |
821 | |
822 | const [portRef1, portRef2] = root.ports.createChannel() |
823 | await root.ports.bind('first', portRef1) |
824 | await root.send(creationPort, new CreationMessage({ |
825 | data: { |
826 | type: Root.typeId |
827 | }, |
828 | ports: [portRef2] |
829 | })) |
830 | |
831 | await root.send(portRef1, root.createMessage()) |
832 | root.shutdown() |
833 | const sr = await hypervisor.createStateRoot() |
834 | |
835 | t.deepEquals(sr, expectedSr, 'should produce the corret state root') |
836 | t.end() |
837 | }) |
838 | |
839 | tape('should not remove connected nodes', async t => { |
840 | const tree = new RadixTree({ |
841 | db: db |
842 | }) |
843 | |
844 | const expectedSr = { |
845 | '/': Buffer.from('9aeb0ce35c0ab845e7b273afe0329f826297124e', 'hex') |
846 | } |
847 | |
848 | const hypervisor = new Hypervisor(tree) |
849 | const creationPort = hypervisor.creationService.getPort() |
850 | |
851 | class Root extends BaseContainer { |
852 | async onMessage (m) { |
853 | if (m.ports.length) { |
854 | const port = this.kernel.ports.get('test1') |
855 | await this.kernel.send(port, m) |
856 | return this.kernel.ports.unbind('test1') |
857 | } else { |
858 | const [portRef1, portRef2] = this.kernel.ports.createChannel() |
859 | await this.kernel.send(creationPort, new CreationMessage({ |
860 | data: { |
861 | type: Sub.typeId |
862 | }, |
863 | ports: [portRef2] |
864 | })) |
865 | await this.kernel.ports.bind('test1', portRef1) |
866 | |
867 | const [portRef3, portRef4] = this.kernel.ports.createChannel() |
868 | await this.kernel.send(creationPort, new CreationMessage({ |
869 | data: { |
870 | type: Sub.typeId |
871 | }, |
872 | ports: [portRef4] |
873 | })) |
874 | await this.kernel.ports.bind('test2', portRef3) |
875 | await this.kernel.send(portRef3, this.kernel.createMessage({ |
876 | data: 'getChannel' |
877 | })) |
878 | } |
879 | } |
880 | } |
881 | |
882 | class Sub extends BaseContainer { |
883 | async onMessage (message) { |
884 | if (message.data === 'getChannel') { |
885 | const ports = this.kernel.ports.createChannel() |
886 | await this.kernel.send(message.fromPort, this.kernel.createMessage({ |
887 | data: 'bindPort', |
888 | ports: [ports[1]] |
889 | })) |
890 | return this.kernel.ports.bind('channel', ports[0]) |
891 | } else { |
892 | return this.kernel.ports.bind('channel', message.ports[0]) |
893 | } |
894 | } |
895 | static get typeId () { |
896 | return 111 |
897 | } |
898 | } |
899 | |
900 | hypervisor.registerContainer(Root) |
901 | hypervisor.registerContainer(Sub) |
902 | |
903 | let root = await hypervisor.send(creationPort, new CreationMessage({ |
904 | data: { |
905 | type: Root.typeId |
906 | } |
907 | })) |
908 | |
909 | root = await hypervisor.getInstance(root.id) |
910 | hypervisor.pin(root) |
911 | |
912 | const [portRef1, portRef2] = root.ports.createChannel() |
913 | await root.ports.bind('first', portRef1) |
914 | await root.send(creationPort, new CreationMessage({ |
915 | data: { |
916 | type: Root.typeId |
917 | }, |
918 | ports: [portRef2] |
919 | })) |
920 | |
921 | await root.send(portRef1, root.createMessage()) |
922 | root.shutdown() |
923 | const sr = await hypervisor.createStateRoot() |
924 | |
925 | t.deepEquals(sr, expectedSr, 'should produce the corret state root') |
926 | t.end() |
927 | }) |
928 | |
929 | tape('should remove multiple subgraphs', async t => { |
930 | const tree = new RadixTree({ |
931 | db: db |
932 | }) |
933 | const expectedSr = { |
934 | '/': Buffer.from('196a7b55f26afb41f065923332e14b40cd0edf2e', 'hex') |
935 | } |
936 | |
937 | const hypervisor = new Hypervisor(tree) |
938 | const creationPort = hypervisor.creationService.getPort() |
939 | |
940 | class Root extends BaseContainer { |
941 | onMessage (m) { |
942 | if (m.ports.length) { |
943 | const port = this.kernel.ports.get('test1') |
944 | return Promise.all([ |
945 | this.kernel.send(port, m), |
946 | this.kernel.ports.unbind('test1'), |
947 | this.kernel.ports.unbind('test2') |
948 | ]) |
949 | } else { |
950 | const [portRef1, portRef2] = this.kernel.ports.createChannel() |
951 | const [portRef3, portRef4] = this.kernel.ports.createChannel() |
952 | return Promise.all([ |
953 | this.kernel.send(creationPort, new CreationMessage({ |
954 | data: { |
955 | type: Sub.typeId |
956 | }, |
957 | ports: [portRef2] |
958 | })), |
959 | this.kernel.ports.bind('test1', portRef1), |
960 | this.kernel.send(creationPort, new CreationMessage({ |
961 | data: { |
962 | type: Sub.typeId |
963 | }, |
964 | ports: [portRef4] |
965 | })), |
966 | this.kernel.ports.bind('test2', portRef3), |
967 | this.kernel.send(portRef3, this.kernel.createMessage({ |
968 | data: 'getChannel' |
969 | })) |
970 | ]) |
971 | } |
972 | } |
973 | } |
974 | |
975 | class Sub extends BaseContainer { |
976 | async onMessage (message) { |
977 | if (message.data === 'getChannel') { |
978 | const ports = this.kernel.ports.createChannel() |
979 | await this.kernel.send(message.fromPort, this.kernel.createMessage({ |
980 | data: 'bindPort', |
981 | ports: [ports[1]] |
982 | })) |
983 | return this.kernel.ports.bind('channel', ports[0]) |
984 | } else { |
985 | return this.kernel.ports.bind('channel', message.ports[0]) |
986 | } |
987 | } |
988 | static get typeId () { |
989 | return 111 |
990 | } |
991 | } |
992 | |
993 | hypervisor.registerContainer(Root) |
994 | hypervisor.registerContainer(Sub) |
995 | |
996 | let root = await hypervisor.send(creationPort, new CreationMessage({ |
997 | data: { |
998 | type: Root.typeId |
999 | } |
1000 | })) |
1001 | |
1002 | root = await hypervisor.getInstance(root.id) |
1003 | hypervisor.pin(root) |
1004 | |
1005 | const [portRef1, portRef2] = root.ports.createChannel() |
1006 | await Promise.all([ |
1007 | root.ports.bind('first', portRef1), |
1008 | root.send(creationPort, new CreationMessage({ |
1009 | data: { |
1010 | type: Root.typeId |
1011 | }, |
1012 | ports: [portRef2] |
1013 | })), |
1014 | root.send(portRef1, root.createMessage()) |
1015 | ]) |
1016 | |
1017 | root.shutdown() |
1018 | |
1019 | const sr = await hypervisor.createStateRoot() |
1020 | |
1021 | t.deepEquals(sr, expectedSr, 'should produce the corret state root') |
1022 | |
1023 | t.end() |
1024 | }) |
1025 | |
1026 | tape('response ports', async t => { |
1027 | t.plan(2) |
1028 | const tree = new RadixTree({ |
1029 | db: db |
1030 | }) |
1031 | let runs = 0 |
1032 | const returnValue = 'this is a test' |
1033 | const hypervisor = new Hypervisor(tree) |
1034 | const creationPort = hypervisor.creationService.getPort() |
1035 | |
1036 | class testVMContainer extends BaseContainer { |
1037 | onMessage (m) { |
1038 | runs++ |
1039 | if (runs === 1) { |
1040 | return returnValue |
1041 | } else { |
1042 | t.equals(m.data, returnValue, 'should have correct return value') |
1043 | } |
1044 | } |
1045 | } |
1046 | |
1047 | hypervisor.registerContainer(testVMContainer) |
1048 | |
1049 | let rootContainer = await hypervisor.send(creationPort, new CreationMessage({ |
1050 | data: { |
1051 | type: testVMContainer.typeId |
1052 | } |
1053 | })) |
1054 | |
1055 | rootContainer = await hypervisor.getInstance(rootContainer.id) |
1056 | |
1057 | hypervisor.pin(rootContainer) |
1058 | |
1059 | const [portRef1, portRef2] = rootContainer.ports.createChannel() |
1060 | const initMessage = new CreationMessage({ |
1061 | data: { |
1062 | type: testVMContainer.typeId |
1063 | }, |
1064 | ports: [portRef2] |
1065 | }) |
1066 | |
1067 | rootContainer.send(creationPort, initMessage) |
1068 | |
1069 | await rootContainer.ports.bind('first', portRef1) |
1070 | const message = rootContainer.createMessage() |
1071 | const rPort = rootContainer.getResponsePort(message) |
1072 | const rPort2 = rootContainer.getResponsePort(message) |
1073 | |
1074 | t.equals(rPort2, rPort) |
1075 | |
1076 | rootContainer.send(portRef1, message) |
1077 | await rootContainer.ports.bind('response', rPort) |
1078 | }) |
1079 | |
1080 | tape('start up', async t => { |
1081 | t.plan(1) |
1082 | |
1083 | const tree = new RadixTree({ |
1084 | db: db |
1085 | }) |
1086 | |
1087 | class testVMContainer extends BaseContainer { |
1088 | onStartup () { |
1089 | t.pass('should start up') |
1090 | } |
1091 | } |
1092 | |
1093 | const hypervisor = new Hypervisor(tree) |
1094 | const creationPort = hypervisor.creationService.getPort() |
1095 | hypervisor.registerContainer(testVMContainer) |
1096 | const instance = await hypervisor.send(creationPort, new CreationMessage({ |
1097 | data: { |
1098 | type: testVMContainer.typeId |
1099 | } |
1100 | })) |
1101 | hypervisor.getInstance(instance.id) |
1102 | }) |
1103 | |
1104 | tape('large code size', async t => { |
1105 | t.plan(1) |
1106 | const tree = new RadixTree({ |
1107 | db: db |
1108 | }) |
1109 | const content = Buffer.from(new ArrayBuffer(1e6)) |
1110 | class testVMContainer extends BaseContainer {} |
1111 | |
1112 | const hypervisor = new Hypervisor(tree) |
1113 | const creationPort = hypervisor.creationService.getPort() |
1114 | hypervisor.registerContainer(testVMContainer) |
1115 | const oldInst = await hypervisor.send(creationPort, new CreationMessage({ |
1116 | data: { |
1117 | type: testVMContainer.typeId, |
1118 | code: content |
1119 | } |
1120 | })) |
1121 | const instance = await hypervisor.getInstance(oldInst.id) |
1122 | t.equals(content.length, instance.code.length) |
1123 | }) |
1124 | |
1125 | tape('creation service messaging', async t => { |
1126 | t.plan(1) |
1127 | |
1128 | const tree = new RadixTree({ |
1129 | db: db |
1130 | }) |
1131 | |
1132 | class TestVMContainer extends BaseContainer { |
1133 | async onCreation (m) { |
1134 | const creationPort = m.ports[0] |
1135 | const [port1, port2] = this.kernel.ports.createChannel() |
1136 | await this.kernel.ports.bind('child', port1) |
1137 | |
1138 | const message = new CreationMessage({ |
1139 | data: { |
1140 | type: TestVMContainer2.typeId |
1141 | }, |
1142 | ports: [port2] |
1143 | }) |
1144 | return this.kernel.send(creationPort, message) |
1145 | } |
1146 | } |
1147 | |
1148 | class TestVMContainer2 extends BaseContainer { |
1149 | static get typeId () { |
1150 | return 66 |
1151 | } |
1152 | } |
1153 | |
1154 | const hypervisor = new Hypervisor(tree) |
1155 | hypervisor.registerContainer(TestVMContainer) |
1156 | hypervisor.registerContainer(TestVMContainer2) |
1157 | |
1158 | const port = hypervisor.creationService.getPort() |
1159 | const port2 = hypervisor.creationService.getPort() |
1160 | |
1161 | const root = await hypervisor.send(port2, new CreationMessage({ |
1162 | data: { |
1163 | type: TestVMContainer.typeId |
1164 | }, |
1165 | ports: [port] |
1166 | })) |
1167 | |
1168 | hypervisor.pin(root) |
1169 | |
1170 | const stateRoot = await hypervisor.createStateRoot() |
1171 | // await hypervisor.graph.tree(hypervisor.state, Infinity, true) |
1172 | const expectedSR = { |
1173 | '/': Buffer.from('c86f6a4519b4a18e1f31abe357a84712aabce8d2', 'hex') |
1174 | } |
1175 | t.deepEquals(stateRoot, expectedSR) |
1176 | }) |
1177 | |
1178 | tape('creation service - port copy', async t => { |
1179 | t.plan(2) |
1180 | |
1181 | const tree = new RadixTree({ |
1182 | db: db |
1183 | }) |
1184 | |
1185 | class TestVMContainer extends BaseContainer { |
1186 | onCreation (m) { |
1187 | const creationPort = m.ports[0] |
1188 | |
1189 | const message = this.kernel.createMessage() |
1190 | const responePort = this.kernel.getResponsePort(message) |
1191 | |
1192 | return Promise.all([ |
1193 | this.kernel.ports.bind('response', responePort), |
1194 | this.kernel.send(creationPort, message) |
1195 | ]) |
1196 | } |
1197 | onMessage (m) { |
1198 | t.equal(m.fromName, 'response') |
1199 | t.equal(m.ports.length, 1) |
1200 | } |
1201 | } |
1202 | |
1203 | const hypervisor = new Hypervisor(tree) |
1204 | hypervisor.registerContainer(TestVMContainer) |
1205 | |
1206 | const port = hypervisor.creationService.getPort() |
1207 | |
1208 | const root = await hypervisor.send(port, new CreationMessage({ |
1209 | data: { |
1210 | type: TestVMContainer.typeId |
1211 | }, |
1212 | ports: [port] |
1213 | })) |
1214 | |
1215 | hypervisor.pin(root) |
1216 | }) |
1217 | |
1218 | tape('waiting on ports', async t => { |
1219 | t.plan(1) |
1220 | |
1221 | const tree = new RadixTree({ |
1222 | db: db |
1223 | }) |
1224 | |
1225 | class TestVMContainer extends BaseContainer { |
1226 | async onCreation (m) { |
1227 | await this.kernel.ports.bind('test', m.ports[0]) |
1228 | this.kernel.ports.getNextMessage() |
1229 | try { |
1230 | await this.kernel.ports.getNextMessage() |
1231 | } catch (e) { |
1232 | t.pass('should throw if already trying to get a message') |
1233 | } |
1234 | } |
1235 | } |
1236 | |
1237 | const hypervisor = new Hypervisor(tree) |
1238 | hypervisor.registerContainer(TestVMContainer) |
1239 | |
1240 | const port = hypervisor.creationService.getPort() |
1241 | |
1242 | await hypervisor.send(port, new CreationMessage({ |
1243 | data: { |
1244 | type: TestVMContainer.typeId |
1245 | }, |
1246 | ports: [port] |
1247 | })) |
1248 | }) |
1249 |
Built with git-ssb-web