From 21d86cd2befdb36e120958c03a4c2be76e22c880 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 26 Apr 2019 12:29:28 +0200 Subject: [PATCH] Delete all children of ItemType when it is deleted --- src/structs/AbstractItem.js | 1 + src/structs/ItemType.js | 18 +++++++++++++++--- src/types/AbstractType.js | 9 +++------ src/utils/Transaction.js | 2 +- src/utils/cursor.js | 5 +---- tests/testHelper.js | 34 +++++++++++++++++++++++----------- 6 files changed, 44 insertions(+), 25 deletions(-) diff --git a/src/structs/AbstractItem.js b/src/structs/AbstractItem.js index bcdbbe22..f63a97a4 100644 --- a/src/structs/AbstractItem.js +++ b/src/structs/AbstractItem.js @@ -425,6 +425,7 @@ export class AbstractItem extends AbstractStruct { * @private */ gc (transaction, store) { + this.delete(transaction) let r if (this.parent._item !== null && this.parent._item.deleted) { r = new GC(this.id, this.length) diff --git a/src/structs/ItemType.js b/src/structs/ItemType.js index 9765cc5a..98503838 100644 --- a/src/structs/ItemType.js +++ b/src/structs/ItemType.js @@ -100,9 +100,21 @@ export class ItemType extends AbstractItem { * @private */ delete (transaction) { - super.delete(transaction) - transaction.changed.delete(this.type) - transaction.changedParentTypes.delete(this.type) + if (!this.deleted) { + super.delete(transaction) + let item = this.type._start + while (item !== null) { + if (!item.deleted) { + item.delete(transaction) + } + item = item.right + } + this.type._map.forEach(item => { + item.delete(transaction) + }) + transaction.changed.delete(this.type) + transaction.changedParentTypes.delete(this.type) + } } /** diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index a26948dc..0afec930 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -454,13 +454,10 @@ export const typeArrayDelete = (transaction, parent, index, length) => { if (length === 0) { return } let n = parent._start // compute the first item to be deleted - for (; n !== null; n = n.right) { + for (; n !== null && index > 0; n = n.right) { if (!n.deleted && n.countable) { - if (index <= n.length) { - if (index < n.length && index > 0) { - n = getItemCleanStart(transaction, transaction.y.store, createID(n.id.client, n.id.clock + index)) - } - break + if (index < n.length) { + getItemCleanStart(transaction, transaction.y.store, createID(n.id.client, n.id.clock + index)) } index -= n.length } diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index 1b16ed0a..8061618d 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -165,9 +165,9 @@ export const transact = (y, f) => { // transaction cleanup const store = transaction.y.store const ds = transaction.deleteSet - // replace deleted items with ItemDeleted / GC sortAndMergeDeleteSet(ds) y.emit('afterTransaction', [transaction, y]) + // replace deleted items with ItemDeleted / GC for (const [client, deleteItems] of ds.clients) { /** * @type {Array} diff --git a/src/utils/cursor.js b/src/utils/cursor.js index 75f7aaad..a9e4d24d 100644 --- a/src/utils/cursor.js +++ b/src/utils/cursor.js @@ -270,8 +270,5 @@ export const createAbsolutePositionFromCursor = (cursor, y) => { * @function */ export const compareCursors = (a, b) => a === b || ( - a !== null && b !== null && a.tname === b.tname && ( - (a.item !== null && b.item !== null && compareIDs(a.item, b.item)) || - (a.type !== null && b.type !== null && compareIDs(a.type, b.type)) - ) + a !== null && b !== null && a.tname === b.tname && compareIDs(a.item, b.item) && compareIDs(a.type, b.type) ) diff --git a/tests/testHelper.js b/tests/testHelper.js index 5d46c533..23b44a79 100644 --- a/tests/testHelper.js +++ b/tests/testHelper.js @@ -230,11 +230,13 @@ export class TestConnector { } /** + * @template T * @param {t.TestCase} tc * @param {{users?:number}} conf - * @return {{testConnector:TestConnector,users:Array,array0:Y.Array,array1:Y.Array,array2:Y.Array,map0:Y.Map,map1:Y.Map,map2:Y.Map,map3:Y.Map,text0:Y.Text,text1:Y.Text,text2:Y.Text,xml0:Y.XmlElement,xml1:Y.XmlElement,xml2:Y.XmlElement}} + * @param {InitTestObjectCallback} initTestObject + * @return {{testObjects:Array,testConnector:TestConnector,users:Array,array0:Y.Array,array1:Y.Array,array2:Y.Array,map0:Y.Map,map1:Y.Map,map2:Y.Map,map3:Y.Map,text0:Y.Text,text1:Y.Text,text2:Y.Text,xml0:Y.XmlElement,xml1:Y.XmlElement,xml2:Y.XmlElement}} */ -export const init = (tc, { users = 5 } = {}) => { +export const init = (tc, { users = 5 } = {}, initTestObject) => { /** * @type {Object} */ @@ -254,6 +256,7 @@ export const init = (tc, { users = 5 } = {}) => { result['text' + i] = y.get('text', Y.Text) } testConnector.syncAll() + result.testObjects = result.users.map(initTestObject) // @ts-ignore return result } @@ -365,15 +368,24 @@ export const compareDS = (ds1, ds2) => { } /** - * @param {t.TestCase} tc - * @param {Array} mods - * @param {number} iterations + * @template T + * @callback InitTestObjectCallback + * @param {TestYInstance} y + * @return {T} */ -export const applyRandomTests = (tc, mods, iterations) => { + +/** + * @template T + * @param {t.TestCase} tc + * @param {Array} mods + * @param {number} iterations + * @param {InitTestObjectCallback} [initTestObject] + */ +export const applyRandomTests = (tc, mods, iterations, initTestObject) => { const gen = tc.prng - const result = init(tc, { users: 5 }) + const result = init(tc, { users: 5 }, initTestObject || (() => null)) const { testConnector, users } = result - for (var i = 0; i < iterations; i++) { + for (let i = 0; i < iterations; i++) { if (prng.int31(gen, 0, 100) <= 2) { // 2% chance to disconnect/reconnect a random user if (prng.bool(gen)) { @@ -388,9 +400,9 @@ export const applyRandomTests = (tc, mods, iterations) => { // 50% chance to flush a random message testConnector.flushRandomMessage() } - let user = prng.oneOf(gen, users) - var test = prng.oneOf(gen, mods) - test(user, gen) + const user = prng.int31(gen, 0, users.length - 1) + const test = prng.oneOf(gen, mods) + test(users[user], gen, result.testObjects[user]) } compare(users) return result