fixing double late join test fail

This commit is contained in:
DadaMonad 2015-02-17 19:23:27 +00:00
parent f609c22be8
commit 77b83cae2a
23 changed files with 225 additions and 166 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -10,7 +10,7 @@ adaptConnector = function(connector, engine, HB, execution_listener) {
} }
connector.setIsBoundToY(); connector.setIsBoundToY();
send_ = function(o) { send_ = function(o) {
if ((o.uid.creator === HB.getUserId()) && (typeof o.uid.op_number !== "string") && (o.uid.doSync === "true" || o.uid.doSync === true) && (HB.getUserId() !== "_temp")) { if ((o.uid.creator === HB.getUserId()) && (typeof o.uid.op_number !== "string") && (HB.getUserId() !== "_temp")) {
return connector.broadcast(o); return connector.broadcast(o);
} }
}; };

View File

@ -105,13 +105,13 @@ module.exports = {
/* /*
* Broadcast a message to all connected peers. * Broadcast a message to all connected peers.
* @param message {Object} The message to broadcast. * @param message {Object} The message to broadcast.
* #
broadcast: (message)-> broadcast: (message)->
throw new Error "You must implement broadcast!" throw new Error "You must implement broadcast!"
* #
* Send a message to a peer, or set of peers * Send a message to a peer, or set of peers
* #
send: (peer_s, message)-> send: (peer_s, message)->
throw new Error "You must implement send!" throw new Error "You must implement send!"
*/ */

View File

@ -13,9 +13,9 @@ if (typeof window !== "undefined" && window !== null) {
} }
Engine = (function() { Engine = (function() {
function Engine(HB, types) { function Engine(_at_HB, _at_types) {
this.HB = HB; this.HB = _at_HB;
this.types = types; this.types = _at_types;
this.unprocessed_ops = []; this.unprocessed_ops = [];
} }
@ -73,6 +73,7 @@ Engine = (function() {
op_json.fromHB = "true"; op_json.fromHB = "true";
} }
o = this.parseOperation(op_json); o = this.parseOperation(op_json);
o.parsed_from_json = op_json;
if (op_json.fromHB != null) { if (op_json.fromHB != null) {
o.fromHB = op_json.fromHB; o.fromHB = op_json.fromHB;
} }

View File

@ -2,8 +2,8 @@ var HistoryBuffer,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
HistoryBuffer = (function() { HistoryBuffer = (function() {
function HistoryBuffer(user_id) { function HistoryBuffer(_at_user_id) {
this.user_id = user_id; this.user_id = _at_user_id;
this.emptyGarbage = __bind(this.emptyGarbage, this); this.emptyGarbage = __bind(this.emptyGarbage, this);
this.operation_counter = {}; this.operation_counter = {};
this.buffer = {}; this.buffer = {};
@ -92,15 +92,14 @@ HistoryBuffer = (function() {
return this.garbageCollectTimeoutId = void 0; return this.garbageCollectTimeoutId = void 0;
}; };
HistoryBuffer.prototype.setGarbageCollectTimeout = function(garbageCollectTimeout) { HistoryBuffer.prototype.setGarbageCollectTimeout = function(_at_garbageCollectTimeout) {
this.garbageCollectTimeout = garbageCollectTimeout; this.garbageCollectTimeout = _at_garbageCollectTimeout;
}; };
HistoryBuffer.prototype.getReservedUniqueIdentifier = function() { HistoryBuffer.prototype.getReservedUniqueIdentifier = function() {
return { return {
creator: '_', creator: '_',
op_number: "_" + (this.reserved_identifier_counter++), op_number: "_" + (this.reserved_identifier_counter++)
doSync: false
}; };
}; };
@ -143,9 +142,12 @@ HistoryBuffer = (function() {
_ref = this.buffer; _ref = this.buffer;
for (u_name in _ref) { for (u_name in _ref) {
user = _ref[u_name]; user = _ref[u_name];
if (u_name === "_") {
continue;
}
for (o_number in user) { for (o_number in user) {
o = user[o_number]; o = user[o_number];
if ((o.uid.noOperation == null) && o.uid.doSync && unknown(u_name, o_number)) { if ((o.uid.noOperation == null) && unknown(u_name, o_number)) {
o_json = o._encode(); o_json = o._encode();
if (o.next_cl != null) { if (o.next_cl != null) {
o_next = o.next_cl; o_next = o.next_cl;
@ -177,8 +179,7 @@ HistoryBuffer = (function() {
} }
uid = { uid = {
'creator': user_id, 'creator': user_id,
'op_number': this.operation_counter[user_id], 'op_number': this.operation_counter[user_id]
'doSync': true
}; };
this.operation_counter[user_id]++; this.operation_counter[user_id]++;
return uid; return uid;
@ -238,15 +239,18 @@ HistoryBuffer = (function() {
}; };
HistoryBuffer.prototype.addToCounter = function(o) { HistoryBuffer.prototype.addToCounter = function(o) {
if (this.operation_counter[o.uid.creator] == null) { var _base, _name;
this.operation_counter[o.uid.creator] = 0; if ((_base = this.operation_counter)[_name = o.uid.creator] == null) {
_base[_name] = 0;
} }
if (typeof o.uid.op_number === 'number' && o.uid.creator !== this.getUserId()) { if (o.uid.creator !== this.getUserId()) {
if (o.uid.op_number === this.operation_counter[o.uid.creator]) { if (o.uid.op_number === this.operation_counter[o.uid.creator]) {
return this.operation_counter[o.uid.creator]++; this.operation_counter[o.uid.creator]++;
} else {
return this.invokeSync(o.uid.creator);
} }
while (this.buffer[o.uid.creator][this.operation_counter[o.uid.creator]] != null) {
this.operation_counter[o.uid.creator]++;
}
return void 0;
} }
}; };

View File

@ -1,6 +1,6 @@
var __slice = [].slice, var __slice = [].slice,
__hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; __hasProp = {}.hasOwnProperty;
module.exports = function(HB) { module.exports = function(HB) {
var execution_listener, types; var execution_listener, types;
@ -79,8 +79,8 @@ module.exports = function(HB) {
return this.deleteAllObservers(); return this.deleteAllObservers();
}; };
Operation.prototype.setParent = function(parent) { Operation.prototype.setParent = function(_at_parent) {
this.parent = parent; this.parent = _at_parent;
}; };
Operation.prototype.getParent = function() { Operation.prototype.getParent = function() {
@ -95,7 +95,6 @@ module.exports = function(HB) {
if (this.uid.alt != null) { if (this.uid.alt != null) {
map_uid = this.uid.alt.cloneUid(); map_uid = this.uid.alt.cloneUid();
map_uid.sub = this.uid.sub; map_uid.sub = this.uid.sub;
map_uid.doSync = false;
return map_uid; return map_uid;
} else { } else {
return void 0; return void 0;
@ -114,10 +113,6 @@ module.exports = function(HB) {
return uid; return uid;
}; };
Operation.prototype.dontSync = function() {
return this.uid.doSync = false;
};
Operation.prototype.execute = function() { Operation.prototype.execute = function() {
var l, _i, _len; var l, _i, _len;
this.is_executed = true; this.is_executed = true;
@ -135,7 +130,7 @@ module.exports = function(HB) {
}; };
Operation.prototype.saveOperation = function(name, op) { Operation.prototype.saveOperation = function(name, op) {
if ((op != null ? op.execute : void 0) != null) { if (((op != null ? op.execute : void 0) != null) || typeof op === "string") {
return this[name] = op; return this[name] = op;
} else if (op != null) { } else if (op != null) {
if (this.unchecked == null) { if (this.unchecked == null) {
@ -296,6 +291,8 @@ module.exports = function(HB) {
this.prev_cl = this.parent.beginning; this.prev_cl = this.parent.beginning;
} }
if (this.origin == null) { if (this.origin == null) {
this.origin = this.prev_cl;
} else if (this.origin === "Delimiter") {
this.origin = this.parent.beginning; this.origin = this.parent.beginning;
} }
if (this.next_cl == null) { if (this.next_cl == null) {
@ -389,8 +386,8 @@ module.exports = function(HB) {
types.ImmutableObject = (function(_super) { types.ImmutableObject = (function(_super) {
__extends(ImmutableObject, _super); __extends(ImmutableObject, _super);
function ImmutableObject(uid, content) { function ImmutableObject(uid, _at_content) {
this.content = content; this.content = _at_content;
ImmutableObject.__super__.constructor.call(this, uid); ImmutableObject.__super__.constructor.call(this, uid);
} }

View File

@ -1,6 +1,6 @@
var text_types_uninitialized, var text_types_uninitialized,
__hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; __hasProp = {}.hasOwnProperty;
text_types_uninitialized = require("./TextTypes"); text_types_uninitialized = require("./TextTypes");

View File

@ -1,6 +1,6 @@
var basic_types_uninitialized, var basic_types_uninitialized,
__hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; __hasProp = {}.hasOwnProperty;
basic_types_uninitialized = require("./BasicTypes"); basic_types_uninitialized = require("./BasicTypes");
@ -159,9 +159,9 @@ module.exports = function(HB) {
types.ReplaceManager = (function(_super) { types.ReplaceManager = (function(_super) {
__extends(ReplaceManager, _super); __extends(ReplaceManager, _super);
function ReplaceManager(event_properties, event_this, uid, beginning, end) { function ReplaceManager(_at_event_properties, _at_event_this, uid, beginning, end) {
this.event_properties = event_properties; this.event_properties = _at_event_properties;
this.event_this = event_this; this.event_this = _at_event_this;
if (this.event_properties['object'] == null) { if (this.event_properties['object'] == null) {
this.event_properties['object'] = this.event_this; this.event_properties['object'] = this.event_this;
} }
@ -326,10 +326,14 @@ module.exports = function(HB) {
'parent': this.parent.getUid(), 'parent': this.parent.getUid(),
'prev': this.prev_cl.getUid(), 'prev': this.prev_cl.getUid(),
'next': this.next_cl.getUid(), 'next': this.next_cl.getUid(),
'origin': this.origin.getUid(),
'uid': this.getUid(), 'uid': this.getUid(),
'is_deleted': this.is_deleted 'is_deleted': this.is_deleted
}; };
if (this.origin.type === "Delimiter") {
json.origin = "Delimiter";
} else if (this.origin !== this.prev_cl) {
json.origin = this.origin.getUid();
}
if (this.content instanceof types.Operation) { if (this.content instanceof types.Operation) {
json['content'] = this.content.getUid(); json['content'] = this.content.getUid();
} else { } else {

View File

@ -1,6 +1,6 @@
var structured_types_uninitialized, var structured_types_uninitialized,
__hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; __hasProp = {}.hasOwnProperty;
structured_types_uninitialized = require("./StructuredTypes"); structured_types_uninitialized = require("./StructuredTypes");

View File

@ -1,6 +1,6 @@
var Engine, HistoryBuffer, adaptConnector, createY, json_types_uninitialized, var Engine, HistoryBuffer, adaptConnector, createY, json_types_uninitialized,
__hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; __hasProp = {}.hasOwnProperty;
json_types_uninitialized = require("./Types/JsonTypes"); json_types_uninitialized = require("./Types/JsonTypes");

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -76,8 +76,8 @@ gulp.task 'build_node', ->
gulp.task 'build', ['build_node', 'build_browser'], -> gulp.task 'build', ['build_node', 'build_browser'], ->
gulp.task 'watch', ['build_browser'], -> gulp.task 'watch', ['build'], ->
gulp.watch files.all, ['build_browser'] gulp.watch files.all, ['build']
gulp.task 'mocha', -> gulp.task 'mocha', ->
gulp.src files.test, { read: false } gulp.src files.test, { read: false }

View File

@ -15,7 +15,6 @@ adaptConnector = (connector, engine, HB, execution_listener)->
send_ = (o)-> send_ = (o)->
if (o.uid.creator is HB.getUserId()) and if (o.uid.creator is HB.getUserId()) and
(typeof o.uid.op_number isnt "string") and # TODO: i don't think that we need this anymore.. (typeof o.uid.op_number isnt "string") and # TODO: i don't think that we need this anymore..
(o.uid.doSync is "true" or o.uid.doSync is true) and # TODO: ensure, that only true is valid
(HB.getUserId() isnt "_temp") (HB.getUserId() isnt "_temp")
connector.broadcast o connector.broadcast o

View File

@ -69,6 +69,7 @@ class Engine
op_json.fromHB = "true" # execute immediately, if op_json.fromHB = "true" # execute immediately, if
# $parse_and_execute will return false if $o_json was parsed and executed, otherwise the parsed operadion # $parse_and_execute will return false if $o_json was parsed and executed, otherwise the parsed operadion
o = @parseOperation op_json o = @parseOperation op_json
o.parsed_from_json = op_json
if op_json.fromHB? if op_json.fromHB?
o.fromHB = op_json.fromHB o.fromHB = op_json.fromHB
# @HB.addOperation o # @HB.addOperation o

View File

@ -84,7 +84,6 @@ class HistoryBuffer
{ {
creator : '_' creator : '_'
op_number : "_#{@reserved_identifier_counter++}" op_number : "_#{@reserved_identifier_counter++}"
doSync: false
} }
# #
@ -116,8 +115,10 @@ class HistoryBuffer
for u_name,user of @buffer for u_name,user of @buffer
# TODO next, if @state_vector[user] <= state_vector[user] # TODO next, if @state_vector[user] <= state_vector[user]
if u_name is "_"
continue
for o_number,o of user for o_number,o of user
if (not o.uid.noOperation?) and o.uid.doSync and unknown(u_name, o_number) if (not o.uid.noOperation?) and unknown(u_name, o_number)
# its necessary to send it, and not known in state_vector # its necessary to send it, and not known in state_vector
o_json = o._encode() o_json = o._encode()
if o.next_cl? # applies for all ops but the most right delimiter! if o.next_cl? # applies for all ops but the most right delimiter!
@ -149,7 +150,6 @@ class HistoryBuffer
uid = uid =
'creator' : user_id 'creator' : user_id
'op_number' : @operation_counter[user_id] 'op_number' : @operation_counter[user_id]
'doSync' : true
@operation_counter[user_id]++ @operation_counter[user_id]++
uid uid
@ -206,14 +206,14 @@ class HistoryBuffer
# Increment the operation_counter that defines the current state of the Engine. # Increment the operation_counter that defines the current state of the Engine.
# #
addToCounter: (o)-> addToCounter: (o)->
if not @operation_counter[o.uid.creator]? @operation_counter[o.uid.creator] ?= 0
@operation_counter[o.uid.creator] = 0 if o.uid.creator isnt @getUserId()
if typeof o.uid.op_number is 'number' and o.uid.creator isnt @getUserId()
# TODO: check if operations are send in order # TODO: check if operations are send in order
if o.uid.op_number is @operation_counter[o.uid.creator] if o.uid.op_number is @operation_counter[o.uid.creator]
@operation_counter[o.uid.creator]++ @operation_counter[o.uid.creator]++
else while @buffer[o.uid.creator][@operation_counter[o.uid.creator]]?
@invokeSync o.uid.creator @operation_counter[o.uid.creator]++
undefined
#if @operation_counter[o.uid.creator] isnt (o.uid.op_number + 1) #if @operation_counter[o.uid.creator] isnt (o.uid.op_number + 1)
#console.log (@operation_counter[o.uid.creator] - (o.uid.op_number + 1)) #console.log (@operation_counter[o.uid.creator] - (o.uid.op_number + 1))

View File

@ -114,7 +114,6 @@ module.exports = (HB)->
if @uid.alt? # could be (safely) undefined if @uid.alt? # could be (safely) undefined
map_uid = @uid.alt.cloneUid() map_uid = @uid.alt.cloneUid()
map_uid.sub = @uid.sub map_uid.sub = @uid.sub
map_uid.doSync = false
map_uid map_uid
else else
undefined undefined
@ -125,9 +124,6 @@ module.exports = (HB)->
uid[n] = v uid[n] = v
uid uid
dontSync: ()->
@uid.doSync = false
# #
# @private # @private
# If not already done, set the uid # If not already done, set the uid
@ -172,8 +168,9 @@ module.exports = (HB)->
# We use duck-typing to check if op is instantiated since there # We use duck-typing to check if op is instantiated since there
# could exist multiple classes of $Operation # could exist multiple classes of $Operation
# #
if op?.execute? if op?.execute? or typeof op is "string"
# is instantiated # is instantiated, or op is string. Currently "Delimiter" is saved as string
# (in combination with @parent you can retrieve the delimiter..)
@[name] = op @[name] = op
else if op? else if op?
# not initialized. Do it when calling $validateSavedOperations() # not initialized. Do it when calling $validateSavedOperations()
@ -350,6 +347,8 @@ module.exports = (HB)->
if not @prev_cl? if not @prev_cl?
@prev_cl = @parent.beginning @prev_cl = @parent.beginning
if not @origin? if not @origin?
@origin = @prev_cl
else if @origin is "Delimiter"
@origin = @parent.beginning @origin = @parent.beginning
if not @next_cl? if not @next_cl?
@next_cl = @parent.end @next_cl = @parent.end

View File

@ -320,10 +320,14 @@ module.exports = (HB)->
'parent' : @parent.getUid() 'parent' : @parent.getUid()
'prev': @prev_cl.getUid() 'prev': @prev_cl.getUid()
'next': @next_cl.getUid() 'next': @next_cl.getUid()
'origin' : @origin.getUid()
'uid' : @getUid() 'uid' : @getUid()
'is_deleted': @is_deleted 'is_deleted': @is_deleted
} }
if @origin.type is "Delimiter"
json.origin = "Delimiter"
else if @origin isnt @prev_cl
json.origin = @origin.getUid()
if @content instanceof types.Operation if @content instanceof types.Operation
json['content'] = @content.getUid() json['content'] = @content.getUid()
else else

View File

@ -36,7 +36,7 @@ class JsonTest extends Test
return root return root
elems = elems.filter (elem)-> elems = elems.filter (elem)->
(elem.type is "Array") or (elem.type is "Object") elem? and ((elem.type is "Array") or (elem.type is "Object"))
if elems.length is 0 if elems.length is 0
root root
else else
@ -110,10 +110,19 @@ describe "JsonFramework", ->
u2 = @yTest.users[1] u2 = @yTest.users[1]
ops1 = u1.HB._encode() ops1 = u1.HB._encode()
ops2 = u2.HB._encode() ops2 = u2.HB._encode()
u1.HB.renewStateVector u2.HB.getOperationCounter() u1.engine.applyOp ops2, true
u2.HB.renewStateVector u1.HB.getOperationCounter() u2.engine.applyOp ops1, true
u1.engine.applyOps ops2 compare = (o1, o2)->
u2.engine.applyOps ops1 if o1.type? and o1.type isnt o2.type
throw new Error "different types"
else if o1.type is "Object"
for name, val of o1.val()
compare(val, o2.val(name))
else if o1.type?
compare(o1.val(), o2.val())
else if o1 isnt o2
throw new Error "different values"
compare u1, u2
expect(test.getContent(0)).to.deep.equal(@yTest.getContent(1)) expect(test.getContent(0)).to.deep.equal(@yTest.getContent(1))
it "can handle creaton of complex json (1)", -> it "can handle creaton of complex json (1)", ->

View File

@ -13,7 +13,7 @@ module.exports = class Test
constructor: (@name_suffix = "")-> constructor: (@name_suffix = "")->
@number_of_test_cases_multiplier = 1 @number_of_test_cases_multiplier = 1
@repeat_this = 3 * @number_of_test_cases_multiplier @repeat_this = 3 * @number_of_test_cases_multiplier
@doSomething_amount = 123 * @number_of_test_cases_multiplier @doSomething_amount = 1230 * @number_of_test_cases_multiplier
@number_of_engines = 5 + @number_of_test_cases_multiplier - 1 @number_of_engines = 5 + @number_of_test_cases_multiplier - 1
@time = 0 # denotes to the time when run was started @time = 0 # denotes to the time when run was started
@ -36,7 +36,7 @@ module.exports = class Test
# is called by implementing class # is called by implementing class
makeNewUser: (user)-> makeNewUser: (user)->
user.HB.setManualGarbageCollect() user.HB.stopGarbageCollection()
user user
getSomeUser: ()-> getSomeUser: ()->

File diff suppressed because one or more lines are too long

4
y.js

File diff suppressed because one or more lines are too long