// 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()