Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

var TEST_SIZE = 100;
function run_test() {
let library = open_ctypes_test_lib();
let start = library.declare(
"test_finalizer_start",
ctypes.default_abi,
ctypes.void_t,
ctypes.size_t
);
let stop = library.declare(
"test_finalizer_stop",
ctypes.default_abi,
ctypes.void_t
);
let status = library.declare(
"test_finalizer_resource_is_acquired",
ctypes.default_abi,
ctypes.bool,
ctypes.size_t
);
let released = function released(value, witness) {
return witness == undefined;
};
let samples = [];
samples.push({
name: "size_t",
acquire: library.declare(
"test_finalizer_acq_size_t",
ctypes.default_abi,
ctypes.size_t,
ctypes.size_t
),
release: library.declare(
"test_finalizer_rel_size_t",
ctypes.default_abi,
ctypes.void_t,
ctypes.size_t
),
compare: library.declare(
"test_finalizer_cmp_size_t",
ctypes.default_abi,
ctypes.bool,
ctypes.size_t,
ctypes.size_t
),
status,
released,
});
samples.push({
name: "size_t",
acquire: library.declare(
"test_finalizer_acq_size_t",
ctypes.default_abi,
ctypes.size_t,
ctypes.size_t
),
release: library.declare(
"test_finalizer_rel_size_t_set_errno",
ctypes.default_abi,
ctypes.void_t,
ctypes.size_t
),
compare: library.declare(
"test_finalizer_cmp_size_t",
ctypes.default_abi,
ctypes.bool,
ctypes.size_t,
ctypes.size_t
),
status,
released,
});
samples.push({
name: "int32_t",
acquire: library.declare(
"test_finalizer_acq_int32_t",
ctypes.default_abi,
ctypes.int32_t,
ctypes.size_t
),
release: library.declare(
"test_finalizer_rel_int32_t",
ctypes.default_abi,
ctypes.void_t,
ctypes.int32_t
),
compare: library.declare(
"test_finalizer_cmp_int32_t",
ctypes.default_abi,
ctypes.bool,
ctypes.int32_t,
ctypes.int32_t
),
status,
released,
});
samples.push({
name: "int64_t",
acquire: library.declare(
"test_finalizer_acq_int64_t",
ctypes.default_abi,
ctypes.int64_t,
ctypes.size_t
),
release: library.declare(
"test_finalizer_rel_int64_t",
ctypes.default_abi,
ctypes.void_t,
ctypes.int64_t
),
compare: library.declare(
"test_finalizer_cmp_int64_t",
ctypes.default_abi,
ctypes.bool,
ctypes.int64_t,
ctypes.int64_t
),
status,
released,
});
samples.push({
name: "ptr",
acquire: library.declare(
"test_finalizer_acq_ptr_t",
ctypes.default_abi,
ctypes.PointerType(ctypes.void_t),
ctypes.size_t
),
release: library.declare(
"test_finalizer_rel_ptr_t",
ctypes.default_abi,
ctypes.void_t,
ctypes.PointerType(ctypes.void_t)
),
compare: library.declare(
"test_finalizer_cmp_ptr_t",
ctypes.default_abi,
ctypes.bool,
ctypes.void_t.ptr,
ctypes.void_t.ptr
),
status,
released,
});
samples.push({
name: "string",
acquire: library.declare(
"test_finalizer_acq_string_t",
ctypes.default_abi,
ctypes.char.ptr,
ctypes.int
),
release: library.declare(
"test_finalizer_rel_string_t",
ctypes.default_abi,
ctypes.void_t,
ctypes.char.ptr
),
compare: library.declare(
"test_finalizer_cmp_string_t",
ctypes.default_abi,
ctypes.bool,
ctypes.char.ptr,
ctypes.char.ptr
),
status,
released,
});
const rect_t = new ctypes.StructType("myRECT", [
{ top: ctypes.int32_t },
{ left: ctypes.int32_t },
{ bottom: ctypes.int32_t },
{ right: ctypes.int32_t },
]);
samples.push({
name: "struct",
acquire: library.declare(
"test_finalizer_acq_struct_t",
ctypes.default_abi,
rect_t,
ctypes.int
),
release: library.declare(
"test_finalizer_rel_struct_t",
ctypes.default_abi,
ctypes.void_t,
rect_t
),
compare: library.declare(
"test_finalizer_cmp_struct_t",
ctypes.default_abi,
ctypes.bool,
rect_t,
rect_t
),
status,
released,
});
samples.push({
name: "size_t, release returns size_t",
acquire: library.declare(
"test_finalizer_acq_size_t",
ctypes.default_abi,
ctypes.size_t,
ctypes.size_t
),
release: library.declare(
"test_finalizer_rel_size_t_return_size_t",
ctypes.default_abi,
ctypes.size_t,
ctypes.size_t
),
compare: library.declare(
"test_finalizer_cmp_size_t",
ctypes.default_abi,
ctypes.bool,
ctypes.size_t,
ctypes.size_t
),
status,
released: function released_eq(i, witness) {
return i == witness;
},
});
samples.push({
name: "size_t, release returns myRECT",
acquire: library.declare(
"test_finalizer_acq_size_t",
ctypes.default_abi,
ctypes.size_t,
ctypes.size_t
),
release: library.declare(
"test_finalizer_rel_size_t_return_struct_t",
ctypes.default_abi,
rect_t,
ctypes.size_t
),
compare: library.declare(
"test_finalizer_cmp_size_t",
ctypes.default_abi,
ctypes.bool,
ctypes.size_t,
ctypes.size_t
),
status,
released: function released_rect_eq(i, witness) {
return (
witness.top == i &&
witness.bottom == i &&
witness.left == i &&
witness.right == i
);
},
});
samples.push({
name: "using null",
acquire: library.declare(
"test_finalizer_acq_null_t",
ctypes.default_abi,
ctypes.PointerType(ctypes.void_t),
ctypes.size_t
),
release: library.declare(
"test_finalizer_rel_null_t",
ctypes.default_abi,
ctypes.void_t,
ctypes.PointerType(ctypes.void_t)
),
status: library.declare(
"test_finalizer_null_resource_is_acquired",
ctypes.default_abi,
ctypes.bool,
ctypes.size_t
),
compare: library.declare(
"test_finalizer_cmp_null_t",
ctypes.default_abi,
ctypes.bool,
ctypes.void_t.ptr,
ctypes.void_t.ptr
),
released,
});
let tester = new ResourceTester(start, stop);
samples.forEach(function run_sample(sample) {
dump("Executing finalization test for data " + sample.name + "\n");
tester.launch(TEST_SIZE, test_executing_finalizers, sample);
tester.launch(
TEST_SIZE,
test_do_not_execute_finalizers_on_referenced_stuff,
sample
);
tester.launch(TEST_SIZE, test_executing_dispose, sample);
tester.launch(TEST_SIZE, test_executing_forget, sample);
tester.launch(TEST_SIZE, test_result_dispose, sample);
dump(
"Successfully completed finalization test for data " + sample.name + "\n"
);
});
/*
* Following test deactivated: Cycle collection never takes place
* (see bug 727371)
tester.launch(TEST_SIZE, test_cycles, samples[0]);
*/
dump("Successfully completed all finalization tests\n");
library.close();
}
// If only I could have Promises to test this :)
// There is only so much we can do at this stage,
// if we want to avoid tests overlapping.
// Deactivated - see comment above.
// function test_cycles(size, tc) {
// // Now, restart this with unreferenced cycles
// for (let i = 0; i < size / 2; ++i) {
// let a = {
// a: ctypes.CDataFinalizer(tc.acquire(i * 2), tc.release),
// b: {
// b: ctypes.CDataFinalizer(tc.acquire(i * 2 + 1), tc.release),
// },
// };
// a.b.a = a;
// }
// do_test_pending();
// Cu.schedulePreciseGC(function after_gc() {
// // Check that _something_ has been finalized
// Assert.ok(count_finalized(size, tc) > 0);
// do_test_finished();
// });
// do_timeout(10000, do_throw);
// }
function count_finalized(size, tc) {
let finalizedItems = 0;
for (let i = 0; i < size; ++i) {
if (!tc.status(i)) {
++finalizedItems;
}
}
return finalizedItems;
}
/**
* Test:
* - that (some) finalizers are executed;
* - that no finalizer is executed twice (this is done on the C side).
*/
function test_executing_finalizers(size, tc, cleanup) {
dump("test_executing_finalizers " + tc.name + "\n");
// Allocate |size| items without references
for (let i = 0; i < size; ++i) {
cleanup.add(ctypes.CDataFinalizer(tc.acquire(i), tc.release));
}
trigger_gc(); // This should trigger some finalizations, hopefully all
// Check that _something_ has been finalized
Assert.ok(count_finalized(size, tc) > 0);
}
/**
* Check that
* - |dispose| returns the proper result
*/
function test_result_dispose(size, tc, cleanup) {
dump("test_result_dispose " + tc.name + "\n");
let ref = [];
// Allocate |size| items with references
for (let i = 0; i < size; ++i) {
let value = ctypes.CDataFinalizer(tc.acquire(i), tc.release);
cleanup.add(value);
ref.push(value);
}
Assert.equal(count_finalized(size, tc), 0);
for (let i = 0; i < size; ++i) {
let witness = ref[i].dispose();
ref[i] = null;
if (!tc.released(i, witness)) {
info("test_result_dispose failure at index " + i);
Assert.ok(false);
}
}
Assert.equal(count_finalized(size, tc), size);
}
/**
* Check that
* - |dispose| is executed properly
* - finalizers are not executed after |dispose|
*/
function test_executing_dispose(size, tc, cleanup) {
dump("test_executing_dispose " + tc.name + "\n");
let ref = [];
// Allocate |size| items with references
for (let i = 0; i < size; ++i) {
let value = ctypes.CDataFinalizer(tc.acquire(i), tc.release);
cleanup.add(value);
ref.push(value);
}
Assert.equal(count_finalized(size, tc), 0);
// Dispose of everything and make sure that everything has been cleaned up
ref.forEach(function dispose(v) {
v.dispose();
});
Assert.equal(count_finalized(size, tc), size);
// Remove references
ref = [];
// Re-acquire data and make sure that everything has been reinialized
for (let i = 0; i < size; ++i) {
tc.acquire(i);
}
Assert.equal(count_finalized(size, tc), 0);
// Attempt to trigger finalizations, ensure that they do not take place
trigger_gc();
Assert.equal(count_finalized(size, tc), 0);
}
/**
* Check that
* - |forget| does not dispose
* - |forget| has the right content
* - finalizers are not executed after |forget|
*/
function test_executing_forget(size, tc, cleanup) {
dump("test_executing_forget " + tc.name + "\n");
let ref = [];
// Allocate |size| items with references
for (let i = 0; i < size; ++i) {
let original = tc.acquire(i);
let finalizer = ctypes.CDataFinalizer(original, tc.release);
ref.push({
original,
finalizer,
});
cleanup.add(finalizer);
Assert.ok(tc.compare(original, finalizer));
}
Assert.equal(count_finalized(size, tc), 0);
// Forget everything, making sure that we recover the original info
ref.forEach(function compare_original_to_recovered(v) {
let original = v.original;
let recovered = v.finalizer.forget();
// Note: Cannot use do_check_eq on Uint64 et al.
Assert.ok(tc.compare(original, recovered));
Assert.equal(original.constructor, recovered.constructor);
});
// Also make sure that we have not performed any clean up
Assert.equal(count_finalized(size, tc), 0);
// Remove references
ref = [];
// Attempt to trigger finalizations, ensure that they have no effect
trigger_gc();
Assert.equal(count_finalized(size, tc), 0);
}
/**
* Check that finalizers are not executed
*/
function test_do_not_execute_finalizers_on_referenced_stuff(size, tc, cleanup) {
dump("test_do_not_execute_finalizers_on_referenced_stuff " + tc.name + "\n");
let ref = [];
// Allocate |size| items without references
for (let i = 0; i < size; ++i) {
let value = ctypes.CDataFinalizer(tc.acquire(i), tc.release);
cleanup.add(value);
ref.push(value);
}
trigger_gc(); // This might trigger some finalizations, but it should not
// Check that _nothing_ has been finalized
Assert.equal(count_finalized(size, tc), 0);
}