Files
yjs/tests/updates.tests.js

178 lines
4.3 KiB
JavaScript
Raw Normal View History

2020-12-07 19:47:48 +01:00
import * as t from 'lib0/testing.js'
import { init, compare } from './testHelper.js' // eslint-disable-line
import * as Y from '../src/index.js'
/**
* @typedef {Object} Enc
* @property {function(Array<Uint8Array>):Uint8Array} Enc.mergeUpdates
* @property {function(Y.Doc):Uint8Array} Enc.encodeStateAsUpdate
* @property {function(Y.Doc, Uint8Array):void} Enc.applyUpdate
* @property {function(Uint8Array):void} Enc.logUpdate
* @property {string} Enc.updateEventName
* @property {string} Enc.description
*/
/**
* @type {Enc}
*/
2020-12-29 16:59:27 +01:00
const encV1 = {
mergeUpdates: Y.mergeUpdates,
encodeStateAsUpdate: Y.encodeStateAsUpdate,
applyUpdate: Y.applyUpdate,
logUpdate: Y.logUpdate,
updateEventName: 'update',
description: 'V1'
2020-12-29 16:59:27 +01:00
}
/**
* @type {Enc}
*/
2020-12-29 16:59:27 +01:00
const encV2 = {
mergeUpdates: Y.mergeUpdatesV2,
encodeStateAsUpdate: Y.encodeStateAsUpdateV2,
applyUpdate: Y.applyUpdateV2,
logUpdate: Y.logUpdateV2,
updateEventName: 'updateV2',
description: 'V2'
}
/**
* @type {Enc}
*/
const encDoc = {
mergeUpdates: (updates) => {
const ydoc = new Y.Doc()
updates.forEach(update => {
Y.applyUpdate(ydoc, update)
})
return Y.encodeStateAsUpdate(ydoc)
},
encodeStateAsUpdate: Y.encodeStateAsUpdate,
applyUpdate: Y.applyUpdate,
logUpdate: Y.logUpdate,
updateEventName: 'update',
description: 'Merge via Y.Doc'
2020-12-29 16:59:27 +01:00
}
2020-12-16 23:26:38 +01:00
const encoders = [encV1, encV2, encDoc]
2020-12-16 23:26:38 +01:00
2020-12-07 19:47:48 +01:00
/**
* @param {Array<Y.Doc>} users
* @param {Enc} enc
2020-12-07 19:47:48 +01:00
*/
2020-12-29 16:59:27 +01:00
const fromUpdates = (users, enc) => {
2020-12-07 19:47:48 +01:00
const updates = users.map(user =>
2020-12-29 16:59:27 +01:00
enc.encodeStateAsUpdate(user)
2020-12-07 19:47:48 +01:00
)
const ydoc = new Y.Doc()
2020-12-29 16:59:27 +01:00
enc.applyUpdate(ydoc, enc.mergeUpdates(updates))
2020-12-07 19:47:48 +01:00
return ydoc
}
/**
* @param {t.TestCase} tc
*/
export const testMergeUpdates = tc => {
const { users, array0, array1 } = init(tc, { users: 3 })
array0.insert(0, [1])
array1.insert(0, [2])
compare(users)
2020-12-29 16:59:27 +01:00
encoders.forEach(enc => {
const merged = fromUpdates(users, enc)
t.compareArrays(array0.toArray(), merged.getArray('array').toArray())
})
2020-12-07 19:47:48 +01:00
}
/**
2020-12-16 23:45:28 +01:00
* @param {Y.Doc} ydoc
* @param {Array<Uint8Array>} updates - expecting at least 4 updates
* @param {Enc} enc
2020-12-07 19:47:48 +01:00
*/
2020-12-29 16:59:27 +01:00
const checkUpdateCases = (ydoc, updates, enc) => {
2020-12-16 23:26:38 +01:00
const cases = []
// Case 1: Simple case, simply merge everything
2020-12-29 16:59:27 +01:00
cases.push(enc.mergeUpdates(updates))
2020-12-16 23:26:38 +01:00
// Case 2: Overlapping updates
2020-12-29 16:59:27 +01:00
cases.push(enc.mergeUpdates([
enc.mergeUpdates(updates.slice(2)),
enc.mergeUpdates(updates.slice(0, 2))
2020-12-16 23:26:38 +01:00
]))
// Case 3: Overlapping updates
2020-12-29 16:59:27 +01:00
cases.push(enc.mergeUpdates([
enc.mergeUpdates(updates.slice(2)),
enc.mergeUpdates(updates.slice(1, 3)),
2020-12-07 19:47:48 +01:00
updates[0]
2020-12-16 23:26:38 +01:00
]))
// Case 4: Separated updates (containing skips)
2020-12-29 16:59:27 +01:00
cases.push(enc.mergeUpdates([
enc.mergeUpdates([updates[0], updates[2]]),
enc.mergeUpdates([updates[1], updates[3]]),
enc.mergeUpdates(updates.slice(4))
2020-12-16 23:26:38 +01:00
]))
2020-12-07 19:47:48 +01:00
2020-12-16 23:26:38 +01:00
// Case 5: overlapping with many duplicates
2020-12-29 16:59:27 +01:00
cases.push(enc.mergeUpdates(cases))
2020-12-16 23:26:38 +01:00
2020-12-29 16:59:27 +01:00
const targetState = enc.encodeStateAsUpdate(ydoc)
2020-12-16 21:25:00 +01:00
t.info('Target State: ')
2020-12-29 16:59:27 +01:00
enc.logUpdate(targetState)
2020-12-16 23:26:38 +01:00
cases.forEach((updates, i) => {
t.info('State Case $' + i + ':')
2020-12-29 16:59:27 +01:00
enc.logUpdate(updates)
2020-12-07 19:47:48 +01:00
const merged = new Y.Doc()
2020-12-29 16:59:27 +01:00
enc.applyUpdate(merged, updates)
2020-12-16 23:45:28 +01:00
t.compareArrays(merged.getArray().toArray(), ydoc.getArray().toArray())
2020-12-07 19:47:48 +01:00
})
}
2020-12-16 23:45:28 +01:00
/**
* @param {t.TestCase} tc
*/
export const testMergeUpdates1 = tc => {
2020-12-29 16:59:27 +01:00
encoders.forEach((enc, i) => {
t.info(`Using encoder: ${enc.description}`)
2020-12-29 16:59:27 +01:00
const ydoc = new Y.Doc({ gc: false })
const updates = /** @type {Array<Uint8Array>} */ ([])
ydoc.on(enc.updateEventName, update => { updates.push(update) })
const array = ydoc.getArray()
array.insert(0, [1])
array.insert(0, [2])
array.insert(0, [3])
array.insert(0, [4])
checkUpdateCases(ydoc, updates, enc)
})
2020-12-16 23:45:28 +01:00
}
/**
* @param {t.TestCase} tc
*/
export const testMergeUpdates2 = tc => {
2020-12-29 16:59:27 +01:00
encoders.forEach((enc, i) => {
t.info(`Using encoder: ${enc.description}`)
2020-12-29 16:59:27 +01:00
const ydoc = new Y.Doc({ gc: false })
const updates = /** @type {Array<Uint8Array>} */ ([])
ydoc.on(enc.updateEventName, update => { updates.push(update) })
const array = ydoc.getArray()
array.insert(0, [1, 2])
array.delete(1, 1)
array.insert(0, [3, 4])
array.delete(1, 2)
checkUpdateCases(ydoc, updates, enc)
})
2020-12-16 23:45:28 +01:00
}
/**
* @todo be able to apply Skip structs to Yjs docs
*/