338 lines
10 KiB
Plaintext
338 lines
10 KiB
Plaintext
// blob_test.cm
|
|
var Blob = use('blob');
|
|
var os = use('os');
|
|
|
|
function assert(condition, message) {
|
|
if (!condition) {
|
|
return message || "Assertion failed"
|
|
}
|
|
}
|
|
|
|
function assertEqual(actual, expected, message) {
|
|
if (actual != expected) {
|
|
return message || "Expected " + expected + ", got " + actual
|
|
}
|
|
}
|
|
|
|
function should_disrupt(fn) {
|
|
var caught = false
|
|
var wrapper = function() { fn() } disruption { caught = true }
|
|
wrapper()
|
|
return caught
|
|
}
|
|
|
|
return {
|
|
test_create_empty_blob: function() {
|
|
var b = Blob();
|
|
return assertEqual(length(b), 0, "Empty blob should have length 0")
|
|
},
|
|
|
|
test_create_blob_with_capacity: function() {
|
|
var b = Blob(100);
|
|
return assertEqual(length(b), 0, "New blob with capacity should still have length 0")
|
|
},
|
|
|
|
test_write_and_read_single_bit: function() {
|
|
var b = Blob();
|
|
b.write_bit(true);
|
|
b.write_bit(false);
|
|
b.write_bit(1);
|
|
b.write_bit(0);
|
|
var r = assertEqual(length(b), 4, "Should have 4 bits after writing")
|
|
if (r) return r
|
|
|
|
stone(b);
|
|
r = assertEqual(b.read_logical(0), true, "First bit should be true")
|
|
if (r) return r
|
|
r = assertEqual(b.read_logical(1), false, "Second bit should be false")
|
|
if (r) return r
|
|
r = assertEqual(b.read_logical(2), true, "Third bit should be true (1)")
|
|
if (r) return r
|
|
return assertEqual(b.read_logical(3), false, "Fourth bit should be false (0)")
|
|
},
|
|
|
|
test_out_of_range_read_throws_error: function() {
|
|
var b = Blob();
|
|
b.write_bit(true);
|
|
stone(b);
|
|
|
|
if (!should_disrupt(function() { b.read_logical(100) }))
|
|
return "Out of range read should disrupt"
|
|
|
|
if (!should_disrupt(function() { b.read_logical(-1) }))
|
|
return "Negative index read should disrupt"
|
|
},
|
|
|
|
test_write_and_read_numbers: function() {
|
|
var b = Blob();
|
|
b.write_number(3.14159);
|
|
b.write_number(-42);
|
|
b.write_number(0);
|
|
b.write_number(1e20);
|
|
stone(b);
|
|
|
|
var r = assertEqual(b.read_number(0), 3.14159, "First number should match")
|
|
if (r) return r
|
|
r = assertEqual(b.read_number(64), -42, "Second number should match")
|
|
if (r) return r
|
|
r = assertEqual(b.read_number(128), 0, "Third number should match")
|
|
if (r) return r
|
|
return assertEqual(b.read_number(192), 1e20, "Fourth number should match")
|
|
},
|
|
|
|
test_write_and_read_text: function() {
|
|
var b = Blob();
|
|
b.write_text("Hello");
|
|
b.write_text("World");
|
|
b.write_text("🎉");
|
|
stone(b);
|
|
|
|
return assertEqual(b.read_text(0), "Hello", "First text should match")
|
|
},
|
|
|
|
test_write_and_read_blobs: function() {
|
|
var b1 = Blob();
|
|
b1.write_bit(true);
|
|
b1.write_bit(false);
|
|
b1.write_bit(true);
|
|
|
|
var b2 = Blob(10);
|
|
b2.write_blob(b1);
|
|
b2.write_bit(false);
|
|
var r = assertEqual(length(b2), 4, "Combined blob should have 4 bits")
|
|
if (r) return r
|
|
|
|
stone(b2);
|
|
r = assertEqual(b2.read_logical(0), true)
|
|
if (r) return r
|
|
r = assertEqual(b2.read_logical(1), false)
|
|
if (r) return r
|
|
r = assertEqual(b2.read_logical(2), true)
|
|
if (r) return r
|
|
return assertEqual(b2.read_logical(3), false)
|
|
},
|
|
|
|
test_blob_copy_constructor: function() {
|
|
var b1 = Blob();
|
|
b1.write_bit(true);
|
|
b1.write_bit(false);
|
|
b1.write_bit(true);
|
|
b1.write_bit(true);
|
|
stone(b1);
|
|
|
|
var b2 = Blob(b1);
|
|
stone(b2);
|
|
var r = assertEqual(length(b2), 4, "Copied blob should have same length")
|
|
if (r) return r
|
|
r = assertEqual(b2.read_logical(0), true)
|
|
if (r) return r
|
|
return assertEqual(b2.read_logical(3), true)
|
|
},
|
|
|
|
test_blob_partial_copy_constructor: function() {
|
|
var b1 = Blob();
|
|
for (var i = 0; i < 10; i++) {
|
|
b1.write_bit(i % 2 == 0);
|
|
}
|
|
stone(b1);
|
|
|
|
var b2 = Blob(b1, 2, 7);
|
|
stone(b2);
|
|
var r = assertEqual(length(b2), 5, "Partial copy should have 5 bits")
|
|
if (r) return r
|
|
r = assertEqual(b2.read_logical(0), true)
|
|
if (r) return r
|
|
return assertEqual(b2.read_logical(2), true)
|
|
},
|
|
|
|
test_create_blob_with_fill: function() {
|
|
var b1 = Blob(8, true);
|
|
var b2 = Blob(8, false);
|
|
|
|
stone(b1);
|
|
stone(b2);
|
|
|
|
for (var i = 0; i < 8; i++) {
|
|
var r = assertEqual(b1.read_logical(i), true, "Bit " + i + " should be true")
|
|
if (r) return r
|
|
r = assertEqual(b2.read_logical(i), false, "Bit " + i + " should be false")
|
|
if (r) return r
|
|
}
|
|
},
|
|
|
|
test_create_blob_with_random_function: function() {
|
|
var sequence = [true, false, true, true, false];
|
|
var index = 0;
|
|
|
|
var b = Blob(5, function() {
|
|
return sequence[index++] ? 1 : 0;
|
|
});
|
|
|
|
stone(b);
|
|
for (var i = 0; i < 5; i++) {
|
|
var r = assertEqual(b.read_logical(i), sequence[i], "Bit " + i + " should match sequence")
|
|
if (r) return r
|
|
}
|
|
},
|
|
|
|
test_write_pad_and_check_padding: function() {
|
|
var b = Blob();
|
|
b.write_bit(true);
|
|
b.write_bit(false);
|
|
b.write_bit(true);
|
|
b.write_pad(8);
|
|
|
|
var r = assertEqual(length(b), 8, "Should be padded to 8 bits")
|
|
if (r) return r
|
|
stone(b);
|
|
|
|
r = assert(b['pad?'](3, 8), "Should detect valid padding at position 3")
|
|
if (r) return r
|
|
return assert(!b['pad?'](2, 8), "Should detect invalid padding at position 2")
|
|
},
|
|
|
|
test_read_blob_from_stone_blob: function() {
|
|
var b1 = Blob();
|
|
for (var i = 0; i < 16; i++) {
|
|
b1.write_bit(i % 3 == 0);
|
|
}
|
|
stone(b1);
|
|
|
|
var b2 = b1.read_blob(4, 12);
|
|
stone(b2);
|
|
var r = assertEqual(length(b2), 8, "Read blob should have 8 bits")
|
|
if (r) return r
|
|
|
|
r = assertEqual(b2.read_logical(2), true)
|
|
if (r) return r
|
|
return assertEqual(b2.read_logical(5), true)
|
|
},
|
|
|
|
test_stone_blob_is_immutable: function() {
|
|
var b = Blob();
|
|
b.write_bit(true);
|
|
stone(b);
|
|
|
|
if (!should_disrupt(function() { b.write_bit(false) }))
|
|
return "Writing to stone blob should disrupt"
|
|
},
|
|
|
|
test_multiple_stone_calls_are_safe: function() {
|
|
var b = Blob();
|
|
b.write_bit(true);
|
|
var r = assert(!stone.p(b), "Blob should not be a stone before stone() call")
|
|
if (r) return r
|
|
stone(b);
|
|
r = assert(stone.p(b), "Blob should be a stone after stone() call")
|
|
if (r) return r
|
|
stone(b);
|
|
r = assertEqual(b.read_logical(0), true, "Blob data should remain intact")
|
|
if (r) return r
|
|
|
|
return assert(b.stone == null, "blob.stone should not be available as a method")
|
|
},
|
|
|
|
test_invalid_constructor_arguments: function() {
|
|
if (!should_disrupt(function() { Blob("invalid") }))
|
|
return "Invalid constructor arguments should disrupt"
|
|
},
|
|
|
|
test_write_bit_validation: function() {
|
|
var b = Blob();
|
|
b.write_bit(0);
|
|
b.write_bit(1);
|
|
|
|
if (!should_disrupt(function() { b.write_bit(2) }))
|
|
return "write_bit with value 2 should disrupt"
|
|
},
|
|
|
|
test_complex_data_round_trip: function() {
|
|
var b = Blob();
|
|
|
|
b.write_text("Test");
|
|
b.write_number(123.456);
|
|
b.write_bit(true);
|
|
b.write_bit(false);
|
|
b.write_number(-999.999);
|
|
|
|
var originalLength = length(b);
|
|
stone(b);
|
|
|
|
var b2 = Blob(b);
|
|
stone(b2);
|
|
var r = assertEqual(length(b2), originalLength, "Copy should have same length")
|
|
if (r) return r
|
|
return assertEqual(b2.read_text(0), "Test", "First text should match")
|
|
},
|
|
|
|
test_zero_capacity_blob: function() {
|
|
var b = Blob(0);
|
|
var r = assertEqual(length(b), 0, "Zero capacity blob should have length 0")
|
|
if (r) return r
|
|
b.write_bit(true);
|
|
return assertEqual(length(b), 1, "Should expand when writing")
|
|
},
|
|
|
|
test_large_blob_handling: function() {
|
|
var b = Blob();
|
|
var testSize = 1000;
|
|
|
|
for (var i = 0; i < testSize; i++) {
|
|
b.write_bit(i % 7 == 0);
|
|
}
|
|
|
|
var r = assertEqual(length(b), testSize, "Should have " + testSize + " bits")
|
|
if (r) return r
|
|
stone(b);
|
|
|
|
r = assertEqual(b.read_logical(0), true, "Bit 0 should be true")
|
|
if (r) return r
|
|
r = assertEqual(b.read_logical(7), true, "Bit 7 should be true")
|
|
if (r) return r
|
|
r = assertEqual(b.read_logical(14), true, "Bit 14 should be true")
|
|
if (r) return r
|
|
return assertEqual(b.read_logical(15), false, "Bit 15 should be false")
|
|
},
|
|
|
|
test_non_stone_blob_read_methods_should_throw: function() {
|
|
var b = Blob();
|
|
b.write_bit(true);
|
|
b.write_number(42);
|
|
b.write_text("test");
|
|
|
|
if (!should_disrupt(function() { b.read_logical(0) }))
|
|
return "read_logical on non-stone blob should disrupt"
|
|
|
|
if (!should_disrupt(function() { b.read_number(0) }))
|
|
return "read_number on non-stone blob should disrupt"
|
|
|
|
if (!should_disrupt(function() { b.read_text(0) }))
|
|
return "read_text on non-stone blob should disrupt"
|
|
|
|
if (!should_disrupt(function() { b.read_blob(0, 10) }))
|
|
return "read_blob on non-stone blob should disrupt"
|
|
|
|
if (!should_disrupt(function() { b['pad?'](0, 8) }))
|
|
return "pad? on non-stone blob should disrupt"
|
|
},
|
|
|
|
test_empty_text_write_and_read: function() {
|
|
var b = Blob();
|
|
b.write_text("");
|
|
stone(b);
|
|
return assertEqual(b.read_text(0), "", "Empty string should round-trip")
|
|
},
|
|
|
|
test_invalid_read_positions: function() {
|
|
var b = Blob();
|
|
b.write_number(42);
|
|
stone(b);
|
|
|
|
if (!should_disrupt(function() { b.read_number(-10) }))
|
|
return "Negative position should disrupt"
|
|
|
|
if (!should_disrupt(function() { b.read_number(1000) }))
|
|
return "Position beyond length should disrupt"
|
|
}
|
|
}
|