| suite('tree', function() { |
| suite('adopt', function() { |
| function assertTwoChildren(parent, one, two) { |
| assert.equal(one.parent, parent, 'one.parent is set'); |
| assert.equal(two.parent, parent, 'two.parent is set'); |
| |
| assert.ok(!one[L], 'one has nothing leftward'); |
| assert.equal(one[R], two, 'one[R] is two'); |
| assert.equal(two[L], one, 'two[L] is one'); |
| assert.ok(!two[R], 'two has nothing rightward'); |
| |
| assert.equal(parent.ends[L], one, 'parent.ends[L] is one'); |
| assert.equal(parent.ends[R], two, 'parent.ends[R] is two'); |
| } |
| |
| test('the empty case', function() { |
| var parent = Node(); |
| var child = Node(); |
| |
| child.adopt(parent, 0, 0); |
| |
| assert.equal(child.parent, parent, 'child.parent is set'); |
| assert.ok(!child[R], 'child has nothing rightward'); |
| assert.ok(!child[L], 'child has nothing leftward'); |
| |
| assert.equal(parent.ends[L], child, 'child is parent.ends[L]'); |
| assert.equal(parent.ends[R], child, 'child is parent.ends[R]'); |
| }); |
| |
| test('with two children from the left', function() { |
| var parent = Node(); |
| var one = Node(); |
| var two = Node(); |
| |
| one.adopt(parent, 0, 0); |
| two.adopt(parent, one, 0); |
| |
| assertTwoChildren(parent, one, two); |
| }); |
| |
| test('with two children from the right', function() { |
| var parent = Node(); |
| var one = Node(); |
| var two = Node(); |
| |
| two.adopt(parent, 0, 0); |
| one.adopt(parent, 0, two); |
| |
| assertTwoChildren(parent, one, two); |
| }); |
| |
| test('adding one in the middle', function() { |
| var parent = Node(); |
| var leftward = Node(); |
| var rightward = Node(); |
| var middle = Node(); |
| |
| leftward.adopt(parent, 0, 0); |
| rightward.adopt(parent, leftward, 0); |
| middle.adopt(parent, leftward, rightward); |
| |
| assert.equal(middle.parent, parent, 'middle.parent is set'); |
| assert.equal(middle[L], leftward, 'middle[L] is set'); |
| assert.equal(middle[R], rightward, 'middle[R] is set'); |
| |
| assert.equal(leftward[R], middle, 'leftward[R] is middle'); |
| assert.equal(rightward[L], middle, 'rightward[L] is middle'); |
| |
| assert.equal(parent.ends[L], leftward, 'parent.ends[L] is leftward'); |
| assert.equal(parent.ends[R], rightward, 'parent.ends[R] is rightward'); |
| }); |
| }); |
| |
| suite('disown', function() { |
| function assertSingleChild(parent, child) { |
| assert.equal(parent.ends[L], child, 'parent.ends[L] is child'); |
| assert.equal(parent.ends[R], child, 'parent.ends[R] is child'); |
| assert.ok(!child[L], 'child has nothing leftward'); |
| assert.ok(!child[R], 'child has nothing rightward'); |
| } |
| |
| test('the empty case', function() { |
| var parent = Node(); |
| var child = Node(); |
| |
| child.adopt(parent, 0, 0); |
| child.disown(); |
| |
| assert.ok(!parent.ends[L], 'parent has no left end child'); |
| assert.ok(!parent.ends[R], 'parent has no right end child'); |
| }); |
| |
| test('disowning the right end child', function() { |
| var parent = Node(); |
| var one = Node(); |
| var two = Node(); |
| |
| one.adopt(parent, 0, 0); |
| two.adopt(parent, one, 0); |
| |
| two.disown(); |
| |
| assertSingleChild(parent, one); |
| |
| assert.equal(two.parent, parent, 'two retains its parent'); |
| assert.equal(two[L], one, 'two retains its [L]'); |
| |
| assert.throws(function() { two.disown(); }, |
| 'disown fails on a malformed tree'); |
| }); |
| |
| test('disowning the left end child', function() { |
| var parent = Node(); |
| var one = Node(); |
| var two = Node(); |
| |
| one.adopt(parent, 0, 0); |
| two.adopt(parent, one, 0); |
| |
| one.disown(); |
| |
| assertSingleChild(parent, two); |
| |
| assert.equal(one.parent, parent, 'one retains its parent'); |
| assert.equal(one[R], two, 'one retains its [R]'); |
| |
| assert.throws(function() { one.disown(); }, |
| 'disown fails on a malformed tree'); |
| }); |
| |
| test('disowning the middle', function() { |
| var parent = Node(); |
| var leftward = Node(); |
| var rightward = Node(); |
| var middle = Node(); |
| |
| leftward.adopt(parent, 0, 0); |
| rightward.adopt(parent, leftward, 0); |
| middle.adopt(parent, leftward, rightward); |
| |
| middle.disown(); |
| |
| assert.equal(leftward[R], rightward, 'leftward[R] is rightward'); |
| assert.equal(rightward[L], leftward, 'rightward[L] is leftward'); |
| assert.equal(parent.ends[L], leftward, 'parent.ends[L] is leftward'); |
| assert.equal(parent.ends[R], rightward, 'parent.ends[R] is rightward'); |
| |
| assert.equal(middle.parent, parent, 'middle retains its parent'); |
| assert.equal(middle[R], rightward, 'middle retains its [R]'); |
| assert.equal(middle[L], leftward, 'middle retains its [L]'); |
| |
| assert.throws(function() { middle.disown(); }, |
| 'disown fails on a malformed tree'); |
| }); |
| }); |
| |
| suite('fragments', function() { |
| test('an empty fragment', function() { |
| var empty = Fragment(); |
| var count = 0; |
| |
| empty.each(function() { count += 1 }); |
| |
| assert.equal(count, 0, 'each is a noop on an empty fragment'); |
| }); |
| |
| test('half-empty fragments are disallowed', function() { |
| assert.throws(function() { |
| Fragment(Node(), 0) |
| }, 'half-empty on the right'); |
| |
| assert.throws(function() { |
| Fragment(0, Node()); |
| }, 'half-empty on the left'); |
| }); |
| |
| test('directionalized constructor call', function() { |
| var ChNode = P(Node, { init: function(ch) { this.ch = ch; } }); |
| var parent = Node(); |
| var a = ChNode('a').adopt(parent, parent.ends[R], 0); |
| var b = ChNode('b').adopt(parent, parent.ends[R], 0); |
| var c = ChNode('c').adopt(parent, parent.ends[R], 0); |
| var d = ChNode('d').adopt(parent, parent.ends[R], 0); |
| var e = ChNode('e').adopt(parent, parent.ends[R], 0); |
| |
| function cat(str, node) { return str + node.ch; } |
| assert.equal('bcd', Fragment(b, d).fold('', cat)); |
| assert.equal('bcd', Fragment(b, d, L).fold('', cat)); |
| assert.equal('bcd', Fragment(d, b, R).fold('', cat)); |
| assert.throws(function() { Fragment(d, b, L); }); |
| assert.throws(function() { Fragment(b, d, R); }); |
| }); |
| |
| test('disown is idempotent', function() { |
| var parent = Node(); |
| var one = Node().adopt(parent, 0, 0); |
| var two = Node().adopt(parent, one, 0); |
| |
| var frag = Fragment(one, two); |
| frag.disown(); |
| frag.disown(); |
| }); |
| }); |
| }); |