delete support for Array & Map
This commit is contained in:
		
							parent
							
								
									66a7d2720d
								
							
						
					
					
						commit
						6b153896dd
					
				@ -130,8 +130,8 @@ gulp.task("build_jasmine_browser", function(){
 | 
				
			|||||||
gulp.task("develop", ["build_jasmine_browser", "build"], function(){
 | 
					gulp.task("develop", ["build_jasmine_browser", "build"], function(){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  gulp.watch(files.test, ["build_jasmine_browser"]);
 | 
					  gulp.watch(files.test, ["build_jasmine_browser"]);
 | 
				
			||||||
  gulp.watch(files.test, ["test"]);
 | 
					  //gulp.watch(files.test, ["test"]);
 | 
				
			||||||
  gulp.watch(files.test, ["build"]);
 | 
					  //gulp.watch(files.test, ["build"]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return gulp.src("build/jasmine_browser.js")
 | 
					  return gulp.src("build/jasmine_browser.js")
 | 
				
			||||||
    .pipe(watch("build/jasmine_browser.js"))
 | 
					    .pipe(watch("build/jasmine_browser.js"))
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,6 @@ var globalRoom = {
 | 
				
			|||||||
  users: {},
 | 
					  users: {},
 | 
				
			||||||
  buffers: {},
 | 
					  buffers: {},
 | 
				
			||||||
  removeUser: function(user : AbstractConnector){
 | 
					  removeUser: function(user : AbstractConnector){
 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (var i in this.users) {
 | 
					    for (var i in this.users) {
 | 
				
			||||||
      this.users[i].userLeft(user);
 | 
					      this.users[i].userLeft(user);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -73,10 +73,9 @@ function compareAllUsers(users){//eslint-disable-line
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function createUsers(self, numberOfUsers, done) {//eslint-disable-line
 | 
					function createUsers(self, numberOfUsers, done) {//eslint-disable-line
 | 
				
			||||||
  if (self.users != null) {
 | 
					  //destroy old users
 | 
				
			||||||
    for (var y of self.users) {
 | 
					  for (var u in globalRoom.users) {//eslint-disable-line
 | 
				
			||||||
      y.destroy();
 | 
					    globalRoom.users[u].y.destroy()//eslint-disable-line
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  self.users = [];
 | 
					  self.users = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -9,7 +9,7 @@ class AbstractTransaction { //eslint-disable-line no-unused-vars
 | 
				
			|||||||
    if (t == null) {
 | 
					    if (t == null) {
 | 
				
			||||||
      var op = yield* this.getOperation(id);
 | 
					      var op = yield* this.getOperation(id);
 | 
				
			||||||
      if (op != null) {
 | 
					      if (op != null) {
 | 
				
			||||||
        t = yield* Y[op.type].create(this.store, op);
 | 
					        t = yield* Y[op.type].create.call(this, this.store, op);
 | 
				
			||||||
        this.store.initializedTypes[sid] = t;
 | 
					        this.store.initializedTypes[sid] = t;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -28,7 +28,7 @@ class AbstractTransaction { //eslint-disable-line no-unused-vars
 | 
				
			|||||||
      state.clock++;
 | 
					      state.clock++;
 | 
				
			||||||
      yield* this.setState(state);
 | 
					      yield* this.setState(state);
 | 
				
			||||||
      this.os.add(op);
 | 
					      this.os.add(op);
 | 
				
			||||||
      this.store.operationAdded(op);
 | 
					      yield* this.store.operationAdded(this, op);
 | 
				
			||||||
      return true;
 | 
					      return true;
 | 
				
			||||||
    } else if (op.id[1] < state.clock) {
 | 
					    } else if (op.id[1] < state.clock) {
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
@ -41,7 +41,7 @@ class AbstractTransaction { //eslint-disable-line no-unused-vars
 | 
				
			|||||||
    for (var i = 0; i < ops.length; i++) {
 | 
					    for (var i = 0; i < ops.length; i++) {
 | 
				
			||||||
      var op = ops[i];
 | 
					      var op = ops[i];
 | 
				
			||||||
      yield* Struct[op.struct].execute.call(this, op);
 | 
					      yield* Struct[op.struct].execute.call(this, op);
 | 
				
			||||||
      send.push(Struct[op.struct].encode(op));
 | 
					      send.push(copyObject(Struct[op.struct].encode(op)));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.store.y.connector.broadcast({
 | 
					    this.store.y.connector.broadcast({
 | 
				
			||||||
      type: "update",
 | 
					      type: "update",
 | 
				
			||||||
@ -161,7 +161,7 @@ class AbstractOperationStore { //eslint-disable-line no-unused-vars
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  // called by a transaction when an operation is added
 | 
					  // called by a transaction when an operation is added
 | 
				
			||||||
  operationAdded (op) {
 | 
					  *operationAdded (transaction, op) {
 | 
				
			||||||
    var sid = JSON.stringify(op.id);
 | 
					    var sid = JSON.stringify(op.id);
 | 
				
			||||||
    var l = this.listenersById[sid];
 | 
					    var l = this.listenersById[sid];
 | 
				
			||||||
    delete this.listenersById[sid];
 | 
					    delete this.listenersById[sid];
 | 
				
			||||||
@ -178,7 +178,7 @@ class AbstractOperationStore { //eslint-disable-line no-unused-vars
 | 
				
			|||||||
    // notify parent, if it has been initialized as a custom type
 | 
					    // notify parent, if it has been initialized as a custom type
 | 
				
			||||||
    var t = this.initializedTypes[JSON.stringify(op.parent)];
 | 
					    var t = this.initializedTypes[JSON.stringify(op.parent)];
 | 
				
			||||||
    if (t != null) {
 | 
					    if (t != null) {
 | 
				
			||||||
      t._changed(op);
 | 
					      yield* t._changed(transaction, copyObject(op));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  removeParentListener (id, f) {
 | 
					  removeParentListener (id, f) {
 | 
				
			||||||
 | 
				
			|||||||
@ -120,7 +120,7 @@ Y.Memory = (function(){ //eslint-disable-line no-unused-vars
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    requestTransaction (makeGen : Function) {
 | 
					    requestTransaction (makeGen : Function) {
 | 
				
			||||||
      var t = new Transaction(this);
 | 
					      var t = new Transaction(this);
 | 
				
			||||||
      var gen = makeGen.call(t, t.getType(["_", 0]).next().value);
 | 
					      var gen = makeGen.call(t);
 | 
				
			||||||
      var res = gen.next();
 | 
					      var res = gen.next();
 | 
				
			||||||
      while(!res.done){
 | 
					      while(!res.done){
 | 
				
			||||||
        if (res.value === "transaction") {
 | 
					        if (res.value === "transaction") {
 | 
				
			||||||
 | 
				
			|||||||
@ -35,7 +35,7 @@ function compareIds(id1, id2) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var Struct = {
 | 
					var Struct = {
 | 
				
			||||||
  /*
 | 
					  /* This Operations does _not_ have an id!
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
  target: Id
 | 
					  target: Id
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -48,12 +48,16 @@ var Struct = {
 | 
				
			|||||||
      return [op.target];
 | 
					      return [op.target];
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    execute: function* (op) {
 | 
					    execute: function* (op) {
 | 
				
			||||||
      if ((yield* this.addOperation(op)) === false) {
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      var target = yield* this.getOperation(op.target);
 | 
					      var target = yield* this.getOperation(op.target);
 | 
				
			||||||
      target.deleted = true;
 | 
					      if (!target.deleted) {
 | 
				
			||||||
      yield* this.setOperation(target);
 | 
					        target.deleted = true;
 | 
				
			||||||
 | 
					        yield* this.setOperation(target);
 | 
				
			||||||
 | 
					        var t = this.store.initializedTypes[JSON.stringify(target.parent)];
 | 
				
			||||||
 | 
					        if (t != null) {
 | 
				
			||||||
 | 
					          yield* t._changed(this, copyObject(op));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  Insert: {
 | 
					  Insert: {
 | 
				
			||||||
@ -280,14 +284,6 @@ var Struct = {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      return res;
 | 
					      return res;
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    delete: function* (op, pos) {
 | 
					 | 
				
			||||||
      var ref = yield* Struct.List.ref.call(this, op, pos);
 | 
					 | 
				
			||||||
      if (ref != null) {
 | 
					 | 
				
			||||||
        yield* Struct.Delete.create.call(this, {
 | 
					 | 
				
			||||||
          target: ref.id
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    map: function* (o : Op, f : Function) : Array<any> {
 | 
					    map: function* (o : Op, f : Function) : Array<any> {
 | 
				
			||||||
      o = o.start;
 | 
					      o = o.start;
 | 
				
			||||||
      var res = [];
 | 
					      var res = [];
 | 
				
			||||||
@ -299,31 +295,6 @@ var Struct = {
 | 
				
			|||||||
        o = operation.right;
 | 
					        o = operation.right;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return res;
 | 
					      return res;
 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    insert: function* (op, pos : number, contents : Array<any>) {
 | 
					 | 
				
			||||||
      var left, right;
 | 
					 | 
				
			||||||
      if (pos === 0) {
 | 
					 | 
				
			||||||
        left = null;
 | 
					 | 
				
			||||||
        right = op.start;
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        var ref = yield* Struct.List.ref.call(this, op, pos - 1);
 | 
					 | 
				
			||||||
        if (ref === null) {
 | 
					 | 
				
			||||||
          left = op.end;
 | 
					 | 
				
			||||||
          right = null;
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
          left = ref.id;
 | 
					 | 
				
			||||||
          right = ref.right;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      for (var key in contents) {
 | 
					 | 
				
			||||||
        var insert = {
 | 
					 | 
				
			||||||
          left: left,
 | 
					 | 
				
			||||||
          right: right,
 | 
					 | 
				
			||||||
          content: contents[key],
 | 
					 | 
				
			||||||
          parent: op.id
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        left = (yield* Struct.Insert.create.call(this, insert)).id;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  Map: {
 | 
					  Map: {
 | 
				
			||||||
 | 
				
			|||||||
@ -12,18 +12,28 @@
 | 
				
			|||||||
      this.eventHandler = new EventHandler( ops =>{
 | 
					      this.eventHandler = new EventHandler( ops =>{
 | 
				
			||||||
        for (var i in ops) {
 | 
					        for (var i in ops) {
 | 
				
			||||||
          var op = ops[i];
 | 
					          var op = ops[i];
 | 
				
			||||||
          var pos;
 | 
					          if (op.struct === "Insert") {
 | 
				
			||||||
          if (op.right === null) {
 | 
					            let pos;
 | 
				
			||||||
            pos = this.idArray.length;
 | 
					            // we check op.left only!,
 | 
				
			||||||
 | 
					            // because op.right might not be defined when this is called
 | 
				
			||||||
 | 
					            if (op.left === null) {
 | 
				
			||||||
 | 
					              pos = 0;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					              var sid = JSON.stringify(op.left);
 | 
				
			||||||
 | 
					              pos = this.idArray.indexOf(sid) + 1;
 | 
				
			||||||
 | 
					              if (pos <= 0) {
 | 
				
			||||||
 | 
					                throw new Error("Unexpected operation!");
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            this.idArray.splice(pos, 0, JSON.stringify(op.id));
 | 
				
			||||||
 | 
					            this.valArray.splice(pos, 0, op.content);
 | 
				
			||||||
 | 
					          } else if (op.struct === "Delete") {
 | 
				
			||||||
 | 
					            let pos = this.idArray.indexOf(JSON.stringify(op.target));
 | 
				
			||||||
 | 
					            this.idArray.splice(pos, 1);
 | 
				
			||||||
 | 
					            this.valArray.splice(pos, 1);
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
            var sid = JSON.stringify(op.right);
 | 
					            throw new Error("Unexpected struct!");
 | 
				
			||||||
            pos = this.idArray.indexOf(sid);
 | 
					 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          if (pos < 0) {
 | 
					 | 
				
			||||||
            throw new Error("Unexpected operation!");
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
          this.idArray.splice(pos, 0, JSON.stringify(op.id));
 | 
					 | 
				
			||||||
          this.valArray.splice(pos, 0, op.content);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -50,7 +60,6 @@
 | 
				
			|||||||
        throw new Error("This position exceeds the range of the array!");
 | 
					        throw new Error("This position exceeds the range of the array!");
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      var mostLeft = pos === 0 ? null : JSON.parse(this.idArray[pos - 1]);
 | 
					      var mostLeft = pos === 0 ? null : JSON.parse(this.idArray[pos - 1]);
 | 
				
			||||||
      var mostRight = pos === this.idArray.length ? null : JSON.parse(this.idArray[pos]);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      var ops = [];
 | 
					      var ops = [];
 | 
				
			||||||
      var prevId = mostLeft;
 | 
					      var prevId = mostLeft;
 | 
				
			||||||
@ -58,7 +67,9 @@
 | 
				
			|||||||
        var op = {
 | 
					        var op = {
 | 
				
			||||||
          left: prevId,
 | 
					          left: prevId,
 | 
				
			||||||
          origin: prevId,
 | 
					          origin: prevId,
 | 
				
			||||||
          right: mostRight,
 | 
					          // right: mostRight,
 | 
				
			||||||
 | 
					          // NOTE: I intentionally do not define right here, because it could be deleted
 | 
				
			||||||
 | 
					          // at the time of creating this operation, and is therefore not defined in idArray
 | 
				
			||||||
          parent: this._model,
 | 
					          parent: this._model,
 | 
				
			||||||
          content: contents[i],
 | 
					          content: contents[i],
 | 
				
			||||||
          struct: "Insert",
 | 
					          struct: "Insert",
 | 
				
			||||||
@ -70,19 +81,58 @@
 | 
				
			|||||||
      var eventHandler = this.eventHandler;
 | 
					      var eventHandler = this.eventHandler;
 | 
				
			||||||
      eventHandler.awaitAndPrematurelyCall(ops);
 | 
					      eventHandler.awaitAndPrematurelyCall(ops);
 | 
				
			||||||
      this.os.requestTransaction(function*(){
 | 
					      this.os.requestTransaction(function*(){
 | 
				
			||||||
 | 
					        // now we can set the right reference.
 | 
				
			||||||
 | 
					        var mostRight;
 | 
				
			||||||
 | 
					        if (mostLeft != null) {
 | 
				
			||||||
 | 
					          mostRight = (yield* this.getOperation(mostLeft)).right;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          mostRight = (yield* this.getOperation(ops[0].parent)).start;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        for (var j in ops) {
 | 
				
			||||||
 | 
					          ops[j].right = mostRight;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        yield* this.applyCreatedOperations(ops);
 | 
					        yield* this.applyCreatedOperations(ops);
 | 
				
			||||||
        eventHandler.awaitedLastOp(ops.length);
 | 
					        eventHandler.awaitedLastInserts(ops.length);
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    *delete (pos) {
 | 
					    delete (pos, length = 1) {
 | 
				
			||||||
 | 
					      if (typeof length !== "number") {
 | 
				
			||||||
 | 
					        throw new Error("pos must be a number!");
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
      if (typeof pos !== "number") {
 | 
					      if (typeof pos !== "number") {
 | 
				
			||||||
        throw new Error("pos must be a number!");
 | 
					        throw new Error("pos must be a number!");
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      var t = yield "transaction";
 | 
					      if (pos + length > this.idArray.length || pos < 0 || length < 0) {
 | 
				
			||||||
      var model = yield* t.getOperation(this._model);
 | 
					        throw new Error("The deletion range exceeds the range of the array!");
 | 
				
			||||||
      yield* Y.Struct.Array.delete.call(t, model, pos);
 | 
					      }
 | 
				
			||||||
 | 
					      var eventHandler = this.eventHandler;
 | 
				
			||||||
 | 
					      var newLeft = pos > 0 ? JSON.parse(this.idArray[pos - 1]) : null;
 | 
				
			||||||
 | 
					      var dels = [];
 | 
				
			||||||
 | 
					      for (var i = 0; i < length; i++) {
 | 
				
			||||||
 | 
					        dels.push({
 | 
				
			||||||
 | 
					          target: JSON.parse(this.idArray[pos + i]),
 | 
				
			||||||
 | 
					          struct: "Delete"
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      eventHandler.awaitAndPrematurelyCall(dels);
 | 
				
			||||||
 | 
					      this.os.requestTransaction(function*(){
 | 
				
			||||||
 | 
					        yield* this.applyCreatedOperations(dels);
 | 
				
			||||||
 | 
					        eventHandler.awaitedLastDeletes(dels.length, newLeft);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    _changed (op) {
 | 
					    *_changed (transaction, op) {
 | 
				
			||||||
 | 
					      if (op.struct === "Insert") {
 | 
				
			||||||
 | 
					        var l = op.left;
 | 
				
			||||||
 | 
					        var left;
 | 
				
			||||||
 | 
					        while (l != null) {
 | 
				
			||||||
 | 
					          left = yield* transaction.getOperation(l);
 | 
				
			||||||
 | 
					          if (!left.deleted) {
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          l = left.left;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        op.left = l;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
      this.eventHandler.receivedOp(op);
 | 
					      this.eventHandler.receivedOp(op);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,10 @@
 | 
				
			|||||||
/* @flow */
 | 
					/* @flow */
 | 
				
			||||||
/*eslint-env browser,jasmine */
 | 
					/*eslint-env browser,jasmine */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var numberOfYArrayTests = 10;
 | 
					var numberOfYArrayTests = 20;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe("Array Type", function(){
 | 
					describe("Array Type", function(){
 | 
				
			||||||
  jasmine.DEFAULT_TIMEOUT_INTERVAL = 3000;
 | 
					  jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
 | 
				
			||||||
  beforeEach(function(done){
 | 
					  beforeEach(function(done){
 | 
				
			||||||
    createUsers(this, 5, done);
 | 
					    createUsers(this, 5, done);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
@ -42,11 +42,64 @@ describe("Array Type", function(){
 | 
				
			|||||||
        done();
 | 
					        done();
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					    it("Basic insert&delete in array (handle three conflicts)", function(done){
 | 
				
			||||||
 | 
					      var y = this.users[0];
 | 
				
			||||||
 | 
					      var l1, l2, l3;
 | 
				
			||||||
 | 
					      y.root.set("Array", Y.Array).then((array)=>{
 | 
				
			||||||
 | 
					        l1 = array;
 | 
				
			||||||
 | 
					        l1.insert(0, ["x", "y", "z"]);
 | 
				
			||||||
 | 
					        y.connector.flushAll();
 | 
				
			||||||
 | 
					        l1.insert(1, [0]);
 | 
				
			||||||
 | 
					        return this.users[1].root.get("Array");
 | 
				
			||||||
 | 
					      }).then((array)=>{
 | 
				
			||||||
 | 
					        l2 = array;
 | 
				
			||||||
 | 
					        l2.delete(0);
 | 
				
			||||||
 | 
					        l2.delete(1);
 | 
				
			||||||
 | 
					        return this.users[2].root.get("Array");
 | 
				
			||||||
 | 
					      }).then((array)=>{
 | 
				
			||||||
 | 
					        l3 = array;
 | 
				
			||||||
 | 
					        l3.insert(1, [2]);
 | 
				
			||||||
 | 
					        y.connector.flushAll();
 | 
				
			||||||
 | 
					        expect(l1.toArray()).toEqual(l2.toArray());
 | 
				
			||||||
 | 
					        expect(l2.toArray()).toEqual(l3.toArray());
 | 
				
			||||||
 | 
					        expect(l2.toArray()).toEqual([0, 2, "y"]);
 | 
				
			||||||
 | 
					        compareAllUsers(this.users);
 | 
				
			||||||
 | 
					        done();
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    it("Basic insert. Then delete the whole array", function(done){
 | 
				
			||||||
 | 
					      var y = this.users[0];
 | 
				
			||||||
 | 
					      var l1, l2, l3;
 | 
				
			||||||
 | 
					      y.root.set("Array", Y.Array).then((array)=>{
 | 
				
			||||||
 | 
					        l1 = array;
 | 
				
			||||||
 | 
					        l1.insert(0, ["x", "y", "z"]);
 | 
				
			||||||
 | 
					        y.connector.flushAll();
 | 
				
			||||||
 | 
					        l1.delete(0, 3);
 | 
				
			||||||
 | 
					        return this.users[1].root.get("Array");
 | 
				
			||||||
 | 
					      }).then((array)=>{
 | 
				
			||||||
 | 
					        l2 = array;
 | 
				
			||||||
 | 
					        return this.users[2].root.get("Array");
 | 
				
			||||||
 | 
					      }).then((array)=>{
 | 
				
			||||||
 | 
					        l3 = array;
 | 
				
			||||||
 | 
					        y.connector.flushAll();
 | 
				
			||||||
 | 
					        expect(l1.toArray()).toEqual(l2.toArray());
 | 
				
			||||||
 | 
					        expect(l2.toArray()).toEqual(l3.toArray());
 | 
				
			||||||
 | 
					        expect(l2.toArray()).toEqual([]);
 | 
				
			||||||
 | 
					        compareAllUsers(this.users);
 | 
				
			||||||
 | 
					        done();
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
  describe("Random tests", function(){
 | 
					  describe(`${numberOfYArrayTests} Random tests`, function(){
 | 
				
			||||||
    var randomMapTransactions = [
 | 
					    var randomArrayTransactions = [
 | 
				
			||||||
      function insert (array) {
 | 
					      function insert (array) {
 | 
				
			||||||
        array.insert(getRandomNumber(array.toArray().length), [getRandomNumber()]);
 | 
					        array.insert(getRandomNumber(array.toArray().length), [getRandomNumber()]);
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      function _delete (array) {
 | 
				
			||||||
 | 
					        var length = array.toArray().length;
 | 
				
			||||||
 | 
					        if (length > 0) {
 | 
				
			||||||
 | 
					          array.delete(getRandomNumber(length - 1));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
    function compareArrayValues(arrays){
 | 
					    function compareArrayValues(arrays){
 | 
				
			||||||
@ -82,7 +135,7 @@ describe("Array Type", function(){
 | 
				
			|||||||
      expect(this.arrays.length).toEqual(this.users.length);
 | 
					      expect(this.arrays.length).toEqual(this.users.length);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    it(`succeed after ${numberOfYArrayTests} actions`, function(done){
 | 
					    it(`succeed after ${numberOfYArrayTests} actions`, function(done){
 | 
				
			||||||
      applyRandomTransactions(this.users, this.arrays, randomMapTransactions, numberOfYArrayTests);
 | 
					      applyRandomTransactions(this.users, this.arrays, randomArrayTransactions, numberOfYArrayTests);
 | 
				
			||||||
      setTimeout(()=>{
 | 
					      setTimeout(()=>{
 | 
				
			||||||
        compareAllUsers(this.users);
 | 
					        compareAllUsers(this.users);
 | 
				
			||||||
        compareArrayValues(this.arrays);
 | 
					        compareArrayValues(this.arrays);
 | 
				
			||||||
 | 
				
			|||||||
@ -18,12 +18,12 @@ class EventHandler {
 | 
				
			|||||||
    this.awaiting++;
 | 
					    this.awaiting++;
 | 
				
			||||||
    this.onevent(ops);
 | 
					    this.onevent(ops);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  awaitedLastOp (n) {
 | 
					  awaitedLastInserts (n) {
 | 
				
			||||||
    var ops = this.waiting.splice(this.waiting.length - n);
 | 
					    var ops = this.waiting.splice(this.waiting.length - n);
 | 
				
			||||||
    for (var oid = 0; oid < ops.length; oid++) {
 | 
					    for (var oid = 0; oid < ops.length; oid++) {
 | 
				
			||||||
      var op = ops[oid];
 | 
					      var op = ops[oid];
 | 
				
			||||||
      for (var i = this.waiting.length - 1; i >= 0; i--) {
 | 
					      for (var i = this.waiting.length - 1; i >= 0; i--) {
 | 
				
			||||||
        var w = this.waiting[i];
 | 
					        let w = this.waiting[i];
 | 
				
			||||||
        if (compareIds(op.left, w.id)) {
 | 
					        if (compareIds(op.left, w.id)) {
 | 
				
			||||||
          // include the effect of op in w
 | 
					          // include the effect of op in w
 | 
				
			||||||
          w.right = op.id;
 | 
					          w.right = op.id;
 | 
				
			||||||
@ -36,7 +36,25 @@ class EventHandler {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    this.tryCallEvents();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  awaitedLastDeletes (n, newLeft) {
 | 
				
			||||||
 | 
					    var ops = this.waiting.splice(this.waiting.length - n);
 | 
				
			||||||
 | 
					    for (var j in ops) {
 | 
				
			||||||
 | 
					      var del = ops[j];
 | 
				
			||||||
 | 
					      if (newLeft != null) {
 | 
				
			||||||
 | 
					        for (var i in this.waiting) {
 | 
				
			||||||
 | 
					          let w = this.waiting[i];
 | 
				
			||||||
 | 
					          // We will just care about w.left
 | 
				
			||||||
 | 
					          if (compareIds(del.target, w.left)) {
 | 
				
			||||||
 | 
					            del.left = newLeft;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this.tryCallEvents();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  tryCallEvents () {
 | 
				
			||||||
    this.awaiting--;
 | 
					    this.awaiting--;
 | 
				
			||||||
    if (this.awaiting <= 0) {
 | 
					    if (this.awaiting <= 0) {
 | 
				
			||||||
      var events = this.waiting;
 | 
					      var events = this.waiting;
 | 
				
			||||||
@ -44,25 +62,39 @@ class EventHandler {
 | 
				
			|||||||
      this.onevent(events);
 | 
					      this.onevent(events);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
(function(){
 | 
					(function(){
 | 
				
			||||||
  class Map {
 | 
					  class Map {
 | 
				
			||||||
    constructor (os, model) {
 | 
					    constructor (os, model) {
 | 
				
			||||||
      this._model = model.id;
 | 
					      this._model = model.id;
 | 
				
			||||||
      this.os = os;
 | 
					      this.os = os;
 | 
				
			||||||
      this.map = model.map;
 | 
					      this.map = copyObject(model.map);
 | 
				
			||||||
      this.contents = {};
 | 
					      this.contents = {};
 | 
				
			||||||
      this.opContents = {};
 | 
					      this.opContents = {};
 | 
				
			||||||
      this.eventHandler = new EventHandler( ops =>{
 | 
					      this.eventHandler = new EventHandler( ops =>{
 | 
				
			||||||
        for (var i in ops) {
 | 
					        for (var i in ops) {
 | 
				
			||||||
          var op = ops[i];
 | 
					          var op = ops[i];
 | 
				
			||||||
          if (op.left === null) {
 | 
					          if (op.struct === "Insert"){
 | 
				
			||||||
            if (op.opContent != null) {
 | 
					            if (op.left === null) {
 | 
				
			||||||
              this.opContents[op.parentSub] = op.opContent;
 | 
					              if (op.opContent != null) {
 | 
				
			||||||
            } else {
 | 
					                this.opContents[op.parentSub] = op.opContent;
 | 
				
			||||||
              this.contents[op.parentSub] = op.content;
 | 
					              } else {
 | 
				
			||||||
 | 
					                this.contents[op.parentSub] = op.content;
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              this.map[op.parentSub] = op.id;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					          } else if (op.struct === "Delete") {
 | 
				
			||||||
 | 
					            var key = op.key;
 | 
				
			||||||
 | 
					            if (compareIds(this.map[key], op.target)) {
 | 
				
			||||||
 | 
					              if (this.contents[key] != null) {
 | 
				
			||||||
 | 
					                delete this.contents[key];
 | 
				
			||||||
 | 
					              } else {
 | 
				
			||||||
 | 
					                delete this.opContents[key];
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            throw new Error("Unexpected Operation!");
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
@ -82,6 +114,23 @@ class EventHandler {
 | 
				
			|||||||
        return def.promise;
 | 
					        return def.promise;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    delete (key) {
 | 
				
			||||||
 | 
					      var right = this.map[key];
 | 
				
			||||||
 | 
					      if (right != null) {
 | 
				
			||||||
 | 
					        var del = {
 | 
				
			||||||
 | 
					          target: right,
 | 
				
			||||||
 | 
					          struct: "Delete"
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        var eventHandler = this.eventHandler;
 | 
				
			||||||
 | 
					        var modDel = copyObject(del);
 | 
				
			||||||
 | 
					        modDel.key = key;
 | 
				
			||||||
 | 
					        eventHandler.awaitAndPrematurelyCall([modDel]);
 | 
				
			||||||
 | 
					        this.os.requestTransaction(function*(){
 | 
				
			||||||
 | 
					          yield* this.applyCreatedOperations([del]);
 | 
				
			||||||
 | 
					          eventHandler.awaitedLastDeletes(1);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    set (key, value) {
 | 
					    set (key, value) {
 | 
				
			||||||
      // set property.
 | 
					      // set property.
 | 
				
			||||||
      // if property is a type, return a promise
 | 
					      // if property is a type, return a promise
 | 
				
			||||||
@ -114,7 +163,7 @@ class EventHandler {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        this.os.requestTransaction(function*(){
 | 
					        this.os.requestTransaction(function*(){
 | 
				
			||||||
          yield* this.applyCreatedOperations([insert]);
 | 
					          yield* this.applyCreatedOperations([insert]);
 | 
				
			||||||
          eventHandler.awaitedLastOp(1);
 | 
					          eventHandler.awaitedLastInserts(1);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        def.resolve(value);
 | 
					        def.resolve(value);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@ -126,7 +175,10 @@ class EventHandler {
 | 
				
			|||||||
      var model = yield* t.getOperation(this._model);
 | 
					      var model = yield* t.getOperation(this._model);
 | 
				
			||||||
      yield* Y.Struct.Map.delete.call(t, model, key);
 | 
					      yield* Y.Struct.Map.delete.call(t, model, key);
 | 
				
			||||||
    }*/
 | 
					    }*/
 | 
				
			||||||
    _changed (op) {
 | 
					    *_changed (transaction, op) {
 | 
				
			||||||
 | 
					      if (op.struct === "Delete") {
 | 
				
			||||||
 | 
					        op.key = (yield* transaction.getOperation(op.target)).parentSub;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
      this.eventHandler.receivedOp(op);
 | 
					      this.eventHandler.receivedOp(op);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
/* @flow */
 | 
					/* @flow */
 | 
				
			||||||
/*eslint-env browser,jasmine */
 | 
					/*eslint-env browser,jasmine */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var numberOfYMapTests = 5;
 | 
					var numberOfYMapTests = 70;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe("Map Type", function(){
 | 
					describe("Map Type", function(){
 | 
				
			||||||
  jasmine.DEFAULT_TIMEOUT_INTERVAL = 3000;
 | 
					  jasmine.DEFAULT_TIMEOUT_INTERVAL = 3000;
 | 
				
			||||||
@ -77,6 +77,25 @@ describe("Map Type", function(){
 | 
				
			|||||||
        done();
 | 
					        done();
 | 
				
			||||||
      }, 50);
 | 
					      }, 50);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					    it("Basic get&set&delete of Map property (handle conflict)", function(done){
 | 
				
			||||||
 | 
					      var y = this.users[0];
 | 
				
			||||||
 | 
					      y.connector.flushAll();
 | 
				
			||||||
 | 
					      y.root.set("stuff", "c0");
 | 
				
			||||||
 | 
					      y.root.delete("stuff");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      this.users[1].root.set("stuff", "c1");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      y.connector.flushAll();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      setTimeout( () => {
 | 
				
			||||||
 | 
					        for (var key in this.users) {
 | 
				
			||||||
 | 
					          var u = this.users[key];
 | 
				
			||||||
 | 
					          expect(u.root.get("stuff")).toBeUndefined();
 | 
				
			||||||
 | 
					          compareAllUsers(this.users);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        done();
 | 
				
			||||||
 | 
					      }, 50);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
    it("Basic get&set of Map property (handle three conflicts)", function(done){
 | 
					    it("Basic get&set of Map property (handle three conflicts)", function(done){
 | 
				
			||||||
      var y = this.users[0];
 | 
					      var y = this.users[0];
 | 
				
			||||||
      this.users[0].root.set("stuff", "c0");
 | 
					      this.users[0].root.set("stuff", "c0");
 | 
				
			||||||
@ -94,13 +113,36 @@ describe("Map Type", function(){
 | 
				
			|||||||
        done();
 | 
					        done();
 | 
				
			||||||
      }, 50);
 | 
					      }, 50);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					    it("Basic get&set&delete of Map property (handle three conflicts)", function(done){
 | 
				
			||||||
 | 
					      var y = this.users[0];
 | 
				
			||||||
 | 
					      this.users[0].root.set("stuff", "c0");
 | 
				
			||||||
 | 
					      this.users[1].root.set("stuff", "c1");
 | 
				
			||||||
 | 
					      this.users[2].root.set("stuff", "c2");
 | 
				
			||||||
 | 
					      this.users[3].root.set("stuff", "c3");
 | 
				
			||||||
 | 
					      y.connector.flushAll();
 | 
				
			||||||
 | 
					      this.users[0].root.set("stuff", "deleteme");
 | 
				
			||||||
 | 
					      this.users[0].root.delete("stuff");
 | 
				
			||||||
 | 
					      this.users[1].root.set("stuff", "c1");
 | 
				
			||||||
 | 
					      this.users[2].root.set("stuff", "c2");
 | 
				
			||||||
 | 
					      this.users[3].root.set("stuff", "c3");
 | 
				
			||||||
 | 
					      y.connector.flushAll();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      setTimeout( () => {
 | 
				
			||||||
 | 
					        for (var key in this.users) {
 | 
				
			||||||
 | 
					          var u = this.users[key];
 | 
				
			||||||
 | 
					          expect(u.root.get("stuff")).toBeUndefined();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        compareAllUsers(this.users);
 | 
				
			||||||
 | 
					        done();
 | 
				
			||||||
 | 
					      }, 50);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
  describe("Random tests", function(){
 | 
					  describe(`${numberOfYMapTests} Random tests`, function(){
 | 
				
			||||||
    var randomMapTransactions = [
 | 
					    var randomMapTransactions = [
 | 
				
			||||||
      function set (map) {
 | 
					      function set (map) {
 | 
				
			||||||
        map.set("somekey", getRandomNumber());
 | 
					        map.set("somekey", getRandomNumber());
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      function* delete_ (map) {
 | 
					      function delete_ (map) {
 | 
				
			||||||
        map.delete("somekey");
 | 
					        map.delete("somekey");
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user