add tests to snapshots case and fix the case of empty ranges

This commit is contained in:
Kevin Jahns 2020-09-28 18:32:24 +02:00
parent be8cc8a20c
commit e1a2ccd7f6
2 changed files with 42 additions and 43 deletions

View File

@ -3,7 +3,6 @@ import {
isDeleted, isDeleted,
createDeleteSetFromStructStore, createDeleteSetFromStructStore,
getStateVector, getStateVector,
getItem,
getItemCleanStart, getItemCleanStart,
iterateDeletedStructs, iterateDeletedStructs,
writeDeleteSet, writeDeleteSet,
@ -12,22 +11,18 @@ import {
readStateVector, readStateVector,
createDeleteSet, createDeleteSet,
createID, createID,
ID,
getState, getState,
findIndexCleanStart,
AbstractStruct,
writeClientsStructs,
findIndexSS, findIndexSS,
readUpdateV2, UpdateEncoderV2,
UpdateEncoderV2, UpdateDecoderV2, DefaultDSEncoder,
AbstractDSDecoder, AbstractDSEncoder, DSEncoderV1, DSEncoderV2, DSDecoderV1, DSDecoderV2, Transaction, Doc, DeleteSet, Item // eslint-disable-line applyUpdateV2,
AbstractDSDecoder, AbstractDSEncoder, DSEncoderV2, DSDecoderV1, DSDecoderV2, Transaction, Doc, DeleteSet, Item // eslint-disable-line
} from '../internals.js' } from '../internals.js'
import * as map from 'lib0/map.js' import * as map from 'lib0/map.js'
import * as set from 'lib0/set.js' import * as set from 'lib0/set.js'
import * as decoding from 'lib0/decoding.js' import * as decoding from 'lib0/decoding.js'
import * as encoding from 'lib0/encoding.js' import * as encoding from 'lib0/encoding.js'
import { DefaultDSEncoder } from './encoding.js'
export class Snapshot { export class Snapshot {
/** /**
@ -161,59 +156,47 @@ export const splitSnapshotAffectedStructs = (transaction, snapshot) => {
/** /**
* @param {Doc} originDoc * @param {Doc} originDoc
* @param {Snapshot} snapshot * @param {Snapshot} snapshot
* @param {Doc} [newDoc] Optionally, you may define the Yjs document that receives the data from originDoc
* @return {Doc} * @return {Doc}
*/ */
export const createDocFromSnapshot = (originDoc, snapshot) => { export const createDocFromSnapshot = (originDoc, snapshot, newDoc = new Doc()) => {
if (originDoc.gc) { if (originDoc.gc) {
// we should not try to restore a GC-ed document, because some of the restored items might have their content deleted // we should not try to restore a GC-ed document, because some of the restored items might have their content deleted
throw new Error('originDoc must not be garbage collected') throw new Error('originDoc must not be garbage collected')
} }
const { sv, ds } = snapshot const { sv, ds } = snapshot
const needState = new Map(sv)
let len = 0 const encoder = new UpdateEncoderV2()
const tempStructs = []
/**
* State Map
* @type any[]
*/
const itemsToIntegrate = []
/**
* @type Uint8Array
*/
let updateBuffer = new Uint8Array()
originDoc.transact(transaction => { originDoc.transact(transaction => {
const encoder = new UpdateEncoderV2() let size = 0
sv.forEach(clock => {
encoding.writeVarUint(encoder.restEncoder, sv.size) if (clock > 0) {
size++
}
})
encoding.writeVarUint(encoder.restEncoder, size)
// splitting the structs before writing them to the encoder // splitting the structs before writing them to the encoder
for (const [client, clock] of sv) { for (const [client, clock] of sv) {
if (clock === 0) {
continue
}
if (clock < getState(originDoc.store, client)) { if (clock < getState(originDoc.store, client)) {
getItemCleanStart(transaction, createID(client, clock)) getItemCleanStart(transaction, createID(client, clock))
} }
const structs = originDoc.store.clients.get(client) || [] const structs = originDoc.store.clients.get(client) || []
const lastStructIndex = findIndexSS(structs, clock - 1) const lastStructIndex = findIndexSS(structs, clock - 1)
// write # encoded structs // write # encoded structs
encoding.writeVarUint(encoder.restEncoder, lastStructIndex + 1) encoding.writeVarUint(encoder.restEncoder, lastStructIndex + 1)
encoder.writeClient(client) encoder.writeClient(client)
encoding.writeVarUint(encoder.restEncoder, structs[0].id.clock) // first clock written is 0
const firstStruct = structs[0] encoding.writeVarUint(encoder.restEncoder, 0)
firstStruct.write(encoder, 0) for (let i = 0; i <= lastStructIndex; i++) {
for (let i = 1; i <= lastStructIndex; i++) {
structs[i].write(encoder, 0) structs[i].write(encoder, 0)
} }
} }
writeDeleteSet(encoder, ds) writeDeleteSet(encoder, ds)
updateBuffer = encoder.toUint8Array()
}) })
const newDoc = new Doc() applyUpdateV2(newDoc, encoder.toUint8Array(), 'snapshot')
readUpdateV2(decoding.createDecoder(updateBuffer), newDoc, 'snapshot')
return newDoc return newDoc
} }

View File

@ -17,6 +17,26 @@ export const testBasicRestoreSnapshot = tc => {
t.compare(doc.getArray('array').toArray(), ['hello', 'world']) t.compare(doc.getArray('array').toArray(), ['hello', 'world'])
} }
/**
* @param {t.TestCase} tc
*/
export const testEmptyRestoreSnapshot = tc => {
const doc = new Doc({ gc: false })
const snap = snapshot(doc)
snap.sv.set(9999, 0)
doc.getArray().insert(0, ['world'])
const docRestored = createDocFromSnapshot(doc, snap)
t.compare(docRestored.getArray().toArray(), [])
t.compare(doc.getArray().toArray(), ['world'])
// now this snapshot reflects the latest state. It shoult still work.
const snap2 = snapshot(doc)
const docRestored2 = createDocFromSnapshot(doc, snap2)
t.compare(docRestored2.getArray().toArray(), ['world'])
}
/** /**
* @param {t.TestCase} tc * @param {t.TestCase} tc
*/ */
@ -74,7 +94,6 @@ export const testRestoreLeftItem = tc => {
t.compare(doc.getArray('array').toArray(), ['item0']) t.compare(doc.getArray('array').toArray(), ['item0'])
} }
/** /**
* @param {t.TestCase} tc * @param {t.TestCase} tc
*/ */
@ -91,7 +110,6 @@ export const testDeletedItemsBase = tc => {
t.compare(doc.getArray('array').toArray(), ['item0']) t.compare(doc.getArray('array').toArray(), ['item0'])
} }
/** /**
* @param {t.TestCase} tc * @param {t.TestCase} tc
*/ */
@ -108,7 +126,6 @@ export const testDeletedItems2 = tc => {
t.compare(doc.getArray('array').toArray(), ['item0', 'item1', 'item3']) t.compare(doc.getArray('array').toArray(), ['item0', 'item1', 'item3'])
} }
/** /**
* @param {t.TestCase} tc * @param {t.TestCase} tc
*/ */
@ -152,4 +169,3 @@ export const testDependentChanges = tc => {
const docRestored1 = createDocFromSnapshot(array1.doc, snap) const docRestored1 = createDocFromSnapshot(array1.doc, snap)
t.compare(docRestored1.getArray('array').toArray(), ['user1item1', 'user2item1']) t.compare(docRestored1.getArray('array').toArray(), ['user1item1', 'user2item1'])
} }