325 lines
10 KiB
CoffeeScript
325 lines
10 KiB
CoffeeScript
chai = require('chai')
|
||
expect = chai.expect
|
||
should = chai.should()
|
||
sinon = require('sinon')
|
||
sinonChai = require('sinon-chai')
|
||
_ = require("underscore")
|
||
|
||
chai.use(sinonChai)
|
||
|
||
Y = require "../lib/y.coffee"
|
||
Y.Test = require "../../y-test/lib/y-test.coffee"
|
||
Y.Text = require "../../y-text/lib/y-text"
|
||
Y.List = require "../../y-list/lib/y-list"
|
||
|
||
TestSuite = require "./TestSuite"
|
||
class ObjectTest extends TestSuite
|
||
|
||
constructor: (suffix)->
|
||
super suffix, Y
|
||
|
||
makeNewUser: (userId)->
|
||
conn = new Y.Test userId
|
||
super new Y conn
|
||
|
||
type: "ObjectTest"
|
||
|
||
getRandomRoot: (user_num, root, depth = @max_depth)->
|
||
root ?= @users[user_num]
|
||
if depth is 0 or _.random(0,1) is 1 # take root
|
||
root
|
||
else # take child
|
||
depth--
|
||
elems = null
|
||
if root._name is "Object"
|
||
elems =
|
||
for oname,val of root.val()
|
||
val
|
||
else if root._name is "Array"
|
||
elems = root.val()
|
||
else
|
||
return root
|
||
|
||
elems = elems.filter (elem)->
|
||
elem? and ((elem._name is "Array") or (elem._name is "Object"))
|
||
if elems.length is 0
|
||
root
|
||
else
|
||
p = elems[_.random(0, elems.length-1)]
|
||
@getRandomRoot user_num, p, depth
|
||
|
||
getGeneratingFunctions: (user_num)->
|
||
super(user_num).concat [
|
||
f : (y)=> # Delete Object Property
|
||
list = for name, o of y.val()
|
||
name
|
||
if list.length > 0
|
||
key = list[_.random(0,list.length-1)]
|
||
y.delete(key)
|
||
types: [Y.Object]
|
||
,
|
||
f : (y)=> # SET Object Property
|
||
y.val(@getRandomKey(), new Y.Object(@getRandomObject()))
|
||
types: [Y.Object]
|
||
,
|
||
f : (y)=> # SET PROPERTY TEXT
|
||
y.val(@getRandomKey(), new Y.Text(@getRandomText()))
|
||
types: [Y.Object]
|
||
,
|
||
f : (y)=> # SET PROPERTY List
|
||
y.val(@getRandomKey(), new Y.List(@getRandomText().split("")))
|
||
types: [Y.Object]
|
||
,
|
||
f : (y)=> # Delete Array Element
|
||
list = y.val()
|
||
if list.length > 0
|
||
i = _.random(0,list.length-1)
|
||
y.delete(i)
|
||
types: [Y.List]
|
||
,
|
||
f : (y)=> # insert Object mutable
|
||
l = y.val().length
|
||
y.insert(_.random(0, l-1), new Y.Object(@getRandomObject()))
|
||
types: [Y.List]
|
||
,
|
||
f : (y)=> # insert Text mutable
|
||
l = y.val().length
|
||
y.insert(_.random(0, l-1), new Y.Text(@getRandomText()))
|
||
types : [Y.List]
|
||
,
|
||
f : (y)=> # insert List mutable
|
||
l = y.val().length
|
||
y.insert(_.random(0, l-1), new Y.List(@getRandomText().split("")))
|
||
types : [Y.List]
|
||
,
|
||
f : (y)=> # insert Number (primitive object)
|
||
l = y.val().length
|
||
y.insert(_.random(0,l-1), _.random(0,42))
|
||
types : [Y.List]
|
||
,
|
||
f : (y)=> # SET Object Property (circular)
|
||
y.val(@getRandomKey(), @getRandomRoot user_num)
|
||
types: [Y.Object]
|
||
,
|
||
f : (y)=> # insert Object mutable (circular)
|
||
l = y.val().length
|
||
y.insert(_.random(0, l-1), @getRandomRoot user_num)
|
||
types: [Y.List]
|
||
,
|
||
f : (y)=> # INSERT TEXT
|
||
y
|
||
pos = _.random 0, (y.val().length-1)
|
||
y.insert pos, @getRandomText()
|
||
null
|
||
types: [Y.Text]
|
||
,
|
||
f : (y)-> # DELETE TEXT
|
||
if y.val().length > 0
|
||
pos = _.random 0, (y.val().length-1) # TODO: put here also arbitrary number (test behaviour in error cases)
|
||
length = _.random 0, (y.val().length - pos)
|
||
ops1 = y.delete pos, length
|
||
undefined
|
||
types : [Y.Text]
|
||
]
|
||
|
||
module.exports = ObjectTest
|
||
|
||
describe "Object Test", ->
|
||
@timeout 500000
|
||
|
||
beforeEach (done)->
|
||
@yTest = new ObjectTest()
|
||
@users = @yTest.users
|
||
|
||
@test_user = @yTest.makeNewUser "test_user"
|
||
done()
|
||
|
||
it "can handle many engines, many operations, concurrently (random)", ->
|
||
console.log "" # TODO
|
||
@yTest.run()
|
||
|
||
it "has a working test suite", ->
|
||
@yTest.compareAll()
|
||
|
||
it "handles double-late-join", ->
|
||
test = new ObjectTest("double")
|
||
test.run()
|
||
@yTest.run()
|
||
u1 = test.users[0]
|
||
u2 = @yTest.users[1]
|
||
ops1 = u1._model.HB._encode()
|
||
ops2 = u2._model.HB._encode()
|
||
u1._model.engine.applyOp ops2, true
|
||
u2._model.engine.applyOp ops1, true
|
||
|
||
expect(@yTest.compare(u1, u2)).to.not.be.undefined
|
||
|
||
it "can handle creaton of complex json (1)", ->
|
||
@yTest.users[0].val('a', new Y.Text('q'))
|
||
@yTest.users[1].val('a', new Y.Text('t'))
|
||
@yTest.compareAll()
|
||
q = @yTest.users[2].val('a')
|
||
q.insert(0,'A')
|
||
@yTest.compareAll()
|
||
expect(@yTest.getSomeUser().val("a").val()).to.equal("At")
|
||
|
||
it "can handle creaton of complex json (2)", ->
|
||
@yTest.getSomeUser().val('x', new Y.Object({'a':'b'}))
|
||
@yTest.getSomeUser().val('a', new Y.Object({'a':{q: new Y.Text("dtrndtrtdrntdrnrtdnrtdnrtdnrtdnrdnrdt")}}))
|
||
@yTest.getSomeUser().val('b', new Y.Object({'a':{}}))
|
||
@yTest.getSomeUser().val('c', new Y.Object({'a':'c'}))
|
||
@yTest.getSomeUser().val('c', new Y.Object({'a':'b'}))
|
||
@yTest.compareAll()
|
||
q = @yTest.getSomeUser().val("a").val("a").val("q")
|
||
q.insert(0,'A')
|
||
@yTest.compareAll()
|
||
expect(@yTest.getSomeUser().val("a").val("a").val("q").val()).to.equal("Adtrndtrtdrntdrnrtdnrtdnrtdnrtdnrdnrdt")
|
||
|
||
it "can handle creaton of complex json (3)", ->
|
||
@yTest.users[0].val('l', new Y.List([1,2,3]))
|
||
@yTest.users[1].val('l', new Y.List([4,5,6]))
|
||
@yTest.compareAll()
|
||
@yTest.users[2].val('l').insert(0,'A')
|
||
w = @yTest.users[1].val('l').insert(0,new Y.Text('B')).val(0)
|
||
w.insert 1, "C"
|
||
expect(w.val()).to.equal("BC")
|
||
@yTest.compareAll()
|
||
|
||
it "handles immutables and primitive data types", ->
|
||
@yTest.getSomeUser().val('string', "text")
|
||
@yTest.getSomeUser().val('number', 4)
|
||
@yTest.getSomeUser().val('object', new Y.Object({q:"rr"}))
|
||
@yTest.getSomeUser().val('null', null)
|
||
@yTest.compareAll()
|
||
expect(@yTest.getSomeUser().val('string')).to.equal "text"
|
||
expect(@yTest.getSomeUser().val('number')).to.equal 4
|
||
expect(@yTest.getSomeUser().val('object').val('q')).to.equal "rr"
|
||
expect(@yTest.getSomeUser().val('null') is null).to.be.ok
|
||
|
||
it "handles immutables and primitive data types (2)", ->
|
||
@yTest.users[0].val('string', "text")
|
||
@yTest.users[1].val('number', 4)
|
||
@yTest.users[2].val('object', new Y.Object({q:"rr"}))
|
||
@yTest.users[0].val('null', null)
|
||
@yTest.compareAll()
|
||
expect(@yTest.getSomeUser().val('string')).to.equal "text"
|
||
expect(@yTest.getSomeUser().val('number')).to.equal 4
|
||
expect(@yTest.getSomeUser().val('object').val('q')).to.equal "rr"
|
||
expect(@yTest.getSomeUser().val('null') is null).to.be.ok
|
||
|
||
it "Observers work on JSON Types (add type observers, local and foreign)", ->
|
||
u = @yTest.users[0]
|
||
@yTest.flushAll()
|
||
last_task = null
|
||
observer1 = (changes)->
|
||
expect(changes.length).to.equal(1)
|
||
change = changes[0]
|
||
expect(change.type).to.equal("add")
|
||
expect(change.object).to.equal(u)
|
||
expect(change.changedBy).to.equal('0')
|
||
expect(change.name).to.equal("newStuff")
|
||
last_task = "observer1"
|
||
u.observe observer1
|
||
u.val("newStuff",new Y.Text("someStuff"))
|
||
expect(last_task).to.equal("observer1")
|
||
u.unobserve observer1
|
||
|
||
observer2 = (changes)->
|
||
expect(changes.length).to.equal(1)
|
||
change = changes[0]
|
||
expect(change.type).to.equal("add")
|
||
expect(change.object).to.equal(u)
|
||
expect(change.changedBy).to.equal('1')
|
||
expect(change.name).to.equal("moreStuff")
|
||
last_task = "observer2"
|
||
u.observe observer2
|
||
v = @yTest.users[1]
|
||
v.val("moreStuff","someMoreStuff")
|
||
@yTest.flushAll()
|
||
expect(last_task).to.equal("observer2")
|
||
u.unobserve observer2
|
||
|
||
it "Observers work on JSON Types (update type observers, local and foreign)", ->
|
||
u = @yTest.users[0].val("newStuff", new Y.Text("oldStuff")).val("moreStuff",new Y.Text("moreOldStuff"))
|
||
@yTest.flushAll()
|
||
last_task = null
|
||
observer1 = (changes)->
|
||
expect(changes.length).to.equal(1)
|
||
change = changes[0]
|
||
expect(change.type).to.equal("update")
|
||
expect(change.object).to.equal(u)
|
||
expect(change.changedBy).to.equal('0')
|
||
expect(change.name).to.equal("newStuff")
|
||
expect(change.oldValue.val()).to.equal("oldStuff")
|
||
last_task = "observer1"
|
||
u.observe observer1
|
||
u.val("newStuff","someStuff")
|
||
expect(last_task).to.equal("observer1")
|
||
u.unobserve observer1
|
||
|
||
observer2 = (changes)->
|
||
expect(changes.length).to.equal(1)
|
||
change = changes[0]
|
||
expect(change.type).to.equal("update")
|
||
expect(change.object).to.equal(u)
|
||
expect(change.changedBy).to.equal('1')
|
||
expect(change.name).to.equal("moreStuff")
|
||
expect(change.oldValue.val()).to.equal("moreOldStuff")
|
||
last_task = "observer2"
|
||
u.observe observer2
|
||
v = @yTest.users[1]
|
||
v.val("moreStuff","someMoreStuff")
|
||
@yTest.flushAll()
|
||
expect(last_task).to.equal("observer2")
|
||
u.unobserve observer2
|
||
|
||
|
||
it "Observers work on JSON Types (delete type observers, local and foreign)", ->
|
||
u = @yTest.users[0].val("newStuff",new Y.Text("oldStuff")).val("moreStuff",new Y.Text("moreOldStuff"))
|
||
@yTest.flushAll()
|
||
last_task = null
|
||
observer1 = (changes)->
|
||
expect(changes.length).to.equal(1)
|
||
change = changes[0]
|
||
expect(change.type).to.equal("delete")
|
||
expect(change.object).to.equal(u)
|
||
expect(change.changedBy).to.equal('0')
|
||
expect(change.name).to.equal("newStuff")
|
||
expect(change.oldValue.val()).to.equal("oldStuff")
|
||
last_task = "observer1"
|
||
u.observe observer1
|
||
u.delete("newStuff")
|
||
expect(last_task).to.equal("observer1")
|
||
u.unobserve observer1
|
||
|
||
observer2 = (changes)->
|
||
expect(changes.length).to.equal(1)
|
||
change = changes[0]
|
||
expect(change.type).to.equal("delete")
|
||
expect(change.object).to.equal(u)
|
||
expect(change.changedBy).to.equal('1')
|
||
expect(change.name).to.equal("moreStuff")
|
||
expect(change.oldValue.val()).to.equal("moreOldStuff")
|
||
last_task = "observer2"
|
||
u.observe observer2
|
||
v = @yTest.users[1]
|
||
v.delete("moreStuff")
|
||
@yTest.flushAll()
|
||
expect(last_task).to.equal("observer2")
|
||
u.unobserve observer2
|
||
|
||
it "can handle circular JSON", ->
|
||
u = @yTest.users[0]
|
||
u.val("me", u)
|
||
@yTest.compareAll()
|
||
u.val("stuff", new Y.Object({x: true}))
|
||
u.val("same_stuff", u.val("stuff"))
|
||
u.val("same_stuff").val("x", 5)
|
||
expect(u.val("same_stuff").val("x")).to.equal(5)
|
||
@yTest.compareAll()
|
||
u.val("stuff").val("y", u.val("stuff"))
|
||
@yTest.compareAll()
|
||
|
||
|
||
|