414 lines
11 KiB
Plaintext
414 lines
11 KiB
Plaintext
// blob_test.ce
|
|
var Blob = use('blob');
|
|
var os = use('os');
|
|
|
|
log.console("=== Blob Module Test Suite ===\n");
|
|
|
|
var passed = 0;
|
|
var failed = 0;
|
|
|
|
function test(name, fn) {
|
|
try {
|
|
fn();
|
|
passed++;
|
|
log.console("✓ " + name);
|
|
} catch (e) {
|
|
failed++;
|
|
log.console("✗ " + name + ": " + e);
|
|
}
|
|
}
|
|
|
|
function assert(condition, message) {
|
|
if (!condition) {
|
|
throw new Error(message || "Assertion failed");
|
|
}
|
|
}
|
|
|
|
function assertEqual(actual, expected, message) {
|
|
if (actual !== expected) {
|
|
throw new Error(message || "Expected " + expected + ", got " + actual);
|
|
}
|
|
}
|
|
|
|
// Test 1: Empty blob creation
|
|
test("Create empty blob", function() {
|
|
var b = new Blob();
|
|
assertEqual(b.length, 0, "Empty blob should have length 0");
|
|
});
|
|
|
|
// Test 2: Blob with capacity
|
|
test("Create blob with capacity", function() {
|
|
var b = new Blob(100);
|
|
assertEqual(b.length, 0, "New blob with capacity should still have length 0");
|
|
});
|
|
|
|
// Test 3: Write and read single bit
|
|
test("Write and read single bit", function() {
|
|
var b = new Blob();
|
|
b.write_bit(true);
|
|
b.write_bit(false);
|
|
b.write_bit(1);
|
|
b.write_bit(0);
|
|
assertEqual(b.length, 4, "Should have 4 bits after writing");
|
|
|
|
stone(b); // Make it stone to read
|
|
assertEqual(b.read_logical(0), true, "First bit should be true");
|
|
assertEqual(b.read_logical(1), false, "Second bit should be false");
|
|
assertEqual(b.read_logical(2), true, "Third bit should be true (1)");
|
|
assertEqual(b.read_logical(3), false, "Fourth bit should be false (0)");
|
|
});
|
|
|
|
// Test 4: Out of range read throws error
|
|
test("Out of range read throws error", function() {
|
|
var b = new Blob();
|
|
b.write_bit(true);
|
|
stone(b);
|
|
|
|
var threw = false;
|
|
try {
|
|
b.read_logical(100); // Out of range
|
|
} catch (e) {
|
|
threw = true;
|
|
}
|
|
assert(threw, "Out of range read should throw");
|
|
|
|
threw = false;
|
|
try {
|
|
b.read_logical(-1); // Negative index
|
|
} catch (e) {
|
|
threw = true;
|
|
}
|
|
assert(threw, "Negative index read should throw");
|
|
});
|
|
|
|
// Test 5: Write and read numbers
|
|
test("Write and read numbers", function() {
|
|
var b = new Blob();
|
|
b.write_number(3.14159);
|
|
b.write_number(-42);
|
|
b.write_number(0);
|
|
b.write_number(1e20);
|
|
stone(b);
|
|
|
|
// Read back the numbers
|
|
assertEqual(b.read_number(0), 3.14159, "First number should match");
|
|
assertEqual(b.read_number(64), -42, "Second number should match");
|
|
assertEqual(b.read_number(128), 0, "Third number should match");
|
|
assertEqual(b.read_number(192), 1e20, "Fourth number should match");
|
|
});
|
|
|
|
// Test 6: Write and read text
|
|
test("Write and read text", function() {
|
|
var b = new Blob();
|
|
b.write_text("Hello");
|
|
b.write_text("World");
|
|
b.write_text("🎉"); // Unicode test
|
|
stone(b);
|
|
|
|
assertEqual(b.read_text(0), "Hello", "First text should match");
|
|
// Note: We need to know bit positions to read subsequent strings
|
|
});
|
|
|
|
// Test 7: Write and read blobs
|
|
test("Write and read blobs", function() {
|
|
var b1 = new Blob();
|
|
b1.write_bit(true);
|
|
b1.write_bit(false);
|
|
b1.write_bit(true);
|
|
|
|
var b2 = new Blob(10); // Give it some initial capacity
|
|
b2.write_blob(b1);
|
|
b2.write_bit(false);
|
|
assertEqual(b2.length, 4, "Combined blob should have 4 bits");
|
|
|
|
stone(b2);
|
|
assertEqual(b2.read_logical(0), true);
|
|
assertEqual(b2.read_logical(1), false);
|
|
assertEqual(b2.read_logical(2), true);
|
|
assertEqual(b2.read_logical(3), false);
|
|
});
|
|
|
|
// Test 8: Copy constructor
|
|
test("Blob copy constructor", function() {
|
|
var b1 = new Blob();
|
|
b1.write_bit(true);
|
|
b1.write_bit(false);
|
|
b1.write_bit(true);
|
|
b1.write_bit(true);
|
|
stone(b1);
|
|
|
|
var b2 = new Blob(b1);
|
|
stone(b2); // Need to stone the copy before reading
|
|
assertEqual(b2.length, 4, "Copied blob should have same length");
|
|
assertEqual(b2.read_logical(0), true);
|
|
assertEqual(b2.read_logical(3), true);
|
|
});
|
|
|
|
// Test 9: Partial copy constructor
|
|
test("Blob partial copy constructor", function() {
|
|
var b1 = new Blob();
|
|
for (var i = 0; i < 10; i++) {
|
|
b1.write_bit(i % 2 === 0);
|
|
}
|
|
stone(b1);
|
|
|
|
var b2 = new Blob(b1, 2, 7); // Copy bits 2-6 (5 bits)
|
|
stone(b2); // Need to stone the copy before reading
|
|
assertEqual(b2.length, 5, "Partial copy should have 5 bits");
|
|
assertEqual(b2.read_logical(0), true); // bit 2 of original
|
|
assertEqual(b2.read_logical(2), true); // bit 4 of original
|
|
});
|
|
|
|
// Test 10: Blob with fill
|
|
test("Create blob with fill", function() {
|
|
var b1 = new Blob(8, true); // 8 bits all set to 1
|
|
var b2 = new Blob(8, false); // 8 bits all set to 0
|
|
|
|
stone(b1);
|
|
stone(b2);
|
|
|
|
for (var i = 0; i < 8; i++) {
|
|
assertEqual(b1.read_logical(i), true, "Bit " + i + " should be true");
|
|
assertEqual(b2.read_logical(i), false, "Bit " + i + " should be false");
|
|
}
|
|
});
|
|
|
|
// Test 11: Blob with random function
|
|
test("Create blob with random function", function() {
|
|
var sequence = [true, false, true, true, false];
|
|
var index = 0;
|
|
|
|
var b = new Blob(5, function() {
|
|
return sequence[index++] ? 1 : 0;
|
|
});
|
|
|
|
stone(b);
|
|
for (var i = 0; i < 5; i++) {
|
|
assertEqual(b.read_logical(i), sequence[i], "Bit " + i + " should match sequence");
|
|
}
|
|
});
|
|
|
|
// Test 12: Write pad and pad check
|
|
test("Write pad and check padding", function() {
|
|
var b = new Blob();
|
|
b.write_bit(true);
|
|
b.write_bit(false);
|
|
b.write_bit(true);
|
|
b.write_pad(8); // Pad to 8-bit boundary
|
|
|
|
assertEqual(b.length, 8, "Should be padded to 8 bits");
|
|
stone(b);
|
|
|
|
assert(b['pad?'](3, 8), "Should detect valid padding at position 3");
|
|
assert(!b['pad?'](2, 8), "Should detect invalid padding at position 2");
|
|
});
|
|
|
|
// Test 13: read_blob method
|
|
test("Read blob from stone blob", function() {
|
|
var b1 = new Blob();
|
|
for (var i = 0; i < 16; i++) {
|
|
b1.write_bit(i % 3 === 0);
|
|
}
|
|
stone(b1);
|
|
|
|
var b2 = b1.read_blob(4, 12); // Read bits 4-11 (8 bits)
|
|
stone(b2); // Need to stone the read blob before reading from it
|
|
assertEqual(b2.length, 8, "Read blob should have 8 bits");
|
|
|
|
// Check the pattern
|
|
assertEqual(b2.read_logical(2), true); // Original bit 6 (6 % 3 === 0)
|
|
assertEqual(b2.read_logical(5), true); // Original bit 9 (9 % 3 === 0)
|
|
});
|
|
|
|
// Test 14: Stone immutability
|
|
test("Stone blob is immutable", function() {
|
|
var b = new Blob();
|
|
b.write_bit(true);
|
|
stone(b);
|
|
|
|
var threw = false;
|
|
try {
|
|
b.write_bit(false); // Should fail on stone blob
|
|
} catch (e) {
|
|
threw = true;
|
|
}
|
|
assert(threw, "Writing to stone blob should throw error");
|
|
});
|
|
|
|
// Test 15: Multiple stone calls and stone.p check
|
|
test("Multiple stone calls are safe and stone.p works", function() {
|
|
var b = new Blob();
|
|
b.write_bit(true);
|
|
assert(!stone.p(b), "Blob should not be a stone before stone() call");
|
|
stone(b);
|
|
assert(stone.p(b), "Blob should be a stone after stone() call");
|
|
stone(b); // Should be safe to call again
|
|
assertEqual(b.read_logical(0), true, "Blob data should remain intact");
|
|
|
|
// Verify blob.stone is not available
|
|
assert(b.stone === undefined, "blob.stone should not be available as a method");
|
|
});
|
|
|
|
// Test 16: Invalid constructor arguments
|
|
test("Invalid constructor arguments", function() {
|
|
var threw = false;
|
|
try {
|
|
var b = new Blob("invalid");
|
|
} catch (e) {
|
|
threw = true;
|
|
}
|
|
assert(threw, "Invalid constructor arguments should throw");
|
|
});
|
|
|
|
// Test 17: Write invalid bit values
|
|
test("Write bit validation", function() {
|
|
var b = new Blob();
|
|
b.write_bit(0);
|
|
b.write_bit(1);
|
|
|
|
var threw = false;
|
|
try {
|
|
b.write_bit(2); // Should only accept 0, 1, true, false
|
|
} catch (e) {
|
|
threw = true;
|
|
}
|
|
assert(threw, "write_bit with value 2 should throw");
|
|
});
|
|
|
|
// Test 18: Complex data round-trip
|
|
test("Complex data round-trip", function() {
|
|
var b = new Blob();
|
|
|
|
// Write mixed data
|
|
b.write_text("Test");
|
|
b.write_number(123.456);
|
|
b.write_bit(true);
|
|
b.write_bit(false);
|
|
b.write_number(-999.999);
|
|
|
|
var originalLength = b.length;
|
|
stone(b);
|
|
|
|
// Verify we can create a copy
|
|
var b2 = new Blob(b);
|
|
stone(b2); // Need to stone the copy before reading
|
|
assertEqual(b2.length, originalLength, "Copy should have same length");
|
|
assertEqual(b2.read_text(0), "Test", "First text should match");
|
|
});
|
|
|
|
// Test 19: Zero capacity blob
|
|
test("Zero capacity blob", function() {
|
|
var b = new Blob(0);
|
|
assertEqual(b.length, 0, "Zero capacity blob should have length 0");
|
|
b.write_bit(true); // Should auto-expand
|
|
assertEqual(b.length, 1, "Should expand when writing");
|
|
});
|
|
|
|
// Test 20: Large blob handling
|
|
test("Large blob handling", function() {
|
|
var b = new Blob();
|
|
var testSize = 1000;
|
|
|
|
// Write many bits
|
|
for (var i = 0; i < testSize; i++) {
|
|
b.write_bit(i % 7 === 0);
|
|
}
|
|
|
|
assertEqual(b.length, testSize, "Should have " + testSize + " bits");
|
|
stone(b);
|
|
|
|
// Verify pattern
|
|
assertEqual(b.read_logical(0), true, "Bit 0 should be true");
|
|
assertEqual(b.read_logical(7), true, "Bit 7 should be true");
|
|
assertEqual(b.read_logical(14), true, "Bit 14 should be true");
|
|
assertEqual(b.read_logical(15), false, "Bit 15 should be false");
|
|
});
|
|
|
|
// Test 21: Non-stone blob read methods should throw
|
|
test("Non-stone blob read methods should throw", function() {
|
|
var b = new Blob();
|
|
b.write_bit(true);
|
|
b.write_number(42);
|
|
b.write_text("test");
|
|
|
|
// Try to read without stoning - should throw
|
|
var threw = false;
|
|
try {
|
|
b.read_logical(0);
|
|
} catch (e) {
|
|
threw = true;
|
|
}
|
|
assert(threw, "read_logical on non-stone blob should throw");
|
|
|
|
threw = false;
|
|
try {
|
|
b.read_number(0);
|
|
} catch (e) {
|
|
threw = true;
|
|
}
|
|
assert(threw, "read_number on non-stone blob should throw");
|
|
|
|
threw = false;
|
|
try {
|
|
b.read_text(0);
|
|
} catch (e) {
|
|
threw = true;
|
|
}
|
|
assert(threw, "read_text on non-stone blob should throw");
|
|
|
|
threw = false;
|
|
try {
|
|
b.read_blob(0, 10);
|
|
} catch (e) {
|
|
threw = true;
|
|
}
|
|
assert(threw, "read_blob on non-stone blob should throw");
|
|
|
|
threw = false;
|
|
try {
|
|
b['pad?'](0, 8);
|
|
} catch (e) {
|
|
threw = true;
|
|
}
|
|
assert(threw, "pad? on non-stone blob should throw");
|
|
});
|
|
|
|
// Test 22: Empty text write and read
|
|
test("Empty text write and read", function() {
|
|
var b = new Blob();
|
|
b.write_text("");
|
|
stone(b);
|
|
assertEqual(b.read_text(0), "", "Empty string should round-trip");
|
|
});
|
|
|
|
// Test 23: Blob error on invalid read positions
|
|
test("Invalid read positions", function() {
|
|
var b = new Blob();
|
|
b.write_number(42);
|
|
stone(b);
|
|
|
|
var threw = false;
|
|
try {
|
|
b.read_number(-10); // Negative position
|
|
} catch (e) {
|
|
threw = true;
|
|
}
|
|
assert(threw, "Negative position should throw");
|
|
|
|
threw = false;
|
|
try {
|
|
b.read_number(1000); // Beyond blob length
|
|
} catch (e) {
|
|
threw = true;
|
|
}
|
|
assert(threw, "Position beyond length should throw");
|
|
});
|
|
|
|
// Print summary
|
|
log.console("\n=== Test Summary ===");
|
|
log.console("Total tests: " + (passed + failed));
|
|
log.console("Passed: " + passed);
|
|
log.console("Failed: " + failed);
|
|
log.console("\nOverall: " + (failed === 0 ? "PASSED" : "FAILED"));
|
|
|
|
$_.stop() |