@agoric/async-flow 0.1.1-dev-16095c5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -0
- package/LICENSE +201 -0
- package/README.md +40 -0
- package/docs/async-flow-states.key +0 -0
- package/docs/async-flow-states.md +15 -0
- package/docs/async-flow-states.png +0 -0
- package/index.d.ts +2 -0
- package/index.d.ts.map +1 -0
- package/index.js +1 -0
- package/package.json +67 -0
- package/src/async-flow.d.ts +87 -0
- package/src/async-flow.d.ts.map +1 -0
- package/src/async-flow.js +502 -0
- package/src/bijection.d.ts +28 -0
- package/src/bijection.d.ts.map +1 -0
- package/src/bijection.js +132 -0
- package/src/convert.d.ts +5 -0
- package/src/convert.d.ts.map +1 -0
- package/src/convert.js +131 -0
- package/src/ephemera.d.ts +2 -0
- package/src/ephemera.d.ts.map +1 -0
- package/src/ephemera.js +35 -0
- package/src/equate.d.ts +2 -0
- package/src/equate.d.ts.map +1 -0
- package/src/equate.js +123 -0
- package/src/log-store.d.ts +25 -0
- package/src/log-store.d.ts.map +1 -0
- package/src/log-store.js +165 -0
- package/src/replay-membrane.d.ts +71 -0
- package/src/replay-membrane.d.ts.map +1 -0
- package/src/replay-membrane.js +435 -0
- package/src/type-guards.d.ts +4 -0
- package/src/type-guards.d.ts.map +1 -0
- package/src/type-guards.js +54 -0
- package/src/types.d.ts +70 -0
- package/src/types.d.ts.map +1 -0
- package/src/types.js +164 -0
- package/test/async-flow-crank.test.js +96 -0
- package/test/async-flow-no-this.js +59 -0
- package/test/async-flow.test.js +380 -0
- package/test/bad-host.test.js +205 -0
- package/test/bijection.test.js +118 -0
- package/test/convert.test.js +127 -0
- package/test/equate.test.js +116 -0
- package/test/log-store.test.js +112 -0
- package/test/prepare-test-env-ava.js +28 -0
- package/test/replay-membrane-settlement.test.js +154 -0
- package/test/replay-membrane-zombie.test.js +158 -0
- package/test/replay-membrane.test.js +271 -0
- package/tsconfig.build.json +11 -0
- package/tsconfig.json +13 -0
- package/typedoc.json +8 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// eslint-disable-next-line import/order
|
|
2
|
+
import {
|
|
3
|
+
test,
|
|
4
|
+
getBaggage,
|
|
5
|
+
annihilate,
|
|
6
|
+
nextLife,
|
|
7
|
+
} from './prepare-test-env-ava.js';
|
|
8
|
+
|
|
9
|
+
import { Fail } from '@endo/errors';
|
|
10
|
+
import { eventLoopIteration } from '@agoric/internal/src/testing-utils.js';
|
|
11
|
+
import { prepareVowTools } from '@agoric/vow';
|
|
12
|
+
import { makeHeapZone } from '@agoric/zone/heap.js';
|
|
13
|
+
import { makeVirtualZone } from '@agoric/zone/virtual.js';
|
|
14
|
+
import { makeDurableZone } from '@agoric/zone/durable.js';
|
|
15
|
+
|
|
16
|
+
import { prepareLogStore } from '../src/log-store.js';
|
|
17
|
+
import { prepareBijection } from '../src/bijection.js';
|
|
18
|
+
import { makeReplayMembrane } from '../src/replay-membrane.js';
|
|
19
|
+
|
|
20
|
+
const watchWake = _vowish => {};
|
|
21
|
+
const panic = problem => Fail`panic over ${problem}`;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {Zone} zone
|
|
25
|
+
*/
|
|
26
|
+
const preparePingee = zone =>
|
|
27
|
+
zone.exoClass('Pingee', undefined, () => ({}), {
|
|
28
|
+
ping() {},
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {any} t
|
|
33
|
+
* @param {Zone} zone
|
|
34
|
+
*/
|
|
35
|
+
const testFirstPlay = async (t, zone) => {
|
|
36
|
+
const vowTools = prepareVowTools(zone);
|
|
37
|
+
const { makeVowKit } = vowTools;
|
|
38
|
+
const makeLogStore = prepareLogStore(zone);
|
|
39
|
+
const makeBijection = prepareBijection(zone);
|
|
40
|
+
const makePingee = preparePingee(zone);
|
|
41
|
+
const { vow: v1, resolver: r1 } = zone.makeOnce('v1', () => makeVowKit());
|
|
42
|
+
const { vow: _v2, resolver: _r2 } = zone.makeOnce('v2', () => makeVowKit());
|
|
43
|
+
|
|
44
|
+
const log = zone.makeOnce('log', () => makeLogStore());
|
|
45
|
+
const bij = zone.makeOnce('bij', makeBijection);
|
|
46
|
+
|
|
47
|
+
const mem = makeReplayMembrane(log, bij, vowTools, watchWake, panic);
|
|
48
|
+
|
|
49
|
+
const p1 = mem.hostToGuest(v1);
|
|
50
|
+
t.deepEqual(log.dump(), []);
|
|
51
|
+
|
|
52
|
+
const pingee = zone.makeOnce('pingee', () => makePingee());
|
|
53
|
+
const guestPingee = mem.hostToGuest(pingee);
|
|
54
|
+
t.deepEqual(log.dump(), []);
|
|
55
|
+
|
|
56
|
+
guestPingee.ping();
|
|
57
|
+
t.deepEqual(log.dump(), [
|
|
58
|
+
// keep on separate lines
|
|
59
|
+
['checkCall', pingee, 'ping', [], 0],
|
|
60
|
+
['doReturn', 0, undefined],
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
r1.resolve('x');
|
|
64
|
+
t.is(await p1, 'x');
|
|
65
|
+
|
|
66
|
+
t.deepEqual(log.dump(), [
|
|
67
|
+
['checkCall', pingee, 'ping', [], 0],
|
|
68
|
+
['doReturn', 0, undefined],
|
|
69
|
+
['doFulfill', v1, 'x'],
|
|
70
|
+
]);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @param {any} t
|
|
75
|
+
* @param {Zone} zone
|
|
76
|
+
*/
|
|
77
|
+
const testReplay = async (t, zone) => {
|
|
78
|
+
const vowTools = prepareVowTools(zone);
|
|
79
|
+
prepareLogStore(zone);
|
|
80
|
+
prepareBijection(zone);
|
|
81
|
+
preparePingee(zone);
|
|
82
|
+
const { vow: v1 } = zone.makeOnce('v1', () => Fail`need v1`);
|
|
83
|
+
const { vow: v2, resolver: r2 } = zone.makeOnce('v2', () => Fail`need v2`);
|
|
84
|
+
|
|
85
|
+
const log = /** @type {LogStore} */ (
|
|
86
|
+
zone.makeOnce('log', () => Fail`need log`)
|
|
87
|
+
);
|
|
88
|
+
const bij = /** @type {Bijection} */ (
|
|
89
|
+
zone.makeOnce('bij', () => Fail`need bij`)
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const pingee = zone.makeOnce('pingee', () => Fail`need pingee`);
|
|
93
|
+
|
|
94
|
+
t.deepEqual(log.dump(), [
|
|
95
|
+
['checkCall', pingee, 'ping', [], 0],
|
|
96
|
+
['doReturn', 0, undefined],
|
|
97
|
+
['doFulfill', v1, 'x'],
|
|
98
|
+
]);
|
|
99
|
+
|
|
100
|
+
const mem = makeReplayMembrane(log, bij, vowTools, watchWake, panic);
|
|
101
|
+
t.true(log.isReplaying());
|
|
102
|
+
t.is(log.getIndex(), 0);
|
|
103
|
+
|
|
104
|
+
const guestPingee = mem.hostToGuest(pingee);
|
|
105
|
+
const p2 = mem.hostToGuest(v2);
|
|
106
|
+
// @ts-expect-error TS doesn't know that r2 is a resolver
|
|
107
|
+
r2.resolve('y');
|
|
108
|
+
await eventLoopIteration();
|
|
109
|
+
|
|
110
|
+
const p1 = mem.hostToGuest(v1);
|
|
111
|
+
mem.wake();
|
|
112
|
+
t.true(log.isReplaying());
|
|
113
|
+
t.is(log.getIndex(), 0);
|
|
114
|
+
t.deepEqual(log.dump(), [
|
|
115
|
+
['checkCall', pingee, 'ping', [], 0],
|
|
116
|
+
['doReturn', 0, undefined],
|
|
117
|
+
['doFulfill', v1, 'x'],
|
|
118
|
+
]);
|
|
119
|
+
|
|
120
|
+
guestPingee.ping();
|
|
121
|
+
t.is(await p1, 'x');
|
|
122
|
+
t.is(await p2, 'y');
|
|
123
|
+
t.false(log.isReplaying());
|
|
124
|
+
|
|
125
|
+
t.deepEqual(log.dump(), [
|
|
126
|
+
['checkCall', pingee, 'ping', [], 0],
|
|
127
|
+
['doReturn', 0, undefined],
|
|
128
|
+
['doFulfill', v1, 'x'],
|
|
129
|
+
['doFulfill', v2, 'y'],
|
|
130
|
+
]);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
test.serial('test heap replay-membrane settlement', async t => {
|
|
134
|
+
const zone = makeHeapZone('heapRoot');
|
|
135
|
+
return testFirstPlay(t, zone);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test.serial('test virtual replay-membrane settlement', async t => {
|
|
139
|
+
annihilate();
|
|
140
|
+
const zone = makeVirtualZone('virtualRoot');
|
|
141
|
+
return testFirstPlay(t, zone);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test.serial('test durable replay-membrane settlement', async t => {
|
|
145
|
+
annihilate();
|
|
146
|
+
|
|
147
|
+
nextLife();
|
|
148
|
+
const zone1 = makeDurableZone(getBaggage(), 'durableRoot');
|
|
149
|
+
await testFirstPlay(t, zone1);
|
|
150
|
+
|
|
151
|
+
nextLife();
|
|
152
|
+
const zone3 = makeDurableZone(getBaggage(), 'durableRoot');
|
|
153
|
+
return testReplay(t, zone3);
|
|
154
|
+
});
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// eslint-disable-next-line import/order
|
|
2
|
+
import {
|
|
3
|
+
test,
|
|
4
|
+
getBaggage,
|
|
5
|
+
annihilate,
|
|
6
|
+
nextLife,
|
|
7
|
+
} from './prepare-test-env-ava.js';
|
|
8
|
+
|
|
9
|
+
import { Fail } from '@endo/errors';
|
|
10
|
+
import { eventLoopIteration } from '@agoric/internal/src/testing-utils.js';
|
|
11
|
+
import { prepareVowTools } from '@agoric/vow';
|
|
12
|
+
import { makeHeapZone } from '@agoric/zone/heap.js';
|
|
13
|
+
import { makeVirtualZone } from '@agoric/zone/virtual.js';
|
|
14
|
+
import { makeDurableZone } from '@agoric/zone/durable.js';
|
|
15
|
+
|
|
16
|
+
import { prepareLogStore } from '../src/log-store.js';
|
|
17
|
+
import { prepareBijection } from '../src/bijection.js';
|
|
18
|
+
import { makeReplayMembrane } from '../src/replay-membrane.js';
|
|
19
|
+
|
|
20
|
+
const watchWake = _vowish => {};
|
|
21
|
+
const panic = problem => Fail`panic over ${problem}`;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {any} t
|
|
25
|
+
* @param {Zone} zone
|
|
26
|
+
*/
|
|
27
|
+
const testMissingStop = async (t, zone) => {
|
|
28
|
+
const vowTools = prepareVowTools(zone);
|
|
29
|
+
const { makeVowKit } = vowTools;
|
|
30
|
+
const makeLogStore = prepareLogStore(zone);
|
|
31
|
+
const makeBijection = prepareBijection(zone);
|
|
32
|
+
|
|
33
|
+
const log = makeLogStore();
|
|
34
|
+
const bij = makeBijection();
|
|
35
|
+
|
|
36
|
+
const memA = makeReplayMembrane(log, bij, vowTools, watchWake, panic);
|
|
37
|
+
|
|
38
|
+
const { vow: v1, resolver: r1 } = makeVowKit();
|
|
39
|
+
|
|
40
|
+
const p1A = memA.hostToGuest(v1);
|
|
41
|
+
t.true(bij.has(p1A, v1));
|
|
42
|
+
|
|
43
|
+
await eventLoopIteration();
|
|
44
|
+
|
|
45
|
+
t.deepEqual(log.dump(), []);
|
|
46
|
+
|
|
47
|
+
// do all the steps to drop an old membrane and set up a new membrane,
|
|
48
|
+
// except stopping the old membrane,
|
|
49
|
+
// to demonstate why `makeGuestForHostVow` also tests`stopped`.
|
|
50
|
+
log.reset();
|
|
51
|
+
bij.reset();
|
|
52
|
+
const memB = makeReplayMembrane(log, bij, vowTools, watchWake, panic);
|
|
53
|
+
|
|
54
|
+
const p1B = memB.hostToGuest(v1);
|
|
55
|
+
t.true(bij.has(p1B, v1));
|
|
56
|
+
t.false(bij.hasGuest(p1A));
|
|
57
|
+
|
|
58
|
+
await eventLoopIteration();
|
|
59
|
+
|
|
60
|
+
t.deepEqual(log.dump(), []);
|
|
61
|
+
|
|
62
|
+
r1.resolve('x');
|
|
63
|
+
|
|
64
|
+
await eventLoopIteration();
|
|
65
|
+
|
|
66
|
+
t.deepEqual(log.dump(), [
|
|
67
|
+
// keep line break
|
|
68
|
+
['doFulfill', v1, 'x'],
|
|
69
|
+
['doFulfill', v1, 'x'], // this duplication is wrong, is the point
|
|
70
|
+
]);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @param {any} t
|
|
75
|
+
* @param {Zone} zone
|
|
76
|
+
*/
|
|
77
|
+
const testProperStop = async (t, zone) => {
|
|
78
|
+
const vowTools = prepareVowTools(zone);
|
|
79
|
+
const { makeVowKit } = vowTools;
|
|
80
|
+
const makeLogStore = prepareLogStore(zone);
|
|
81
|
+
const makeBijection = prepareBijection(zone);
|
|
82
|
+
|
|
83
|
+
const log = makeLogStore();
|
|
84
|
+
const bij = makeBijection();
|
|
85
|
+
|
|
86
|
+
const memA = makeReplayMembrane(log, bij, vowTools, watchWake, panic);
|
|
87
|
+
|
|
88
|
+
const { vow: v1, resolver: r1 } = makeVowKit();
|
|
89
|
+
|
|
90
|
+
const p1A = memA.hostToGuest(v1);
|
|
91
|
+
t.true(bij.has(p1A, v1));
|
|
92
|
+
|
|
93
|
+
await eventLoopIteration();
|
|
94
|
+
|
|
95
|
+
t.deepEqual(log.dump(), []);
|
|
96
|
+
|
|
97
|
+
// do all the steps to drop an old membrane and set up a new membrane,
|
|
98
|
+
// including stopping the old membrane,
|
|
99
|
+
// to demonstate why `makeGuestForHostVow` also tests`stopped`.
|
|
100
|
+
log.reset();
|
|
101
|
+
bij.reset();
|
|
102
|
+
memA.stop(); // the point
|
|
103
|
+
const memB = makeReplayMembrane(log, bij, vowTools, watchWake, panic);
|
|
104
|
+
|
|
105
|
+
const p1B = memB.hostToGuest(v1);
|
|
106
|
+
t.true(bij.has(p1B, v1));
|
|
107
|
+
t.false(bij.hasGuest(p1A));
|
|
108
|
+
|
|
109
|
+
await eventLoopIteration();
|
|
110
|
+
|
|
111
|
+
t.deepEqual(log.dump(), []);
|
|
112
|
+
|
|
113
|
+
r1.resolve('x');
|
|
114
|
+
|
|
115
|
+
await eventLoopIteration();
|
|
116
|
+
|
|
117
|
+
t.deepEqual(log.dump(), [
|
|
118
|
+
// keep line break
|
|
119
|
+
['doFulfill', v1, 'x'],
|
|
120
|
+
]);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
test.serial('test heap replay-membrane missing stop', async t => {
|
|
124
|
+
const zone = makeHeapZone('heapRoot');
|
|
125
|
+
return testMissingStop(t, zone);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test.serial('test heap replay-membrane proper stop', async t => {
|
|
129
|
+
annihilate();
|
|
130
|
+
const zone = makeHeapZone('heapRoot');
|
|
131
|
+
return testProperStop(t, zone);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test.serial('test virtual replay-membrane missing stop', async t => {
|
|
135
|
+
annihilate();
|
|
136
|
+
const zone = makeVirtualZone('virtualRoot');
|
|
137
|
+
return testMissingStop(t, zone);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test.serial('test virtual replay-membrane proper stop', async t => {
|
|
141
|
+
annihilate();
|
|
142
|
+
const zone = makeVirtualZone('virtualRoot');
|
|
143
|
+
return testProperStop(t, zone);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test.serial('test durable replay-membrane missing stop', async t => {
|
|
147
|
+
annihilate();
|
|
148
|
+
nextLife();
|
|
149
|
+
const zone = makeDurableZone(getBaggage(), 'durableRoot');
|
|
150
|
+
return testMissingStop(t, zone);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test.serial('test durable replay-membrane proper stop', async t => {
|
|
154
|
+
annihilate();
|
|
155
|
+
nextLife();
|
|
156
|
+
const zone = makeDurableZone(getBaggage(), 'durableRoot');
|
|
157
|
+
return testProperStop(t, zone);
|
|
158
|
+
});
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
// eslint-disable-next-line import/order
|
|
2
|
+
import {
|
|
3
|
+
test,
|
|
4
|
+
getBaggage,
|
|
5
|
+
annihilate,
|
|
6
|
+
nextLife,
|
|
7
|
+
asyncFlowVerbose,
|
|
8
|
+
} from './prepare-test-env-ava.js';
|
|
9
|
+
|
|
10
|
+
import { Fail } from '@endo/errors';
|
|
11
|
+
import { isPromise } from '@endo/promise-kit';
|
|
12
|
+
import { prepareVowTools } from '@agoric/vow';
|
|
13
|
+
import { makeHeapZone } from '@agoric/zone/heap.js';
|
|
14
|
+
import { makeVirtualZone } from '@agoric/zone/virtual.js';
|
|
15
|
+
import { makeDurableZone } from '@agoric/zone/durable.js';
|
|
16
|
+
|
|
17
|
+
import { prepareLogStore } from '../src/log-store.js';
|
|
18
|
+
import { prepareBijection } from '../src/bijection.js';
|
|
19
|
+
import { makeReplayMembrane } from '../src/replay-membrane.js';
|
|
20
|
+
|
|
21
|
+
const watchWake = _vowish => {};
|
|
22
|
+
const panic = problem => Fail`panic over ${problem}`;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {Zone} zone
|
|
26
|
+
* @param {number} [k]
|
|
27
|
+
*/
|
|
28
|
+
const prepareOrchestra = (zone, k = 1) =>
|
|
29
|
+
zone.exoClass(
|
|
30
|
+
'Orchestra',
|
|
31
|
+
undefined,
|
|
32
|
+
(factor, vow, resolver) => ({ factor, vow, resolver }),
|
|
33
|
+
{
|
|
34
|
+
scale(n) {
|
|
35
|
+
const { state } = this;
|
|
36
|
+
return k * state.factor * n;
|
|
37
|
+
},
|
|
38
|
+
vow() {
|
|
39
|
+
const { state } = this;
|
|
40
|
+
return state.vow;
|
|
41
|
+
},
|
|
42
|
+
resolve(x) {
|
|
43
|
+
const { state } = this;
|
|
44
|
+
state.resolver.resolve(x);
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param {any} t
|
|
51
|
+
* @param {Zone} zone
|
|
52
|
+
* @param {boolean} [showOnConsole]
|
|
53
|
+
*/
|
|
54
|
+
const testFirstPlay = async (t, zone, showOnConsole = false) => {
|
|
55
|
+
const vowTools = prepareVowTools(zone);
|
|
56
|
+
const { makeVowKit } = vowTools;
|
|
57
|
+
const makeLogStore = prepareLogStore(zone);
|
|
58
|
+
const makeBijection = prepareBijection(zone);
|
|
59
|
+
const makeOrchestra = prepareOrchestra(zone);
|
|
60
|
+
const { vow: v1, resolver: r1 } = makeVowKit();
|
|
61
|
+
const { vow: v2, resolver: r2 } = makeVowKit();
|
|
62
|
+
|
|
63
|
+
const log = zone.makeOnce('log', () => makeLogStore());
|
|
64
|
+
const bij = zone.makeOnce('bij', makeBijection);
|
|
65
|
+
|
|
66
|
+
const mem = makeReplayMembrane(log, bij, vowTools, watchWake, panic);
|
|
67
|
+
|
|
68
|
+
const g1 = mem.hostToGuest(v1);
|
|
69
|
+
t.true(isPromise(g1));
|
|
70
|
+
r1.resolve('x');
|
|
71
|
+
t.is(await g1, 'x');
|
|
72
|
+
|
|
73
|
+
const hOrch7 = makeOrchestra(7, v2, r2);
|
|
74
|
+
t.false(bij.hasHost(hOrch7));
|
|
75
|
+
const gOrch7 = mem.hostToGuest(hOrch7);
|
|
76
|
+
t.true(bij.has(gOrch7, hOrch7));
|
|
77
|
+
|
|
78
|
+
const prod = gOrch7.scale(3);
|
|
79
|
+
t.is(prod, 21);
|
|
80
|
+
|
|
81
|
+
let gErr;
|
|
82
|
+
try {
|
|
83
|
+
gOrch7.scale(9n);
|
|
84
|
+
} catch (e) {
|
|
85
|
+
gErr = e;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// TODO make E work across the membrane *well*
|
|
89
|
+
// TODO also try E on remote promise
|
|
90
|
+
// const prodP = E(gOrch7).scale(33);
|
|
91
|
+
// t.is(await prodP, 231);
|
|
92
|
+
// const badP = E(gOrch7).scale(99n);
|
|
93
|
+
// let gErr1;
|
|
94
|
+
// try {
|
|
95
|
+
// await badP;
|
|
96
|
+
// } catch (e) {
|
|
97
|
+
// gErr1 = e;
|
|
98
|
+
// }
|
|
99
|
+
// t.is(gErr1.name, 'TypeError');
|
|
100
|
+
|
|
101
|
+
t.deepEqual(log.dump(), [
|
|
102
|
+
['doFulfill', v1, 'x'],
|
|
103
|
+
['checkCall', hOrch7, 'scale', [3], 1],
|
|
104
|
+
['doReturn', 1, 21],
|
|
105
|
+
['checkCall', hOrch7, 'scale', [9n], 3],
|
|
106
|
+
['doThrow', 3, mem.guestToHost(gErr)],
|
|
107
|
+
]);
|
|
108
|
+
|
|
109
|
+
if (showOnConsole) {
|
|
110
|
+
// To see the annotation chain. Once we're synced with the next ses-ava,
|
|
111
|
+
// change this to a t.log, so we will see the annotation chain in context.
|
|
112
|
+
t.log('gErr', gErr);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @param {any} t
|
|
118
|
+
* @param {Zone} zone
|
|
119
|
+
*/
|
|
120
|
+
const testBadReplay = async (t, zone) => {
|
|
121
|
+
const vowTools = prepareVowTools(zone);
|
|
122
|
+
prepareLogStore(zone);
|
|
123
|
+
prepareBijection(zone);
|
|
124
|
+
prepareOrchestra(zone);
|
|
125
|
+
|
|
126
|
+
const log = /** @type {LogStore} */ (
|
|
127
|
+
zone.makeOnce('log', () => Fail`need log`)
|
|
128
|
+
);
|
|
129
|
+
const bij = /** @type {Bijection} */ (
|
|
130
|
+
zone.makeOnce('bij', () => Fail`need bij`)
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
const dump = log.dump();
|
|
134
|
+
const v1 = dump[0][1];
|
|
135
|
+
const hOrch7 = dump[1][1];
|
|
136
|
+
const hErr = dump[4][2];
|
|
137
|
+
|
|
138
|
+
t.false(bij.hasHost(hOrch7));
|
|
139
|
+
|
|
140
|
+
t.deepEqual(dump, [
|
|
141
|
+
['doFulfill', v1, 'x'],
|
|
142
|
+
['checkCall', hOrch7, 'scale', [3], 1],
|
|
143
|
+
['doReturn', 1, 21],
|
|
144
|
+
['checkCall', hOrch7, 'scale', [9n], 3],
|
|
145
|
+
['doThrow', 3, hErr],
|
|
146
|
+
]);
|
|
147
|
+
|
|
148
|
+
const mem = makeReplayMembrane(log, bij, vowTools, watchWake, panic);
|
|
149
|
+
|
|
150
|
+
const g1 = mem.hostToGuest(v1);
|
|
151
|
+
mem.wake();
|
|
152
|
+
t.is(await g1, 'x');
|
|
153
|
+
const gOrch7 = mem.hostToGuest(hOrch7);
|
|
154
|
+
t.true(bij.has(gOrch7, hOrch7));
|
|
155
|
+
|
|
156
|
+
// failure of guest to reproduce behavior from previous incarnations
|
|
157
|
+
t.throws(() => gOrch7.scale(4), {
|
|
158
|
+
message: /^panic over "\[Error: replay/,
|
|
159
|
+
});
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* @param {any} t
|
|
164
|
+
* @param {Zone} zone
|
|
165
|
+
*/
|
|
166
|
+
const testGoodReplay = async (t, zone) => {
|
|
167
|
+
const vowTools = prepareVowTools(zone);
|
|
168
|
+
prepareLogStore(zone);
|
|
169
|
+
prepareBijection(zone);
|
|
170
|
+
prepareOrchestra(zone, 2); // 2 is new incarnation behavior change
|
|
171
|
+
|
|
172
|
+
const log = /** @type {LogStore} */ (
|
|
173
|
+
zone.makeOnce('log', () => Fail`need log`)
|
|
174
|
+
);
|
|
175
|
+
const bij = /** @type {Bijection} */ (
|
|
176
|
+
zone.makeOnce('bij', () => Fail`need bij`)
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
const dump = log.dump();
|
|
180
|
+
const v1 = dump[0][1];
|
|
181
|
+
const hOrch7 = dump[1][1];
|
|
182
|
+
const hErr = dump[4][2];
|
|
183
|
+
|
|
184
|
+
t.false(bij.hasHost(hOrch7));
|
|
185
|
+
|
|
186
|
+
t.deepEqual(dump, [
|
|
187
|
+
['doFulfill', v1, 'x'],
|
|
188
|
+
['checkCall', hOrch7, 'scale', [3], 1],
|
|
189
|
+
['doReturn', 1, 21],
|
|
190
|
+
['checkCall', hOrch7, 'scale', [9n], 3],
|
|
191
|
+
['doThrow', 3, hErr],
|
|
192
|
+
]);
|
|
193
|
+
|
|
194
|
+
const oldLogLen = dump.length;
|
|
195
|
+
|
|
196
|
+
const mem = makeReplayMembrane(log, bij, vowTools, watchWake, panic);
|
|
197
|
+
|
|
198
|
+
const g1 = mem.hostToGuest(v1);
|
|
199
|
+
mem.wake();
|
|
200
|
+
t.is(await g1, 'x');
|
|
201
|
+
const gOrch7 = mem.hostToGuest(hOrch7);
|
|
202
|
+
t.true(bij.has(gOrch7, hOrch7));
|
|
203
|
+
|
|
204
|
+
// replay
|
|
205
|
+
const prodA = gOrch7.scale(3);
|
|
206
|
+
t.is(prodA, 21); // According to log of earlier incarnations
|
|
207
|
+
// let gErr;
|
|
208
|
+
try {
|
|
209
|
+
gOrch7.scale(9n);
|
|
210
|
+
} catch (e) {
|
|
211
|
+
// gErr = e;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// new play
|
|
215
|
+
const prodB = gOrch7.scale(3);
|
|
216
|
+
t.is(prodB, 42); // According to new incarnation behavior
|
|
217
|
+
|
|
218
|
+
const g2 = gOrch7.vow();
|
|
219
|
+
const h2 = mem.guestToHost(g2);
|
|
220
|
+
t.true(isPromise(g2));
|
|
221
|
+
const pairA = [gOrch7, g1];
|
|
222
|
+
gOrch7.resolve(pairA);
|
|
223
|
+
const pairB = await g2;
|
|
224
|
+
const [gOrchB, gB] = pairB;
|
|
225
|
+
t.not(pairB, pairA);
|
|
226
|
+
t.is(gOrchB, gOrch7);
|
|
227
|
+
t.is(gB, g1);
|
|
228
|
+
|
|
229
|
+
t.deepEqual(log.dump(), [
|
|
230
|
+
['doFulfill', v1, 'x'],
|
|
231
|
+
['checkCall', hOrch7, 'scale', [3], 1],
|
|
232
|
+
['doReturn', 1, 21],
|
|
233
|
+
['checkCall', hOrch7, 'scale', [9n], 3],
|
|
234
|
+
['doThrow', 3, hErr],
|
|
235
|
+
|
|
236
|
+
['checkCall', hOrch7, 'scale', [3], oldLogLen],
|
|
237
|
+
['doReturn', oldLogLen, 42],
|
|
238
|
+
['checkCall', hOrch7, 'vow', [], oldLogLen + 2],
|
|
239
|
+
['doReturn', oldLogLen + 2, h2],
|
|
240
|
+
['checkCall', hOrch7, 'resolve', [[hOrch7, v1]], oldLogLen + 4],
|
|
241
|
+
['doReturn', oldLogLen + 4, undefined],
|
|
242
|
+
['doFulfill', h2, [hOrch7, v1]],
|
|
243
|
+
]);
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
test.serial('test heap replay-membrane', async t => {
|
|
247
|
+
const zone = makeHeapZone('heapRoot');
|
|
248
|
+
return testFirstPlay(t, zone, asyncFlowVerbose());
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
test.serial('test virtual replay-membrane', async t => {
|
|
252
|
+
annihilate();
|
|
253
|
+
const zone = makeVirtualZone('virtualRoot');
|
|
254
|
+
return testFirstPlay(t, zone);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test.serial('test durable replay-membrane', async t => {
|
|
258
|
+
annihilate();
|
|
259
|
+
|
|
260
|
+
nextLife();
|
|
261
|
+
const zone1 = makeDurableZone(getBaggage(), 'durableRoot');
|
|
262
|
+
await testFirstPlay(t, zone1);
|
|
263
|
+
|
|
264
|
+
nextLife();
|
|
265
|
+
const zone2 = makeDurableZone(getBaggage(), 'durableRoot');
|
|
266
|
+
await testBadReplay(t, zone2);
|
|
267
|
+
|
|
268
|
+
nextLife();
|
|
269
|
+
const zone3 = makeDurableZone(getBaggage(), 'durableRoot');
|
|
270
|
+
return testGoodReplay(t, zone3);
|
|
271
|
+
});
|
package/tsconfig.json
ADDED