diff --git a/packages/SwingSet/test/test-simulate-upgrade.js b/packages/SwingSet/test/test-simulate-upgrade.js new file mode 100644 index 000000000000..e4c9c1ad4d7a --- /dev/null +++ b/packages/SwingSet/test/test-simulate-upgrade.js @@ -0,0 +1,42 @@ +import { test, simulateIncarnation } from '../tools/prepare-test-env-ava.js'; + +// eslint-disable-next-line import/order +import { provideDurableMapStore, vivifyKind } from '@agoric/vat-data'; + +const initFoo = () => ({ count: 0 }); +const fooBehavior = { + increment: ({ state }) => { + state.count += 1; + }, + read: ({ state }) => state.count, +}; + +test('simulate upgrade', async t => { + await simulateIncarnation(async baggage => { + // first incarnation + const s = provideDurableMapStore(baggage, 'store'); + const makeFoo = vivifyKind(baggage, 'foo', initFoo, fooBehavior); + + s.init('key1', 'value1'); + + const foo1 = makeFoo(); + s.init('foo1', foo1); + foo1.increment(); + foo1.increment(); + t.is(foo1.read(), 2); + }); + + await simulateIncarnation(async baggage => { + // second incarnation + const s = provideDurableMapStore(baggage, 'store'); + const makeFoo = vivifyKind(baggage, 'foo', initFoo, fooBehavior); + + t.is(s.get('key1'), 'value1'); + + const foo1 = s.get('foo1'); + t.is(foo1.read(), 2); + foo1.increment(); + t.is(foo1.read(), 3); + t.is(makeFoo().read(), 0); + }); +}); diff --git a/packages/SwingSet/tools/prepare-test-env-ava.js b/packages/SwingSet/tools/prepare-test-env-ava.js index 914762bc2453..7465e409d1ed 100644 --- a/packages/SwingSet/tools/prepare-test-env-ava.js +++ b/packages/SwingSet/tools/prepare-test-env-ava.js @@ -13,6 +13,8 @@ import '@endo/ses-ava/exported.js'; import { wrapTest } from '@endo/ses-ava'; import rawTest from 'ava'; +export { simulateIncarnation } from './prepare-test-env.js'; + /** @type {typeof rawTest} */ // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error // @ts-ignore XXX https://github.com/endojs/endo/issues/1235 diff --git a/packages/SwingSet/tools/prepare-test-env.js b/packages/SwingSet/tools/prepare-test-env.js index 8fa049dd23be..fc80daeead6e 100644 --- a/packages/SwingSet/tools/prepare-test-env.js +++ b/packages/SwingSet/tools/prepare-test-env.js @@ -11,39 +11,74 @@ import '@endo/init/pre-bundle-source.js'; import './install-ses-debug.js'; import { makeFakeVirtualStuff } from '@agoric/swingset-liveslots/tools/fakeVirtualSupport.js'; -const { vom, cm, wpm } = makeFakeVirtualStuff({ cacheSize: 3 }); - -const { - defineKind, - defineKindMulti, - defineDurableKind, - defineDurableKindMulti, - makeKindHandle, - canBeDurable, -} = vom; - -const { - makeScalarBigMapStore, - makeScalarBigWeakMapStore, - makeScalarBigSetStore, - makeScalarBigWeakSetStore, -} = cm; - -const { watchPromise, providePromiseWatcher } = wpm; - -const VatData = harden({ - defineKind, - defineKindMulti, - defineDurableKind, - defineDurableKindMulti, - makeKindHandle, - providePromiseWatcher, - watchPromise, - makeScalarBigMapStore, - makeScalarBigWeakMapStore, - makeScalarBigSetStore, - makeScalarBigWeakSetStore, - canBeDurable, -}); +function makeEverything(options) { + const { vom, cm, wpm } = makeFakeVirtualStuff(options); + + const { + defineKind, + defineKindMulti, + defineDurableKind, + defineDurableKindMulti, + makeKindHandle, + canBeDurable, + flushCache, + } = vom; + + const { + makeScalarBigMapStore, + makeScalarBigWeakMapStore, + makeScalarBigSetStore, + makeScalarBigWeakSetStore, + provideBaggage, + } = cm; + + const { watchPromise, providePromiseWatcher } = wpm; + + const vatData = { + defineKind, + defineKindMulti, + defineDurableKind, + defineDurableKindMulti, + makeKindHandle, + providePromiseWatcher, + watchPromise, + makeScalarBigMapStore, + makeScalarBigWeakMapStore, + makeScalarBigSetStore, + makeScalarBigWeakSetStore, + canBeDurable, + }; + + return harden({ vatData, provideBaggage, flushCache }); +} + +// we replace these during simulated upgrade, so each incarnation gets +// a new set +let currentVatData = makeEverything({ cacheSize: 3 }).vatData; + +// these are exported/imported in pieces, so they are immutable +const VatData = {}; +for (const name of Object.keys(currentVatData)) { + VatData[name] = (...args) => currentVatData[name](...args); +} +harden(VatData); globalThis.VatData = VatData; + +let incarnationStore; + +// runs async thunk() within a simulated per-upgrade "incarnation" +export const simulateIncarnation = async thunk => { + // make a new backing store by cloning the previous one, if any + incarnationStore = new Map(incarnationStore); + const { vatData, provideBaggage, flushCache } = makeEverything({ + cacheSize: 3, + fakeStore: incarnationStore, + }); + currentVatData = vatData; + const baggage = provideBaggage(); + + await thunk(baggage); + flushCache(); +}; +harden(simulateIncarnation); diff --git a/packages/swingset-liveslots/tools/fakeCollectionManager.js b/packages/swingset-liveslots/tools/fakeCollectionManager.js index 5ea9f44b0b8f..d11954761ab6 100644 --- a/packages/swingset-liveslots/tools/fakeCollectionManager.js +++ b/packages/swingset-liveslots/tools/fakeCollectionManager.js @@ -7,6 +7,7 @@ export function makeFakeCollectionManager(vrm, fakeStuff, _options = {}) { makeScalarBigSetStore, makeScalarBigWeakSetStore, initializeStoreKindInfo, + provideBaggage, } = makeCollectionManager( fakeStuff.syscall, vrm, @@ -26,6 +27,7 @@ export function makeFakeCollectionManager(vrm, fakeStuff, _options = {}) { makeScalarBigWeakMapStore, makeScalarBigSetStore, makeScalarBigWeakSetStore, + provideBaggage, }; const debugTools = { diff --git a/packages/swingset-liveslots/tools/fakeVirtualSupport.js b/packages/swingset-liveslots/tools/fakeVirtualSupport.js index e9a551ebaadf..cabedf6a17e2 100644 --- a/packages/swingset-liveslots/tools/fakeVirtualSupport.js +++ b/packages/swingset-liveslots/tools/fakeVirtualSupport.js @@ -32,9 +32,9 @@ export function makeFakeLiveSlotsStuff(options = {}) { FinalizationRegistry = FakeFinalizationRegistry, addToPossiblyDeadSet = () => {}, addToPossiblyRetiredSet = () => {}, + fakeStore = new Map(), } = options; - const fakeStore = new Map(); let sortedKeys; let priorKeyReturned; let priorKeyIndex;