261 lines
6.8 KiB
JavaScript
261 lines
6.8 KiB
JavaScript
/* global Y */
|
|
/* 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 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 = 5000
|
|
|
|
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)
|
|
})
|
|
}
|
|
g.wait = wait
|
|
|
|
/*
|
|
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
|
|
|
|
g.applyRandomTransactions = async(function * applyRandomTransactions (users, objects, transactions, numberOfTransactions) {
|
|
function randomTransaction (root) {
|
|
var f = getRandom(transactions)
|
|
f(root)
|
|
}
|
|
function * applyTransactions (relAmount) {
|
|
for (var i = 0; i < numberOfTransactions * relAmount + 1; i++) {
|
|
var r = Math.random()
|
|
if (r >= 0.9) {
|
|
// 10% chance to flush
|
|
users[0].connector.flushOne() // flushes for some user.. (not necessarily 0)
|
|
} else if (r >= 0.1) {
|
|
// 80% chance to create operation
|
|
randomTransaction(getRandom(objects))
|
|
} else {
|
|
// 10% chance to disconnect/reconnect
|
|
var u = getRandom(users)
|
|
if (u.connector.isDisconnected()) {
|
|
u.reconnect()
|
|
} else {
|
|
u.disconnect()
|
|
}
|
|
}
|
|
yield wait()
|
|
}
|
|
}
|
|
yield* applyTransactions(0.5)
|
|
yield users[0].connector.flushAll()
|
|
yield g.garbageCollectAllUsers(users)
|
|
yield wait()
|
|
users[0].disconnect()
|
|
yield wait()
|
|
yield* applyTransactions(0.5)
|
|
yield users[0].connector.flushAll()
|
|
yield wait(50)
|
|
for (var u in users) {
|
|
users[u].reconnect()
|
|
}
|
|
yield wait(100)
|
|
yield users[0].connector.flushAll()
|
|
})
|
|
|
|
g.garbageCollectAllUsers = async(function * garbageCollectAllUsers (users) {
|
|
return yield wait(100)// TODO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
for (var i in users) {
|
|
yield users[i].db.garbageCollect()
|
|
yield users[i].db.garbageCollect()
|
|
}
|
|
})
|
|
|
|
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 = []
|
|
this.ds.iterate(null, null, function (d) {
|
|
allDels1.push(d)
|
|
})
|
|
}
|
|
function * t2 () {
|
|
s2 = yield* this.getStateSet()
|
|
ds2 = yield* this.getDeleteSet()
|
|
allDels2 = []
|
|
this.ds.iterate(null, null, function (d) {
|
|
allDels2.push(d)
|
|
})
|
|
}
|
|
yield users[0].connector.flushAll()
|
|
// gc two times because of the two gc phases (really collect everything)
|
|
yield g.garbageCollectAllUsers(users)
|
|
|
|
for (var uid = 0; uid < users.length; uid++) {
|
|
var u = users[uid]
|
|
// compare deleted ops against deleteStore
|
|
u.db.os.iterate(null, null, function (o) {
|
|
if (o.deleted === true) {
|
|
expect(u.db.ds.isDeleted(o.id)).toBeTruthy()
|
|
}
|
|
})
|
|
// compare deleteStore against deleted ops
|
|
u.db.requestTransaction(function * () {
|
|
var ds = []
|
|
u.db.ds.iterate(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).toBeUndefined()
|
|
} else {
|
|
expect(o.deleted).toBeTruthy()
|
|
}
|
|
}
|
|
}
|
|
})
|
|
// compare allDels tree
|
|
yield wait()
|
|
if (s1 == null) {
|
|
u.db.requestTransaction(t1)
|
|
yield wait()
|
|
u.db.os.iterate(null, null, function (o) {
|
|
db1.push(o)
|
|
})
|
|
} else {
|
|
u.db.requestTransaction(t2)
|
|
yield wait()
|
|
expect(s1).toEqual(s2)
|
|
expect(allDels1).toEqual(allDels2) // inner structure
|
|
expect(ds1).toEqual(ds2) // exported structure
|
|
var count = 0
|
|
u.db.os.iterate(null, null, function (o) {
|
|
expect(db1[count++]).toEqual(o)
|
|
})
|
|
}
|
|
}
|
|
})
|
|
|
|
g.createUsers = async(function * createUsers (self, numberOfUsers) {
|
|
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: 'Memory',
|
|
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) // TODO: check this out
|
|
// return Promise.reject(ex)
|
|
}
|
|
}
|
|
}
|
|
g.async = async
|
|
|
|
function logUsers (self) {
|
|
if (self.constructor === Array) {
|
|
self = {users: self}
|
|
}
|
|
console.log('User 1: ', self.users[0].connector.userId, "=============================================") // eslint-disable-line
|
|
self.users[0].db.logTable() // eslint-disable-line
|
|
console.log('User 2: ', self.users[1].connector.userId, "=============================================") // eslint-disable-line
|
|
self.users[1].db.logTable() // eslint-disable-line
|
|
console.log('User 3: ', self.users[2].connector.userId, "=============================================") // eslint-disable-line
|
|
self.users[2].db.logTable() // eslint-disable-line
|
|
}
|
|
g.logUsers = logUsers
|