blob: c80ba0ec5df235b281afc0fbea4955fadd1ab998 [file] [log] [blame] [edit]
function assert(condition, message) {
if (!condition)
throw new Error(message || "Assertion failed");
}
// Build a rope string that exceeds the 0x128 (296) length threshold.
function makeLongRope() {
let a = "a".repeat(200);
let b = "b".repeat(200);
return a + b;
}
// Basic: rope includes with single character.
function testBasicRopeIncludes() {
let rope = makeLongRope();
assert(rope.includes("a") === true, "'a' should be found");
assert(rope.includes("b") === true, "'b' should be found");
assert(rope.includes("z") === false, "'z' should not be found");
}
// startPosition argument.
function testRopeIncludesWithPosition() {
let rope = makeLongRope();
assert(rope.includes("a", 100) === true, "'a' from 100 should be found");
assert(rope.includes("a", 200) === false, "'a' from 200 should not be found");
assert(rope.includes("b", 300) === true, "'b' from 300 should be found");
assert(rope.includes("b", 400) === false, "'b' from 400 should not be found");
}
// Deep rope (multi-level concatenation).
function testDeepRope() {
let s = "x".repeat(100);
for (let i = 0; i < 10; i++)
s = s + "y".repeat(30);
// s is "x"*100 + "y"*300 = 400 chars, exceeds threshold
assert(s.includes("x") === true, "'x' should be found");
assert(s.includes("y") === true, "'y' should be found");
assert(s.includes("z") === false, "'z' should not be found");
}
// Edge: character at fiber boundary.
function testFiberBoundary() {
let a = "a".repeat(150);
let b = "b" + "a".repeat(149);
let rope = a + b; // 300 chars, 'b' is at position 150
assert(rope.includes("b") === true, "'b' at fiber boundary should be found");
}
// Three-fiber rope.
function testThreeFiberRope() {
let a = "a".repeat(100);
let b = "b".repeat(100);
let c = "c".repeat(100);
let rope = a + b + c; // JSC may create a 3-fiber rope
assert(rope.includes("a") === true);
assert(rope.includes("b") === true);
assert(rope.includes("c") === true);
assert(rope.includes("c", 250) === true);
assert(rope.includes("z") === false);
}
// Last character.
function testLastChar() {
let a = "a".repeat(299);
let rope = a + "z"; // 300 chars
assert(rope.includes("z") === true);
}
// Ensure short ropes still work (they take the normal path).
function testShortRope() {
let rope = "hello" + " world";
assert(rope.includes("w") === true);
assert(rope.includes("z") === false);
}
// Substring rope as root: slice() creates a substring rope.
function testSubstringRopeRoot() {
let base = "a".repeat(200) + "b".repeat(200);
// Force resolve so slice() creates a substring rope over a resolved base.
base.charCodeAt(0);
let sub = base.slice(100, 350); // 250 chars, substring rope
assert(sub.includes("a") === true, "sub: 'a' should be found");
assert(sub.includes("b") === true, "sub: 'b' should be found");
assert(sub.includes("z") === false, "sub: 'z' should not be found");
assert(sub.includes("b", 150) === true, "sub: 'b' from 150 should be found");
}
// Substring rope as fiber: concat a substring with another string.
function testSubstringRopeFiber() {
let base = "x".repeat(400);
base.charCodeAt(0);
let sub = base.slice(0, 200); // substring rope, 200 chars of 'x'
let rope = sub + "y".repeat(200); // fiber0=substring rope, fiber1=resolved
assert(rope.includes("x") === true, "fiber sub: 'x' should be found");
assert(rope.includes("y") === true, "fiber sub: 'y' should be found");
assert(rope.includes("z") === false, "fiber sub: 'z' should not be found");
assert(rope.includes("x", 199) === true, "fiber sub: 'x' at 199 should be found");
assert(rope.includes("x", 200) === false, "fiber sub: no 'x' from 200");
}
// Nested rope with startPosition: ensure startPosition is not regressed
// when tryFindOneChar bails out on a non-substring rope fiber.
function makeNestedRope() {
let partA = "a".repeat(50) + "z" + "a".repeat(449); // 500 chars, 'z' at index 50
let partB = "a".repeat(500); // 500 chars
let inner = partA + partB; // 1000 chars, rope
let partC = "b".repeat(200); // 200 chars
return inner + partC; // 1200 chars, fiber0 = inner (rope)
}
function testNestedRopeStartPosition() {
// Each assertion uses a fresh rope because the first includes call
// resolves the rope, hiding the bug for subsequent calls.
assert(makeNestedRope().includes("z", 100) === false, "nested rope: includes('z', 100) should be false");
assert(makeNestedRope().includes("z", 51) === false, "nested rope: includes('z', 51) should be false");
assert(makeNestedRope().includes("z", 50) === true, "nested rope: includes('z', 50) should be true");
assert(makeNestedRope().includes("z") === true, "nested rope: includes('z') should be true");
}
for (let i = 0; i < testLoopCount; i++) {
testBasicRopeIncludes();
testRopeIncludesWithPosition();
testDeepRope();
testFiberBoundary();
testThreeFiberRope();
testLastChar();
testShortRope();
testSubstringRopeRoot();
testSubstringRopeFiber();
testNestedRopeStartPosition();
}