220 lines
7.3 KiB
CoffeeScript
220 lines
7.3 KiB
CoffeeScript
chai = require('chai')
|
|
expect = chai.expect
|
|
sinon = require('sinon')
|
|
sinonChai = require('sinon-chai')
|
|
_ = require("underscore")
|
|
|
|
chai.use(sinonChai)
|
|
|
|
Connector = require "../../y-test/lib/y-test.coffee"
|
|
Y = null # need global reference!
|
|
|
|
module.exports = class Test
|
|
constructor: (@name_suffix = "", Yjs)->
|
|
Y = Yjs
|
|
@number_of_test_cases_multiplier = 1
|
|
@repeat_this = 1 * @number_of_test_cases_multiplier
|
|
@doSomething_amount = 123 * @number_of_test_cases_multiplier
|
|
@number_of_engines = 5 + @number_of_test_cases_multiplier - 1
|
|
|
|
@time = 0 # denotes to the time when run was started
|
|
@ops = 0 # number of operations (used with @time)
|
|
@time_now = 0 # current time
|
|
@max_depth = 3
|
|
|
|
@debug = false
|
|
|
|
@reinitialize()
|
|
for gf in @getGeneratingFunctions(0)
|
|
if not (gf.types? and gf.f?)
|
|
throw new Error "Generating Functions are not initialized properly!"
|
|
for t in gf.types
|
|
if not t?
|
|
throw new Error "You havent includedt this type in Y (do require 'y-whatever')"
|
|
|
|
reinitialize: ()->
|
|
@users = []
|
|
for i in [0...@number_of_engines]
|
|
u = @makeNewUser (i+@name_suffix)
|
|
for user in @users
|
|
u._model.connector.join(user._model.connector) # TODO: change the test-connector to make this more convenient
|
|
@users.push u
|
|
@initUsers?(@users[0])
|
|
@flushAll()
|
|
|
|
# is called by implementing class
|
|
makeNewUser: (user)->
|
|
user._model.HB.stopGarbageCollection()
|
|
user
|
|
|
|
getSomeUser: ()->
|
|
i = _.random 0, (@users.length-1)
|
|
@users[i]
|
|
|
|
getRandomText: (chars, min_length = 0)->
|
|
chars ?= "abcdefghijklmnopqrstuvwxyz"
|
|
length = _.random min_length, 10
|
|
#length = 1
|
|
nextchar = chars[(_.random 0, (chars.length-1))]
|
|
text = ""
|
|
_(length).times ()-> text += nextchar
|
|
text
|
|
|
|
getRandomObject: ()->
|
|
result = {}
|
|
key1 = @getRandomKey()
|
|
key2 = @getRandomKey()
|
|
val1 = @getRandomText()
|
|
val2 = null
|
|
if _.random(0,1) is 1
|
|
val2 = @getRandomObject()
|
|
else
|
|
val2 = @getRandomText()
|
|
result[key1] = val1
|
|
result[key2] = val2
|
|
result
|
|
|
|
getRandomKey: ()->
|
|
@getRandomText [1,2,'x','y'], 1 # only 4 keys
|
|
|
|
getGeneratingFunctions: (user_num)=>
|
|
types = @users[user_num]._model.operations
|
|
[]
|
|
getRandomRoot: (user_num)->
|
|
throw new Error "implement me!"
|
|
|
|
compare: (o1, o2, depth = (@max_depth+1))->
|
|
if o1 is o2 or depth <= 0
|
|
true
|
|
else if o1._name? and o1._name isnt o2._name
|
|
throw new Error "different types"
|
|
else if o1._model?
|
|
@compare o1._model, o2._model, depth
|
|
else if o1.type is "MapManager"
|
|
for name, val of o1.val()
|
|
@compare(val, o2.val(name), depth-1)
|
|
else if o1.type is "ListManager"
|
|
for val,i in o1.val()
|
|
@compare(val, o2.val(i), depth-1)
|
|
else if o1.constructor is Array and o2.constructor is Array
|
|
if o1.length isnt o2.length
|
|
throw new Error "The Arrays do not have the same size!"
|
|
for o,i in o1
|
|
@compare o, o2[i], (depth-1)
|
|
else if o1 isnt o2
|
|
throw new Error "different values"
|
|
else
|
|
throw new Error "I don't know what to do .. "
|
|
|
|
generateRandomOp: (user_num)=>
|
|
y = @getRandomRoot(user_num)
|
|
choices = @getGeneratingFunctions(user_num).filter (gf)->
|
|
_.some gf.types, (type)->
|
|
y instanceof type
|
|
|
|
if choices.length is 0
|
|
console.dir(y)
|
|
throw new Error "You forgot to specify a test generation methot for this Operation! (#{y.type})"
|
|
i = _.random 0, (choices.length-1)
|
|
choices[i].f y
|
|
|
|
applyRandomOp: (user_num)=>
|
|
user = @users[user_num]
|
|
user._model.connector.flushOneRandom()
|
|
|
|
doSomething: ()->
|
|
user_num = _.random (@number_of_engines-1)
|
|
choices = [@applyRandomOp, @generateRandomOp]
|
|
choice = _.random (choices.length-1)
|
|
choices[choice](user_num)
|
|
|
|
flushAll: (final)->
|
|
# TODO:!!
|
|
final = false
|
|
if @users.length <= 1 or not final
|
|
for user,user_number in @users
|
|
user._model.connector.flushAll()
|
|
else
|
|
for user,user_number in @users[1..]
|
|
user._model.connector.flushAll()
|
|
ops = @users[1].getHistoryBuffer()._encode @users[0].HB.getOperationCounter()
|
|
@users[0].engine.applyOpsCheckDouble ops
|
|
|
|
|
|
compareAll: (test_number)->
|
|
@flushAll(true)
|
|
|
|
@time += (new Date()).getTime() - @time_now
|
|
|
|
number_of_created_operations = 0
|
|
for i in [0...(@users.length)]
|
|
number_of_created_operations += @users[i]._model.connector.getOpsInExecutionOrder().length
|
|
@ops += number_of_created_operations*@users.length
|
|
|
|
ops_per_msek = Math.floor(@ops/@time)
|
|
if test_number? # and @debug
|
|
console.log "#{test_number}/#{@repeat_this}: #{number_of_created_operations} were created and applied on (#{@users.length}) users ops in a different order." + " Over all we consumed #{@ops} operations in #{@time/1000} seconds (#{ops_per_msek} ops/msek)."
|
|
|
|
for i in [0...(@users.length-1)]
|
|
if @debug
|
|
if not _.isEqual @getContent(i), @getContent(i+1)
|
|
printOpsInExecutionOrder = (otnumber, otherotnumber)=>
|
|
ops = _.filter @users[otnumber]._model.connector.getOpsInExecutionOrder(), (o)->
|
|
typeof o.uid.op_name isnt 'string' and o.uid.creator isnt '_'
|
|
for s,j in ops
|
|
console.log "op#{j} = " + (JSON.stringify s)
|
|
console.log ""
|
|
s = "ops = ["
|
|
for o,j in ops
|
|
if j isnt 0
|
|
s += ", "
|
|
s += "op#{j}"
|
|
s += "]"
|
|
console.log s
|
|
console.log "@test_user.engine.applyOps ops"
|
|
console.log "expect(@test_user.val('name').val()).to.equal(\"#{@users[otherotnumber].val('name').val()}\")"
|
|
ops
|
|
console.log ""
|
|
console.log "Found an OT Puzzle!"
|
|
console.log "OT states:"
|
|
for u,j in @users
|
|
console.log "OT#{j}: "+u.val('name').val()
|
|
console.log "\nOT execution order (#{i},#{i+1}):"
|
|
printOpsInExecutionOrder i, i+1
|
|
console.log ""
|
|
ops = printOpsInExecutionOrder i+1, i
|
|
|
|
console.log ""
|
|
expect(@compare(@users[i], @users[i+1])).to.not.be.undefined
|
|
|
|
run: ()->
|
|
if @debug
|
|
console.log ''
|
|
for times in [1..@repeat_this]
|
|
@time_now = (new Date).getTime()
|
|
for i in [1..Math.floor(@doSomething_amount/2)]
|
|
@doSomething()
|
|
@flushAll(false)
|
|
for u in @users
|
|
u._model.HB.emptyGarbage()
|
|
for i in [1..Math.floor(@doSomething_amount/2)]
|
|
@doSomething()
|
|
|
|
@compareAll(times)
|
|
@testHBencoding()
|
|
if times isnt @repeat_this
|
|
@reinitialize()
|
|
|
|
testHBencoding: ()->
|
|
# in case of JsonFramework, every user will create its JSON first! therefore, the testusers id must be small than all the others (see InsertType)
|
|
@users[@users.length] = @makeNewUser (-1) # this does not want to join with anymody
|
|
|
|
@users[@users.length-1]._model.HB.renewStateVector @users[0]._model.HB.getOperationCounter()
|
|
@users[@users.length-1]._model.engine.applyOps @users[0]._model.HB._encode()
|
|
|
|
#if @getContent(@users.length-1) isnt @getContent(0)
|
|
# console.log "testHBencoding:"
|
|
# console.log "Unprocessed ops first: #{@users[0].engine.unprocessed_ops.length}"
|
|
# console.log "Unprocessed ops last: #{@users[@users.length-1].engine.unprocessed_ops.length}"
|
|
expect(@compare(@users[@users.length-1], @users[0])).to.not.be.undefined
|