chai = require('chai') expect = chai.expect should = chai.should() sinon = require('sinon') sinonChai = require('sinon-chai') _ = require("underscore") chai.use(sinonChai) Connector = require "../../y-test/lib/y-test.coffee" module.exports = class Test constructor: (@name_suffix = "")-> @number_of_test_cases_multiplier = 1 @repeat_this = 3 * @number_of_test_cases_multiplier @doSomething_amount = 1230 * @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 @debug = false @reinitialize() reinitialize: ()-> @users = [] for i in [0...@number_of_engines] u = @makeNewUser (i+@name_suffix) for user in @users u.getConnector().join(user.getConnector()) # 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.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].types [ f : (y)=> # INSERT TEXT y pos = _.random 0, (y.val().length-1) y.insert pos, @getRandomText() null types: [types.String] , 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 : [types.String] ] getRandomRoot: (user_num)-> throw new Error "overwrite me!" getContent: (user_num)-> throw new Error "overwrite me!" 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.getConnector().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.getConnector().flushAll() else for user,user_number in @users[1..] user.getConnector().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].getConnector().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].getConnector().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(@getContent(i)).to.deep.equal(@getContent(i+1)) 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.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].HB.renewStateVector @users[0].HB.getOperationCounter() @users[@users.length-1].engine.applyOps @users[0].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(@getContent(@users.length-1)).to.deep.equal(@getContent(0))