From 45814c4e0055b92e502bd96fc99bd949e01fc32e Mon Sep 17 00:00:00 2001
From: Kevin Jahns <kevin.jahns@rwth-aachen.de>
Date: Sat, 17 Oct 2015 17:16:36 +0200
Subject: [PATCH] fixed bug (o.right is already gc'd), implemented some test
 helpers

---
 src/Database.js         | 36 ++++++++++++++++++--------
 src/Transaction.js      | 29 +++++++++++++--------
 src/Types/Array.spec.js | 57 +++++++++++++++++++++++++++++++++++++++--
 src/Types/Map.spec.js   |  2 +-
 4 files changed, 100 insertions(+), 24 deletions(-)

diff --git a/src/Database.js b/src/Database.js
index b0bce0b1..b803a09a 100644
--- a/src/Database.js
+++ b/src/Database.js
@@ -36,7 +36,9 @@ class AbstractDatabase {
     // wont be kept in memory.
     this.initializedTypes = {}
     this.whenUserIdSetListener = null
-
+    if (typeof YConcurrency_TestingMode !== 'undefined') {
+      this.executeOrder = []
+    }
     this.gc1 = [] // first stage
     this.gc2 = [] // second stage -> after that, remove the op
     this.gcTimeout = opts.gcTimeout || 5000
@@ -64,6 +66,21 @@ class AbstractDatabase {
       garbageCollect()
     }
   }
+  addToDebug () {
+    if (typeof YConcurrency_TestingMode !== 'undefined') {
+      var command = Array.prototype.map.call(arguments, function (s) {
+        if (typeof s === 'string') {
+          return s
+        } else {
+          return JSON.stringify(s)
+        }
+      }).join('').replace(/"/g, "'").replace(/,/g, ', ').replace(/:/g, ': ')
+      this.executeOrder.push(command)
+    }
+  }
+  getDebugData () {
+    console.log(this.executeOrder.join('\n'))
+  }
   stopGarbageCollector () {
     var self = this
     return new Promise(function (resolve) {
@@ -230,6 +247,7 @@ class AbstractDatabase {
     Actually execute an operation, when all expected operations are available.
   */
   * tryExecute (op) {
+    this.store.addToDebug('yield* this.store.tryExecute.call(this, ', JSON.stringify(op), ')')
     if (op.struct === 'Delete') {
       yield* Y.Struct.Delete.execute.call(this, op)
     } else if ((yield* this.getOperation(op.id)) == null && !(yield* this.isGarbageCollected(op.id))) {
@@ -284,16 +302,14 @@ class AbstractDatabase {
     }
   }
   requestTransaction (makeGen, callImmediately) {
-    if (!this.transactionInProgress) {
+    if (callImmediately) {
+      this.transact(makeGen)
+    } else if (!this.transactionInProgress) {
       this.transactionInProgress = true
-      if (callImmediately) {
-        this.transact(makeGen)
-      } else {
-        var self = this
-        setTimeout(function () {
-          self.transact(makeGen)
-        }, 0)
-      }
+      var self = this
+      setTimeout(function () {
+        self.transact(makeGen)
+      }, 0)
     } else {
       this.waitingTransactions.push(makeGen)
     }
diff --git a/src/Transaction.js b/src/Transaction.js
index 7f147a90..4b2a602b 100644
--- a/src/Transaction.js
+++ b/src/Transaction.js
@@ -300,6 +300,7 @@ class Transaction {
     * reset origins of all right ops
   */
   * garbageCollectOperation (id) {
+    this.store.addToDebug('yield* this.garbageCollectOperation(', id, ')')
     // check to increase the state of the respective user
     var state = yield* this.getState(id[0])
     if (state.clock === id[1]) {
@@ -371,15 +372,22 @@ class Transaction {
         // remove gc'd op from parent, if it exists
         var parent = yield* this.getOperation(o.parent)
         var setParent = false // whether to save parent to the os
-        if (Y.utils.compareIds(parent.start, o.id)) {
-          // gc'd op is the start
-          setParent = true
-          parent.start = o.right
-        }
-        if (Y.utils.compareIds(parent.end, o.id)) {
-          // gc'd op is the end
-          setParent = true
-          parent.end = o.left
+        if (o.parentSub != null) {
+          if (Y.utils.compareIds(parent.map[o.parentSub], o.id)) {
+            setParent = true
+            parent.map[o.parentSub] = o.right
+          }
+        } else {
+          if (Y.utils.compareIds(parent.start, o.id)) {
+            // gc'd op is the start
+            setParent = true
+            parent.start = o.right
+          }
+          if (Y.utils.compareIds(parent.end, o.id)) {
+            // gc'd op is the end
+            setParent = true
+            parent.end = o.left
+          }
         }
         if (setParent) {
           yield* this.setOperation(parent)
@@ -570,9 +578,8 @@ class Transaction {
         continue
       }
       var startPos = startSS[user] || 0
-      var endPos = endState.clock
 
-      yield* this.os.iterate(this, [user, startPos], [user, endPos], function * (op) {
+      yield* this.os.iterate(this, [user, startPos], [user, Number.MAX_VALUE], function * (op) {
         ops.push(op)
       })
     }
diff --git a/src/Types/Array.spec.js b/src/Types/Array.spec.js
index ebf5a190..b633b085 100644
--- a/src/Types/Array.spec.js
+++ b/src/Types/Array.spec.js
@@ -1,8 +1,8 @@
 /* global createUsers, wait, Y, compareAllUsers, getRandomNumber, applyRandomTransactionsAllRejoinNoGC, applyRandomTransactionsWithGC, async, garbageCollectAllUsers, describeManyTimes */
 /* eslint-env browser,jasmine */
 
-var numberOfYArrayTests = 40
-var repeatArrayTests = 1
+var numberOfYArrayTests = 50
+var repeatArrayTests = 2
 
 describe('Array Type', function () {
   var y1, y2, y3, yconfig1, yconfig2, yconfig3, flushAll
@@ -192,6 +192,59 @@ describe('Array Type', function () {
       expect(l2.toArray()).toEqual([])
       done()
     }))
+    it('debug right not existend in Insert.execute', async(function * (done) {
+      yconfig1.db.requestTransaction(function * () {
+        var ops = [{'struct':'Map','type':'Map','id':['130',0],'map':{}},{'id':['130',1],'left':null,'right':null,'origin':null,'parent':['_',0],'struct':'Insert','parentSub':'Map','opContent':['130',0]},{'struct':'Map','type':'Map','id':['130',0],'map':{}},{'id':['130',1],'left':null,'right':null,'origin':null,'parent':['_',0],'struct':'Insert','parentSub':'Map','opContent':['130',0]},{'struct':'Map','type':'Map','id':['130',0],'map':{}},{'id':['130',1],'left':null,'right':null,'origin':null,'parent':['_',0],'struct':'Insert','parentSub':'Map','opContent':['130',0]},{'left':null,'right':null,'origin':null,'parent':['130',0],'parentSub':'somekey','struct':'Insert','content':512,'id':['133',0]},{'id':['130',2],'left':null,'right':null,'origin':null,'parent':['130',0],'struct':'Insert','parentSub':'somekey','content':1131},{'id':['130',3],'left':null,'right':['130',2],'origin':null,'parent':['130',0],'struct':'Insert','parentSub':'somekey','content':4196},{'id':['131',3],'left':null,'right':null,'origin':null,'parent':['130',0],'struct':'Insert','parentSub':'somekey','content':5022}]//eslint-disable-line
+
+        for (var o of ops) {
+          yield* this.store.tryExecute.call(this, o)
+        }
+      })
+      yield wait()
+      yield yconfig3.disconnect()
+      yield yconfig2.disconnect()
+      yield flushAll()
+      wait()
+      yield yconfig3.reconnect()
+      yield yconfig2.reconnect()
+      yield wait()
+      yield flushAll()
+      done()
+    }))
+    it('debug right not existend in Insert.execute (2)', async(function * (done) {
+      yconfig1.db.requestTransaction(function * () {
+        yield* this.store.tryExecute.call(this, {'struct': 'Map', 'type': 'Map', 'id': ['153', 0], 'map': {}})
+        yield* this.store.tryExecute.call(this, {'id': ['153', 1], 'left': null, 'right': null, 'origin': null, 'parent': ['_', 0], 'struct': 'Insert', 'parentSub': 'Map', 'opContent': ['153', 0]})
+        yield* this.store.tryExecute.call(this, {'struct': 'Map', 'type': 'Map', 'id': ['153', 0], 'map': {}})
+        yield* this.store.tryExecute.call(this, {'id': ['153', 1], 'left': null, 'right': null, 'origin': null, 'parent': ['_', 0], 'struct': 'Insert', 'parentSub': 'Map', 'opContent': ['153', 0]})
+        yield* this.store.tryExecute.call(this, {'struct': 'Map', 'type': 'Map', 'id': ['153', 0], 'map': {}})
+        yield* this.store.tryExecute.call(this, {'id': ['153', 1], 'left': null, 'right': null, 'origin': null, 'parent': ['_', 0], 'struct': 'Insert', 'parentSub': 'Map', 'opContent': ['153', 0]})
+        yield* this.store.tryExecute.call(this, {'left': null, 'right': null, 'origin': null, 'parent': ['153', 0], 'parentSub': 'somekey', 'struct': 'Insert', 'content': 3784, 'id': ['154', 0]})
+        yield* this.store.tryExecute.call(this, {'left': null, 'right': ['154', 0], 'origin': null, 'parent': ['153', 0], 'parentSub': 'somekey', 'struct': 'Insert', 'content': 8217, 'id': ['154', 1]})
+        yield* this.store.tryExecute.call(this, {'left': null, 'right': ['154', 1], 'origin': null, 'parent': ['153', 0], 'parentSub': 'somekey', 'struct': 'Insert', 'content': 5036, 'id': ['154', 2]})
+        yield* this.store.tryExecute.call(this, {'id': ['153', 2], 'left': null, 'right': null, 'origin': null, 'parent': ['153', 0], 'struct': 'Insert', 'parentSub': 'somekey', 'content': 417})
+        yield* this.store.tryExecute.call(this, {'id': ['155', 0], 'left': null, 'right': null, 'origin': null, 'parent': ['153', 0], 'struct': 'Insert', 'parentSub': 'somekey', 'content': 2202})
+        yield* this.garbageCollectOperation(['153', 2])
+        yield* this.garbageCollectOperation(['154', 0])
+        yield* this.garbageCollectOperation(['154', 1])
+        yield* this.garbageCollectOperation(['154', 2])
+        yield* this.garbageCollectOperation(['155', 0])
+        yield* this.garbageCollectOperation(['156', 0])
+        yield* this.garbageCollectOperation(['157', 0])
+        yield* this.garbageCollectOperation(['157', 1])
+        yield* this.store.tryExecute.call(this, {'id': ['153', 3], 'left': null, 'right': null, 'origin': null, 'parent': ['153', 0], 'struct': 'Insert', 'parentSub': 'somekey', 'content': 4372})
+      })
+      yield wait()
+      yield yconfig3.disconnect()
+      yield yconfig2.disconnect()
+      yield flushAll()
+      wait()
+      yield yconfig3.reconnect()
+      yield yconfig2.reconnect()
+      yield wait()
+      yield flushAll()
+      done()
+    }))
   })
   describeManyTimes(repeatArrayTests, `Random tests`, function () {
     var randomArrayTransactions = [
diff --git a/src/Types/Map.spec.js b/src/Types/Map.spec.js
index 245292d6..93abf875 100644
--- a/src/Types/Map.spec.js
+++ b/src/Types/Map.spec.js
@@ -2,7 +2,7 @@
 /* eslint-env browser,jasmine */
 
 var numberOfYMapTests = 40
-var repeatMapTeasts = 1
+var repeatMapTeasts = 2
 
 describe('Map Type', function () {
   var y1, y2, y3, y4, flushAll
-- 
GitLab