yjs/src/OperationStores/RedBlackTree.spec.js
2015-09-10 19:41:07 +02:00

213 lines
6.6 KiB
JavaScript

/* global Y */
/* eslint-env browser,jasmine,console */
var RBTree = Y.RBTree
var compareIds = Y.compareIds
var smaller = Y.smaller
var numberOfRBTreeTests = 1000
function itRedNodesDoNotHaveBlackChildren (tree) {
it('Red nodes do not have black children', function () {
function traverse (n) {
if (n == null) {
return
}
if (n.isRed()) {
if (n.left != null) {
expect(n.left.isRed()).not.toBeTruthy()
}
if (n.right != null) {
expect(n.right.isRed()).not.toBeTruthy()
}
}
traverse(n.left)
traverse(n.right)
}
traverse(tree.root)
})
}
function itBlackHeightOfSubTreesAreEqual (tree) {
it('Black-height of sub-trees are equal', function () {
function traverse (n) {
if (n == null) {
return 0
}
var sub1 = traverse(n.left)
var sub2 = traverse(n.right)
expect(sub1).toEqual(sub2)
if (n.isRed()) {
return sub1
} else {
return sub1 + 1
}
}
traverse(tree.root)
})
}
function itRootNodeIsBlack (tree) {
it('root node is black', function () {
expect(tree.root == null || tree.root.isBlack()).toBeTruthy()
})
}
describe('RedBlack Tree', function () {
beforeEach(function () {
this.tree = new RBTree()
})
it('can add&retrieve 5 elements', function () {
this.tree.add({val: 'four', id: [4]})
this.tree.add({val: 'one', id: [1]})
this.tree.add({val: 'three', id: [3]})
this.tree.add({val: 'two', id: [2]})
this.tree.add({val: 'five', id: [5]})
expect(this.tree.find([1]).val).toEqual('one')
expect(this.tree.find([2]).val).toEqual('two')
expect(this.tree.find([3]).val).toEqual('three')
expect(this.tree.find([4]).val).toEqual('four')
expect(this.tree.find([5]).val).toEqual('five')
})
it('5 elements do not exist anymore after deleting them', function () {
this.tree.add({val: 'four', id: [4]})
this.tree.add({val: 'one', id: [1]})
this.tree.add({val: 'three', id: [3]})
this.tree.add({val: 'two', id: [2]})
this.tree.add({val: 'five', id: [5]})
this.tree.delete([4])
expect(this.tree.find([4])).not.toBeTruthy()
this.tree.delete([3])
expect(this.tree.find([3])).not.toBeTruthy()
this.tree.delete([2])
expect(this.tree.find([2])).not.toBeTruthy()
this.tree.delete([1])
expect(this.tree.find([1])).not.toBeTruthy()
this.tree.delete([5])
expect(this.tree.find([5])).not.toBeTruthy()
})
it('debug #1', function () {
this.tree.add({id: [2]})
this.tree.add({id: [0]})
this.tree.delete([2])
this.tree.add({id: [1]})
expect(this.tree.find([0])).not.toBeUndefined()
expect(this.tree.find([1])).not.toBeUndefined()
expect(this.tree.find([2])).toBeUndefined()
})
describe('debug #2', function () {
var tree = new RBTree()
tree.add({id: [8433]})
tree.add({id: [12844]})
tree.add({id: [1795]})
tree.add({id: [30302]})
tree.add({id: [64287]})
tree.delete([8433])
tree.add({id: [28996]})
tree.delete([64287])
tree.add({id: [22721]})
itRootNodeIsBlack(tree, [])
itBlackHeightOfSubTreesAreEqual(tree, [])
})
describe(`After adding&deleting (0.8/0.2) ${numberOfRBTreeTests} times`, function () {
var elements = []
var tree = new RBTree()
for (var i = 0; i < numberOfRBTreeTests; i++) {
var r = Math.random()
if (r < 0.8) {
var obj = [Math.floor(Math.random() * numberOfRBTreeTests * 10000)]
elements.push(obj)
tree.add({id: obj})
} else if (elements.length > 0) {
var elemid = Math.floor(Math.random() * elements.length)
var elem = elements[elemid]
elements = elements.filter(function (e) {return !compareIds(e, elem); }); // eslint-disable-line
tree.delete(elem)
}
}
itRootNodeIsBlack(tree)
it('can find every object', function () {
for (var id of elements) {
expect(tree.find(id).id).toEqual(id)
}
})
it('can find every object with lower bound search', function () {
for (var id of elements) {
expect(tree.findNodeWithLowerBound(id).val.id).toEqual(id)
}
})
itRedNodesDoNotHaveBlackChildren(tree)
itBlackHeightOfSubTreesAreEqual(tree)
it('iterating over a tree with lower bound yields the right amount of results', function () {
var lowerBound = elements[Math.floor(Math.random() * elements.length)]
var expectedResults = elements.filter(function (e, pos) {
return (smaller(lowerBound, e) || compareIds(e, lowerBound)) && elements.indexOf(e) === pos
}).length
var actualResults = 0
tree.iterate(lowerBound, null, function (val) {
expect(val).not.toBeUndefined()
actualResults++
})
expect(expectedResults).toEqual(actualResults)
})
it('iterating over a tree without bounds yield the right amount of results', function () {
var lowerBound = null
var expectedResults = elements.filter(function (e, pos) {
return elements.indexOf(e) === pos
}).length
var actualResults = 0
tree.iterate(lowerBound, null, function (val) {
expect(val).not.toBeUndefined()
actualResults++
})
expect(expectedResults).toEqual(actualResults)
})
it('iterating over a tree with upper bound yields the right amount of results', function () {
var upperBound = elements[Math.floor(Math.random() * elements.length)]
var expectedResults = elements.filter(function (e, pos) {
return (smaller(e, upperBound) || compareIds(e, upperBound)) && elements.indexOf(e) === pos
}).length
var actualResults = 0
tree.iterate(null, upperBound, function (val) {
expect(val).not.toBeUndefined()
actualResults++
})
expect(expectedResults).toEqual(actualResults)
})
it('iterating over a tree with upper and lower bounds yield the right amount of results', function () {
var b1 = elements[Math.floor(Math.random() * elements.length)]
var b2 = elements[Math.floor(Math.random() * elements.length)]
var upperBound, lowerBound
if (smaller(b1, b2)) {
lowerBound = b1
upperBound = b2
} else {
lowerBound = b2
upperBound = b1
}
var expectedResults = elements.filter(function (e, pos) {
return (smaller(lowerBound, e) || compareIds(e, lowerBound)) &&
(smaller(e, upperBound) || compareIds(e, upperBound)) && elements.indexOf(e) === pos
}).length
var actualResults = 0
tree.iterate(lowerBound, upperBound, function (val) {
expect(val).not.toBeUndefined()
actualResults++
})
expect(expectedResults).toEqual(actualResults)
})
})
})