add mergeUpdates tests to comparison framework
This commit is contained in:
		
							parent
							
								
									d8868c47e1
								
							
						
					
					
						commit
						fbbf085278
					
				@ -640,10 +640,7 @@ export class Item extends AbstractStruct {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    if (origin === null && rightOrigin === null) {
 | 
					    if (origin === null && rightOrigin === null) {
 | 
				
			||||||
      const parent = /** @type {AbstractType<any>} */ (this.parent)
 | 
					      const parent = /** @type {AbstractType<any>} */ (this.parent)
 | 
				
			||||||
      if (parent.constructor === String) { // this edge case was added by differential updates
 | 
					      if (parent._item !== undefined) {
 | 
				
			||||||
        encoder.writeParentInfo(true) // write parentYKey
 | 
					 | 
				
			||||||
        encoder.writeString(parent)
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        const parentItem = parent._item
 | 
					        const parentItem = parent._item
 | 
				
			||||||
        if (parentItem === null) {
 | 
					        if (parentItem === null) {
 | 
				
			||||||
          // parent type on y._map
 | 
					          // parent type on y._map
 | 
				
			||||||
@ -655,6 +652,14 @@ export class Item extends AbstractStruct {
 | 
				
			|||||||
          encoder.writeParentInfo(false) // write parent id
 | 
					          encoder.writeParentInfo(false) // write parent id
 | 
				
			||||||
          encoder.writeLeftID(parentItem.id)
 | 
					          encoder.writeLeftID(parentItem.id)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					      } else if (parent.constructor === String) { // this edge case was added by differential updates
 | 
				
			||||||
 | 
					        encoder.writeParentInfo(true) // write parentYKey
 | 
				
			||||||
 | 
					        encoder.writeString(parent)
 | 
				
			||||||
 | 
					      } else if (parent.constructor === ID) {
 | 
				
			||||||
 | 
					        encoder.writeParentInfo(false) // write parent id
 | 
				
			||||||
 | 
					        encoder.writeLeftID(parent)
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        error.unexpectedCase()
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (parentSub !== null) {
 | 
					      if (parentSub !== null) {
 | 
				
			||||||
        encoder.writeString(parentSub)
 | 
					        encoder.writeString(parentSub)
 | 
				
			||||||
 | 
				
			|||||||
@ -14,7 +14,6 @@ import {
 | 
				
			|||||||
  getState,
 | 
					  getState,
 | 
				
			||||||
  findIndexSS,
 | 
					  findIndexSS,
 | 
				
			||||||
  UpdateEncoderV2,
 | 
					  UpdateEncoderV2,
 | 
				
			||||||
  DefaultDSEncoder,
 | 
					 | 
				
			||||||
  applyUpdateV2,
 | 
					  applyUpdateV2,
 | 
				
			||||||
  AbstractDSDecoder, AbstractDSEncoder, DSEncoderV2, DSDecoderV1, DSDecoderV2, Transaction, Doc, DeleteSet, Item // eslint-disable-line
 | 
					  AbstractDSDecoder, AbstractDSEncoder, DSEncoderV2, DSDecoderV1, DSDecoderV2, Transaction, Doc, DeleteSet, Item // eslint-disable-line
 | 
				
			||||||
} from '../internals.js'
 | 
					} from '../internals.js'
 | 
				
			||||||
@ -23,6 +22,7 @@ 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 { DSEncoderV1 } from './UpdateEncoder.js'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class Snapshot {
 | 
					export class Snapshot {
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@ -91,7 +91,7 @@ export const encodeSnapshotV2 = (snapshot, encoder = new DSEncoderV2()) => {
 | 
				
			|||||||
 * @param {Snapshot} snapshot
 | 
					 * @param {Snapshot} snapshot
 | 
				
			||||||
 * @return {Uint8Array}
 | 
					 * @return {Uint8Array}
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export const encodeSnapshot = snapshot => encodeSnapshotV2(snapshot, new DefaultDSEncoder())
 | 
					export const encodeSnapshot = snapshot => encodeSnapshotV2(snapshot, new DSEncoderV1())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @param {Uint8Array} buf
 | 
					 * @param {Uint8Array} buf
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,7 @@ import {
 | 
				
			|||||||
  Item,
 | 
					  Item,
 | 
				
			||||||
  generateNewClientId,
 | 
					  generateNewClientId,
 | 
				
			||||||
  createID,
 | 
					  createID,
 | 
				
			||||||
  AbstractUpdateEncoder, GC, StructStore, UpdateEncoderV2, DefaultUpdateEncoder, AbstractType, AbstractStruct, YEvent, Doc // eslint-disable-line
 | 
					  AbstractUpdateEncoder, GC, StructStore, UpdateEncoderV2, AbstractType, AbstractStruct, YEvent, Doc // eslint-disable-line
 | 
				
			||||||
} from '../internals.js'
 | 
					} from '../internals.js'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as map from 'lib0/map.js'
 | 
					import * as map from 'lib0/map.js'
 | 
				
			||||||
@ -19,6 +19,7 @@ import * as math from 'lib0/math.js'
 | 
				
			|||||||
import * as set from 'lib0/set.js'
 | 
					import * as set from 'lib0/set.js'
 | 
				
			||||||
import * as logging from 'lib0/logging.js'
 | 
					import * as logging from 'lib0/logging.js'
 | 
				
			||||||
import { callAll } from 'lib0/function.js'
 | 
					import { callAll } from 'lib0/function.js'
 | 
				
			||||||
 | 
					import { UpdateEncoderV1 } from './UpdateEncoder.js'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * A transaction is created for every change on the Yjs model. It is possible
 | 
					 * A transaction is created for every change on the Yjs model. It is possible
 | 
				
			||||||
@ -337,7 +338,7 @@ const cleanupTransactions = (transactionCleanups, i) => {
 | 
				
			|||||||
      // @todo Merge all the transactions into one and provide send the data as a single update message
 | 
					      // @todo Merge all the transactions into one and provide send the data as a single update message
 | 
				
			||||||
      doc.emit('afterTransactionCleanup', [transaction, doc])
 | 
					      doc.emit('afterTransactionCleanup', [transaction, doc])
 | 
				
			||||||
      if (doc._observers.has('update')) {
 | 
					      if (doc._observers.has('update')) {
 | 
				
			||||||
        const encoder = new DefaultUpdateEncoder()
 | 
					        const encoder = new UpdateEncoderV1()
 | 
				
			||||||
        const hasContent = writeUpdateMessageFromTransaction(encoder, transaction)
 | 
					        const hasContent = writeUpdateMessageFromTransaction(encoder, transaction)
 | 
				
			||||||
        if (hasContent) {
 | 
					        if (hasContent) {
 | 
				
			||||||
          doc.emit('update', [encoder.toUint8Array(), transaction.origin, doc])
 | 
					          doc.emit('update', [encoder.toUint8Array(), transaction.origin, doc])
 | 
				
			||||||
 | 
				
			|||||||
@ -41,25 +41,6 @@ import * as decoding from 'lib0/decoding.js'
 | 
				
			|||||||
import * as binary from 'lib0/binary.js'
 | 
					import * as binary from 'lib0/binary.js'
 | 
				
			||||||
import * as map from 'lib0/map.js'
 | 
					import * as map from 'lib0/map.js'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export let DefaultDSEncoder = DSEncoderV1
 | 
					 | 
				
			||||||
export let DefaultDSDecoder = DSDecoderV1
 | 
					 | 
				
			||||||
export let DefaultUpdateEncoder = UpdateEncoderV1
 | 
					 | 
				
			||||||
export let DefaultUpdateDecoder = UpdateDecoderV1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const useV1Encoding = () => {
 | 
					 | 
				
			||||||
  DefaultDSEncoder = DSEncoderV1
 | 
					 | 
				
			||||||
  DefaultDSDecoder = DSDecoderV1
 | 
					 | 
				
			||||||
  DefaultUpdateEncoder = UpdateEncoderV1
 | 
					 | 
				
			||||||
  DefaultUpdateDecoder = UpdateDecoderV1
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const useV2Encoding = () => {
 | 
					 | 
				
			||||||
  DefaultDSEncoder = DSEncoderV2
 | 
					 | 
				
			||||||
  DefaultDSDecoder = DSDecoderV2
 | 
					 | 
				
			||||||
  DefaultUpdateEncoder = UpdateEncoderV2
 | 
					 | 
				
			||||||
  DefaultUpdateDecoder = UpdateDecoderV2
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @param {AbstractUpdateEncoder} encoder
 | 
					 * @param {AbstractUpdateEncoder} encoder
 | 
				
			||||||
 * @param {Array<GC|Item>} structs All structs by `client`
 | 
					 * @param {Array<GC|Item>} structs All structs by `client`
 | 
				
			||||||
@ -445,7 +426,7 @@ export const readUpdateV2 = (decoder, ydoc, transactionOrigin, structDecoder = n
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * @function
 | 
					 * @function
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export const readUpdate = (decoder, ydoc, transactionOrigin) => readUpdateV2(decoder, ydoc, transactionOrigin, new DefaultUpdateDecoder(decoder))
 | 
					export const readUpdate = (decoder, ydoc, transactionOrigin) => readUpdateV2(decoder, ydoc, transactionOrigin, new UpdateDecoderV1(decoder))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Apply a document update created by, for example, `y.on('update', update => ..)` or `update = encodeStateAsUpdate()`.
 | 
					 * Apply a document update created by, for example, `y.on('update', update => ..)` or `update = encodeStateAsUpdate()`.
 | 
				
			||||||
@ -475,7 +456,7 @@ export const applyUpdateV2 = (ydoc, update, transactionOrigin, YDecoder = Update
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * @function
 | 
					 * @function
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export const applyUpdate = (ydoc, update, transactionOrigin) => applyUpdateV2(ydoc, update, transactionOrigin, DefaultUpdateDecoder)
 | 
					export const applyUpdate = (ydoc, update, transactionOrigin) => applyUpdateV2(ydoc, update, transactionOrigin, UpdateDecoderV1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Write all the document as a single update message. If you specify the state of the remote client (`targetStateVector`) it will
 | 
					 * Write all the document as a single update message. If you specify the state of the remote client (`targetStateVector`) it will
 | 
				
			||||||
@ -523,7 +504,7 @@ export const encodeStateAsUpdateV2 = (doc, encodedTargetStateVector, encoder = n
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * @function
 | 
					 * @function
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export const encodeStateAsUpdate = (doc, encodedTargetStateVector) => encodeStateAsUpdateV2(doc, encodedTargetStateVector, new DefaultUpdateEncoder())
 | 
					export const encodeStateAsUpdate = (doc, encodedTargetStateVector) => encodeStateAsUpdateV2(doc, encodedTargetStateVector, new UpdateEncoderV1())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Read state vector from Decoder and return as Map
 | 
					 * Read state vector from Decoder and return as Map
 | 
				
			||||||
@ -562,7 +543,7 @@ export const decodeStateVectorV2 = decodedState => readStateVector(new DSDecoder
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * @function
 | 
					 * @function
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export const decodeStateVector = decodedState => readStateVector(new DefaultDSDecoder(decoding.createDecoder(decodedState)))
 | 
					export const decodeStateVector = decodedState => readStateVector(new DSDecoderV1(decoding.createDecoder(decodedState)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @param {AbstractDSEncoder} encoder
 | 
					 * @param {AbstractDSEncoder} encoder
 | 
				
			||||||
@ -608,4 +589,4 @@ export const encodeStateVectorV2 = (doc, encoder = new DSEncoderV2()) => {
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * @function
 | 
					 * @function
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export const encodeStateVector = doc => encodeStateVectorV2(doc, new DefaultDSEncoder())
 | 
					export const encodeStateVector = doc => encodeStateVectorV2(doc, new DSEncoderV1())
 | 
				
			||||||
 | 
				
			|||||||
@ -27,6 +27,33 @@ const broadcastMessage = (y, m) => {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let useV2 = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export let encodeStateAsUpdate = Y.encodeStateAsUpdate
 | 
				
			||||||
 | 
					export let mergeUpdates = Y.mergeUpdates
 | 
				
			||||||
 | 
					export let applyUpdate = Y.applyUpdate
 | 
				
			||||||
 | 
					export let logUpdate = Y.logUpdate
 | 
				
			||||||
 | 
					export let updateEventName = 'update'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const setEncoders = () => {
 | 
				
			||||||
 | 
					  encodeStateAsUpdate = useV2 ? Y.encodeStateAsUpdateV2 : Y.encodeStateAsUpdate
 | 
				
			||||||
 | 
					  mergeUpdates = useV2 ? Y.mergeUpdatesV2 : Y.mergeUpdates
 | 
				
			||||||
 | 
					  applyUpdate = useV2 ? Y.applyUpdateV2 : Y.applyUpdate
 | 
				
			||||||
 | 
					  logUpdate = useV2 ? Y.logUpdateV2 : Y.logUpdate
 | 
				
			||||||
 | 
					  updateEventName = useV2 ? 'updateV2' : 'update'
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const useV1Encoding = () => {
 | 
				
			||||||
 | 
					  useV2 = false
 | 
				
			||||||
 | 
					  setEncoders()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const useV2Encoding = () => {
 | 
				
			||||||
 | 
					  useV2 = false
 | 
				
			||||||
 | 
					  console.error('sync protocol doesnt support v2 protocol yet, fallback to v1 encoding') // @Todo
 | 
				
			||||||
 | 
					  setEncoders()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class TestYInstance extends Y.Doc {
 | 
					export class TestYInstance extends Y.Doc {
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * @param {TestConnector} testConnector
 | 
					   * @param {TestConnector} testConnector
 | 
				
			||||||
@ -44,12 +71,19 @@ export class TestYInstance extends Y.Doc {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    this.receiving = new Map()
 | 
					    this.receiving = new Map()
 | 
				
			||||||
    testConnector.allConns.add(this)
 | 
					    testConnector.allConns.add(this)
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The list of received updates.
 | 
				
			||||||
 | 
					     * We are going to merge them later using Y.mergeUpdates and check if the resulting document is correct.
 | 
				
			||||||
 | 
					     * @type {Array<Uint8Array>}
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    this.updates = []
 | 
				
			||||||
    // set up observe on local model
 | 
					    // set up observe on local model
 | 
				
			||||||
    this.on('update', /** @param {Uint8Array} update @param {any} origin */ (update, origin) => {
 | 
					    this.on(updateEventName, /** @param {Uint8Array} update @param {any} origin */ (update, origin) => {
 | 
				
			||||||
      if (origin !== testConnector) {
 | 
					      if (origin !== testConnector) {
 | 
				
			||||||
        const encoder = encoding.createEncoder()
 | 
					        const encoder = encoding.createEncoder()
 | 
				
			||||||
        syncProtocol.writeUpdate(encoder, update)
 | 
					        syncProtocol.writeUpdate(encoder, update)
 | 
				
			||||||
        broadcastMessage(this, encoding.toUint8Array(encoder))
 | 
					        broadcastMessage(this, encoding.toUint8Array(encoder))
 | 
				
			||||||
 | 
					        this.updates.push(update)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    this.connect()
 | 
					    this.connect()
 | 
				
			||||||
@ -162,6 +196,17 @@ export class TestConnector {
 | 
				
			|||||||
        // send reply message
 | 
					        // send reply message
 | 
				
			||||||
        sender._receive(encoding.toUint8Array(encoder), receiver)
 | 
					        sender._receive(encoding.toUint8Array(encoder), receiver)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        // If update message, add the received message to the list of received messages
 | 
				
			||||||
 | 
					        const decoder = decoding.createDecoder(m)
 | 
				
			||||||
 | 
					        const messageType = decoding.readVarUint(decoder)
 | 
				
			||||||
 | 
					        switch (messageType) {
 | 
				
			||||||
 | 
					          case syncProtocol.messageYjsUpdate:
 | 
				
			||||||
 | 
					          case syncProtocol.messageYjsSyncStep2:
 | 
				
			||||||
 | 
					            receiver.updates.push(decoding.readVarUint8Array(decoder))
 | 
				
			||||||
 | 
					            break
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
      return true
 | 
					      return true
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return false
 | 
					    return false
 | 
				
			||||||
@ -240,9 +285,9 @@ export const init = (tc, { users = 5 } = {}, initTestObject) => {
 | 
				
			|||||||
  const gen = tc.prng
 | 
					  const gen = tc.prng
 | 
				
			||||||
  // choose an encoding approach at random
 | 
					  // choose an encoding approach at random
 | 
				
			||||||
  if (prng.bool(gen)) {
 | 
					  if (prng.bool(gen)) {
 | 
				
			||||||
    Y.useV2Encoding()
 | 
					    useV2Encoding()
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    Y.useV1Encoding()
 | 
					    useV1Encoding()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const testConnector = new TestConnector(gen)
 | 
					  const testConnector = new TestConnector(gen)
 | 
				
			||||||
@ -258,7 +303,7 @@ export const init = (tc, { users = 5 } = {}, initTestObject) => {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
  testConnector.syncAll()
 | 
					  testConnector.syncAll()
 | 
				
			||||||
  result.testObjects = result.users.map(initTestObject || (() => null))
 | 
					  result.testObjects = result.users.map(initTestObject || (() => null))
 | 
				
			||||||
  Y.useV1Encoding()
 | 
					  useV1Encoding()
 | 
				
			||||||
  return /** @type {any} */ (result)
 | 
					  return /** @type {any} */ (result)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -274,6 +319,14 @@ export const init = (tc, { users = 5 } = {}, initTestObject) => {
 | 
				
			|||||||
export const compare = users => {
 | 
					export const compare = users => {
 | 
				
			||||||
  users.forEach(u => u.connect())
 | 
					  users.forEach(u => u.connect())
 | 
				
			||||||
  while (users[0].tc.flushAllMessages()) {}
 | 
					  while (users[0].tc.flushAllMessages()) {}
 | 
				
			||||||
 | 
					  // For each document, merge all received document updates with Y.mergeUpdates and create a new document which will be added to the list of "users"
 | 
				
			||||||
 | 
					  // This ensures that mergeUpdates works correctly
 | 
				
			||||||
 | 
					  const mergedDocs = users.map(user => {
 | 
				
			||||||
 | 
					    const ydoc = new Y.Doc()
 | 
				
			||||||
 | 
					    applyUpdate(ydoc, mergeUpdates(user.updates))
 | 
				
			||||||
 | 
					    return ydoc
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  users.push(.../** @type {any} */(mergedDocs))
 | 
				
			||||||
  const userArrayValues = users.map(u => u.getArray('array').toJSON())
 | 
					  const userArrayValues = users.map(u => u.getArray('array').toJSON())
 | 
				
			||||||
  const userMapValues = users.map(u => u.getMap('map').toJSON())
 | 
					  const userMapValues = users.map(u => u.getMap('map').toJSON())
 | 
				
			||||||
  const userXmlValues = users.map(u => u.get('xml', Y.YXmlElement).toString())
 | 
					  const userXmlValues = users.map(u => u.get('xml', Y.YXmlElement).toString())
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user