Index: test/tests/typedObjects.js |
=================================================================== |
--- a/test/tests/typedObjects.js |
+++ b/test/tests/typedObjects.js |
@@ -58,17 +58,16 @@ |
mtd: function() { |
return this.foo * 2; |
} |
}, {bufferSize: 8}); |
ok(type, "Type created"); |
equal(typeof type.typeId, "number"); |
equal(typeof type.byteLength, "number"); |
- equal(type.byteLength, 8); |
// Create an object and check default properties |
let objects = []; |
objects.push(type()); |
ok(objects[0], "Object created"); |
equal(typeof objects[0].typeId, "number"); |
equal(objects[0].typeId, type.typeId); |
@@ -79,17 +78,17 @@ |
equal(typeof objects[0].byteOffset, "number"); |
equal(objects[0].byteOffset, 0); |
// The first 8 objects should go into the same buffer |
for (let i = 1; i < 8; i++) |
{ |
objects.push(type()); |
equal(objects[i].bufferIndex, 0); |
- equal(objects[i].byteOffset, 8 * i); |
+ equal(objects[i].byteOffset, type.byteLength * i); |
} |
// Properties should persist and methods should be able to access them |
for (let i = 0; i < objects.length; i++) |
{ |
objects[i].foo = i; |
objects[i].bar = 8.5 - objects[i].foo; |
} |
@@ -104,17 +103,17 @@ |
// Next objects should go into a new buffer |
let obj = type(); |
equal(obj.bufferIndex, 1); |
equal(obj.byteOffset, 0); |
obj = type(); |
equal(obj.bufferIndex, 1); |
- equal(obj.byteOffset, 8); |
+ equal(obj.byteOffset, type.byteLength); |
}); |
test("Object constructors", function() |
{ |
let {ObjectType, uint8, float32} = require("typedObjects"); |
let type = new ObjectType({ |
foo: uint8, |
bar: float32 |
@@ -294,9 +293,108 @@ |
type3.extend({foo: uint8}); |
}, "Property masks method"); |
throws(function() |
{ |
type3.extend({x: function() {}}); |
}, "Method masks property"); |
}); |
+ |
+ test("Garbage collection", function() |
+ { |
+ let {ObjectType, uint8, float32} = require("typedObjects"); |
+ |
+ let destroyed; |
+ |
+ let type1 = new ObjectType({ |
+ foo: uint8 |
+ }, { |
+ constructor: function(foo) |
+ { |
+ this.foo = foo; |
+ }, |
+ destructor: function() |
+ { |
+ destroyed.push(["type1", this.foo]); |
+ } |
+ }); |
+ |
+ // Single release() call |
+ destroyed = []; |
+ type1(1).release(); |
+ deepEqual(destroyed, [["type1", 1]], "Destructor called after release()"); |
+ |
+ // retain() and multiple release() calls |
+ destroyed = []; |
+ let obj2 = type1(2); |
+ equal(obj2.bufferIndex, 0, "New object replaces the destroyed one"); |
+ equal(obj2.byteOffset, 0, "New object replaces the destroyed one"); |
+ |
+ obj2.retain(); |
+ obj2.release(); |
+ deepEqual(destroyed, [], "Destructor not called after release() if retain() was called"); |
+ obj2.release(); |
+ deepEqual(destroyed, [["type1", 2]], "Destructor called after second release()"); |
+ |
+ // References holding object |
+ let type2 = type1.extend({ |
+ bar: type1 |
+ }, { |
+ destructor: function(super_) |
+ { |
+ super_(); |
+ destroyed.push(["type2", this.foo]); |
+ } |
+ }); |
+ |
+ destroyed = []; |
+ let obj3 = type1(3); |
+ let obj4 = type2(4); |
+ obj4.bar = obj3; |
+ obj3.release(); |
+ deepEqual(destroyed, [], "Destructor not called if references to object exist"); |
+ obj4.bar = null; |
+ deepEqual(destroyed, [["type1", 3]], "Destructor called after reference is cleared"); |
+ |
+ // Recursive destruction |
+ destroyed = []; |
+ let obj5 = type1(5); |
+ obj4.bar = obj5; |
+ obj5.release(); |
+ deepEqual(destroyed, [], "Destructor not called if references to object exist"); |
+ obj4.release(); |
+ deepEqual(destroyed, [["type1", 4], ["type2", 4], ["type1", 5]], "Destroying an object released its references"); |
+ |
+ // Misbehaving destructors |
+ let type3 = type1.extend({}, { |
+ destructor: function(super_) |
+ { |
+ this.retain(); |
+ } |
+ }); |
+ throws(function() |
+ { |
+ type3(0).release(); |
+ }, "Retaining reference in destructor is prohibited"); |
+ |
+ let type4 = type1.extend({}, { |
+ destructor: function(super_) |
+ { |
+ this.release(); |
+ } |
+ }); |
+ throws(function() |
+ { |
+ type4(0).release(); |
+ }, "Releasing reference in destructor is prohibited"); |
+ |
+ let type5 = type1.extend({}, { |
+ destructor: function(super_) |
+ { |
+ this.retain(); |
+ this.release(); |
+ } |
+ }); |
+ type5(0).release(); |
+ ok(true, "Temporarily retaining reference in destructor is allowed"); |
+ }); |
})(); |