added spec helper
This commit is contained in:
		
							parent
							
								
									010d0d684e
								
							
						
					
					
						commit
						08d07796ee
					
				
							
								
								
									
										291
									
								
								src/SpecHelper.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										291
									
								
								src/SpecHelper.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,291 @@
 | 
				
			|||||||
 | 
					/* eslint-env browser, jasmine */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					  This is just a compilation of functions that help to test this library!
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// When testing, you store everything on the global object. We call it g
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var Y = require('./y.js')
 | 
				
			||||||
 | 
					module.exports = Y
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var g
 | 
				
			||||||
 | 
					if (typeof global !== 'undefined') {
 | 
				
			||||||
 | 
					  g = global
 | 
				
			||||||
 | 
					} else if (typeof window !== 'undefined') {
 | 
				
			||||||
 | 
					  g = window
 | 
				
			||||||
 | 
					} else {
 | 
				
			||||||
 | 
					  throw new Error('No global object?')
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					g.g = g
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					g.YConcurrency_TestingMode = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jasmine.DEFAULT_TIMEOUT_INTERVAL = 20000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					g.describeManyTimes = function describeManyTimes (times, name, f) {
 | 
				
			||||||
 | 
					  for (var i = 0; i < times; i++) {
 | 
				
			||||||
 | 
					    describe(name, f)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					  Wait for a specified amount of time (in ms). defaults to 5ms
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					function wait (t) {
 | 
				
			||||||
 | 
					  if (t == null) {
 | 
				
			||||||
 | 
					    t = 5
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return new Promise(function (resolve) {
 | 
				
			||||||
 | 
					    setTimeout(function () {
 | 
				
			||||||
 | 
					      resolve()
 | 
				
			||||||
 | 
					    }, t * 3)
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					g.wait = wait
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					g.databases = ['Memory']
 | 
				
			||||||
 | 
					if (typeof window !== 'undefined') {
 | 
				
			||||||
 | 
					  g.databases.push('IndexedDB')
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					  returns a random element of o.
 | 
				
			||||||
 | 
					  works on Object, and Array
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					function getRandom (o) {
 | 
				
			||||||
 | 
					  if (o instanceof Array) {
 | 
				
			||||||
 | 
					    return o[Math.floor(Math.random() * o.length)]
 | 
				
			||||||
 | 
					  } else if (o.constructor === Object) {
 | 
				
			||||||
 | 
					    var ks = []
 | 
				
			||||||
 | 
					    for (var key in o) {
 | 
				
			||||||
 | 
					      ks.push(key)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return o[getRandom(ks)]
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					g.getRandom = getRandom
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function getRandomNumber (n) {
 | 
				
			||||||
 | 
					  if (n == null) {
 | 
				
			||||||
 | 
					    n = 9999
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return Math.floor(Math.random() * n)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					g.getRandomNumber = getRandomNumber
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function * applyTransactions (relAmount, numberOfTransactions, objects, users, transactions) {
 | 
				
			||||||
 | 
					  function randomTransaction (root) {
 | 
				
			||||||
 | 
					    var f = getRandom(transactions)
 | 
				
			||||||
 | 
					    f(root)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  for (var i = 0; i < numberOfTransactions * relAmount + 1; i++) {
 | 
				
			||||||
 | 
					    var r = Math.random()
 | 
				
			||||||
 | 
					    if (r >= 0.5) {
 | 
				
			||||||
 | 
					      // 50% chance to flush
 | 
				
			||||||
 | 
					      users[0].connector.flushOne() // flushes for some user.. (not necessarily 0)
 | 
				
			||||||
 | 
					    } else if (r >= 0.05) {
 | 
				
			||||||
 | 
					      // 45% chance to create operation
 | 
				
			||||||
 | 
					      randomTransaction(getRandom(objects))
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      // 5% chance to disconnect/reconnect
 | 
				
			||||||
 | 
					      var u = getRandom(users)
 | 
				
			||||||
 | 
					      if (u.connector.isDisconnected()) {
 | 
				
			||||||
 | 
					        yield u.reconnect()
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        yield u.disconnect()
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    yield wait()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					g.applyRandomTransactionsAllRejoinNoGC = async(function * applyRandomTransactions (users, objects, transactions, numberOfTransactions) {
 | 
				
			||||||
 | 
					  yield* applyTransactions(1, numberOfTransactions, objects, users, transactions)
 | 
				
			||||||
 | 
					  yield users[0].connector.flushAll()
 | 
				
			||||||
 | 
					  yield wait()
 | 
				
			||||||
 | 
					  for (var u in users) {
 | 
				
			||||||
 | 
					    yield users[u].reconnect()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  yield wait(100)
 | 
				
			||||||
 | 
					  yield users[0].connector.flushAll()
 | 
				
			||||||
 | 
					  yield g.garbageCollectAllUsers(users)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					g.applyRandomTransactionsWithGC = async(function * applyRandomTransactions (users, objects, transactions, numberOfTransactions) {
 | 
				
			||||||
 | 
					  yield* applyTransactions(1, numberOfTransactions, objects, users.slice(1), transactions)
 | 
				
			||||||
 | 
					  yield users[0].connector.flushAll()
 | 
				
			||||||
 | 
					  yield g.garbageCollectAllUsers(users)
 | 
				
			||||||
 | 
					  yield wait(100)
 | 
				
			||||||
 | 
					  for (var u in users) {
 | 
				
			||||||
 | 
					    // TODO: here, we enforce that two users never sync at the same time with u[0]
 | 
				
			||||||
 | 
					    //       enforce that in the connector itself!
 | 
				
			||||||
 | 
					    yield users[u].reconnect()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  yield wait(100)
 | 
				
			||||||
 | 
					  yield users[0].connector.flushAll()
 | 
				
			||||||
 | 
					  yield wait(100)
 | 
				
			||||||
 | 
					  yield g.garbageCollectAllUsers(users)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					g.garbageCollectAllUsers = async(function * garbageCollectAllUsers (users) {
 | 
				
			||||||
 | 
					  // gc two times because of the two gc phases (really collect everything)
 | 
				
			||||||
 | 
					  yield wait(100)
 | 
				
			||||||
 | 
					  for (var i in users) {
 | 
				
			||||||
 | 
					    yield users[i].db.garbageCollect()
 | 
				
			||||||
 | 
					    yield users[i].db.garbageCollect()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  yield wait(100)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					g.compareAllUsers = async(function * compareAllUsers (users) {
 | 
				
			||||||
 | 
					  var s1, s2 // state sets
 | 
				
			||||||
 | 
					  var ds1, ds2 // delete sets
 | 
				
			||||||
 | 
					  var allDels1, allDels2 // all deletions
 | 
				
			||||||
 | 
					  var db1 = [] // operation store of user1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // t1 and t2 basically do the same. They define t[1,2], ds[1,2], and allDels[1,2]
 | 
				
			||||||
 | 
					  function * t1 () {
 | 
				
			||||||
 | 
					    s1 = yield* this.getStateSet()
 | 
				
			||||||
 | 
					    ds1 = yield* this.getDeleteSet()
 | 
				
			||||||
 | 
					    allDels1 = []
 | 
				
			||||||
 | 
					    yield* this.ds.iterate(this, null, null, function * (d) {
 | 
				
			||||||
 | 
					      allDels1.push(d)
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  function * t2 () {
 | 
				
			||||||
 | 
					    s2 = yield* this.getStateSet()
 | 
				
			||||||
 | 
					    ds2 = yield* this.getDeleteSet()
 | 
				
			||||||
 | 
					    allDels2 = []
 | 
				
			||||||
 | 
					    yield* this.ds.iterate(this, null, null, function * (d) {
 | 
				
			||||||
 | 
					      allDels2.push(d)
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  yield users[0].connector.flushAll()
 | 
				
			||||||
 | 
					  yield wait()
 | 
				
			||||||
 | 
					  yield g.garbageCollectAllUsers(users)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (var uid = 0; uid < users.length; uid++) {
 | 
				
			||||||
 | 
					    var u = users[uid]
 | 
				
			||||||
 | 
					    u.db.requestTransaction(function * () {
 | 
				
			||||||
 | 
					      // compare deleted ops against deleteStore
 | 
				
			||||||
 | 
					      yield* this.os.iterate(this, null, null, function * (o) {
 | 
				
			||||||
 | 
					        if (o.deleted === true) {
 | 
				
			||||||
 | 
					          expect(yield* this.isDeleted(o.id)).toBeTruthy()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      // compare deleteStore against deleted ops
 | 
				
			||||||
 | 
					      var ds = []
 | 
				
			||||||
 | 
					      yield* this.ds.iterate(this, null, null, function * (d) {
 | 
				
			||||||
 | 
					        ds.push(d)
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      for (var j in ds) {
 | 
				
			||||||
 | 
					        var d = ds[j]
 | 
				
			||||||
 | 
					        for (var i = 0; i < d.len; i++) {
 | 
				
			||||||
 | 
					          var o = yield* this.getOperation([d.id[0], d.id[1] + i])
 | 
				
			||||||
 | 
					          // gc'd or deleted
 | 
				
			||||||
 | 
					          if (d.gc) {
 | 
				
			||||||
 | 
					            expect(o).toBeFalsy()
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            expect(o.deleted).toBeTruthy()
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    // compare allDels tree
 | 
				
			||||||
 | 
					    yield wait()
 | 
				
			||||||
 | 
					    if (s1 == null) {
 | 
				
			||||||
 | 
					      u.db.requestTransaction(function * () {
 | 
				
			||||||
 | 
					        yield* t1.call(this)
 | 
				
			||||||
 | 
					        yield* this.os.iterate(this, null, null, function * (o) {
 | 
				
			||||||
 | 
					          o = Y.utils.copyObject(o)
 | 
				
			||||||
 | 
					          delete o.origin
 | 
				
			||||||
 | 
					          db1.push(o)
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      yield wait()
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      // TODO: make requestTransaction return a promise..
 | 
				
			||||||
 | 
					      u.db.requestTransaction(function * () {
 | 
				
			||||||
 | 
					        yield* t2.call(this)
 | 
				
			||||||
 | 
					        expect(s1).toEqual(s2)
 | 
				
			||||||
 | 
					        expect(allDels1).toEqual(allDels2) // inner structure
 | 
				
			||||||
 | 
					        expect(ds1).toEqual(ds2) // exported structure
 | 
				
			||||||
 | 
					        var count = 0
 | 
				
			||||||
 | 
					        yield* this.os.iterate(this, null, null, function * (o) {
 | 
				
			||||||
 | 
					          o = Y.utils.copyObject(o)
 | 
				
			||||||
 | 
					          delete o.origin
 | 
				
			||||||
 | 
					          expect(db1[count++]).toEqual(o)
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      yield wait()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					g.createUsers = async(function * createUsers (self, numberOfUsers, database) {
 | 
				
			||||||
 | 
					  if (Y.utils.globalRoom.users[0] != null) {
 | 
				
			||||||
 | 
					    yield Y.utils.globalRoom.users[0].flushAll()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  // destroy old users
 | 
				
			||||||
 | 
					  for (var u in Y.utils.globalRoom.users) {
 | 
				
			||||||
 | 
					    Y.utils.globalRoom.users[u].y.destroy()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  self.users = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  var promises = []
 | 
				
			||||||
 | 
					  for (var i = 0; i < numberOfUsers; i++) {
 | 
				
			||||||
 | 
					    promises.push(Y({
 | 
				
			||||||
 | 
					      db: {
 | 
				
			||||||
 | 
					        name: database,
 | 
				
			||||||
 | 
					        namespace: 'User ' + i,
 | 
				
			||||||
 | 
					        cleanStart: true,
 | 
				
			||||||
 | 
					        gcTimeout: -1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      connector: {
 | 
				
			||||||
 | 
					        name: 'Test',
 | 
				
			||||||
 | 
					        debug: false
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  self.users = yield Promise.all(promises)
 | 
				
			||||||
 | 
					  return self.users
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					  Until async/await arrives in js, we use this function to wait for promises
 | 
				
			||||||
 | 
					  by yielding them.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					function async (makeGenerator) {
 | 
				
			||||||
 | 
					  return function (arg) {
 | 
				
			||||||
 | 
					    var generator = makeGenerator.apply(this, arguments)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function handle (result) {
 | 
				
			||||||
 | 
					      if (result.done) return Promise.resolve(result.value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return Promise.resolve(result.value).then(function (res) {
 | 
				
			||||||
 | 
					        return handle(generator.next(res))
 | 
				
			||||||
 | 
					      }, function (err) {
 | 
				
			||||||
 | 
					        return handle(generator.throw(err))
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      return handle(generator.next())
 | 
				
			||||||
 | 
					    } catch (ex) {
 | 
				
			||||||
 | 
					      generator.throw(ex)
 | 
				
			||||||
 | 
					      // return Promise.reject(ex)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					g.async = async
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function logUsers (self) {
 | 
				
			||||||
 | 
					  if (self.constructor === Array) {
 | 
				
			||||||
 | 
					    self = {users: self}
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  self.users[0].db.logTable()
 | 
				
			||||||
 | 
					  self.users[1].db.logTable()
 | 
				
			||||||
 | 
					  self.users[2].db.logTable()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					g.logUsers = logUsers
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user