ladybird/Tests/LibJS/Runtime/3rdparty/v8/regexp-duplicate-named-groups.js
aplefull b3bd8884ae Tests+Meta: Reimport V8 regexp tests
This commit fixes assertEquals, assertThrows/assertDoesNotThrow in
compatibility shim - we were failing some tests purely because of a
bad shim.

Two test files are moved from XFAIL_TESTS to SKIP_FILES:
1. es6/regexp-tostring.js: checks for V8-specific error message wording
2. es6/unicode-regexp-ignore-case-noi18n.js: tests V8's non-i18n build
    behavior, it doesn't case-fold non-ASCII characters, but we do it
    correctly.

The regexp-unicode-sets.js test is now included and passes.

All files that were previously in XFAIL_TESTS and SKIP_TESTS are now
passing, so both lists are cleared.
2026-05-26 10:00:22 +02:00

254 lines
10 KiB
JavaScript

// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// V8 assertion compatibility shim for Ladybird's test-js harness
function assertEquals(expected, actual, msg) {
if (Array.isArray(expected) && Array.isArray(actual)) {
expect(actual).toEqual(expected);
} else if (expected instanceof RegExp && actual instanceof RegExp) {
expect(actual.source).toBe(expected.source);
expect(actual.flags).toBe(expected.flags);
} else if (expected !== null && typeof expected === "object" && actual !== null && typeof actual === "object") {
expect(actual).toEqual(expected);
} else {
expect(actual).toBe(expected);
}
}
function assertTrue(val, msg) {
expect(val).toBeTrue();
}
function assertFalse(val, msg) {
expect(val).toBeFalse();
}
function assertNull(val, msg) {
expect(val).toBeNull();
}
function assertNotNull(val, msg) {
expect(val).not.toBeNull();
}
function assertThrows(fn, type_opt, msg_opt) {
if (typeof fn === "string") {
expect(() => eval(fn)).toThrow();
} else {
expect(fn).toThrow();
}
}
function assertDoesNotThrow(fn, msg) {
if (typeof fn === "string") {
eval(fn);
} else {
fn();
}
}
function assertInstanceof(val, type, msg) {
expect(val instanceof type).toBeTrue();
}
function assertUnreachable(msg) {
expect().fail("unreachable" + (msg ? ": " + msg : ""));
}
function assertEarlyError(code) {
assertThrows(() => new Function(code));
}
function assertThrowsAtRuntime(code, type_opt) {
const f = new Function(code);
assertThrows(f, type_opt);
}
function assertArrayEquals(expected, actual) {
expect(actual).toEqual(expected);
}
test("regexp-duplicate-named-groups", () => {
assertEarlyError("/(?<a>.)(?<a>.)/");
assertEarlyError("/((?<a>.)(?<a>.))/");
assertEarlyError("/(?<a>.)((?<a>.))/");
assertEarlyError("/((?<a>.))(?<a>.)/");
assertEarlyError("/(?<a>(?<a>.)|.)/");
assertEarlyError("/(?<a>.|(?<b>.(?<b>.)|.))/");
assertEarlyError("/(?<a>.)(?:.|(?<a>.)/");
assertEarlyError("/(?<a>.)(?:.|(?<a>.)/");
assertEarlyError("/(?<a>.)(?:(.)|(?<a>.)/");
assertEarlyError("/(?<a>.)(?:(?:.)|(?<a>.)/");
assertEarlyError("/(?<a>.)((?<a>.)|(?<b>.))/");
assertEarlyError("/(?<b>.)((?<a>.)|(?<b>.))/");
assertEarlyError("/((?<a>.)|(?<b>.))(?<a>.)/");
assertEarlyError("/((?<a>.)|(?<b>.))(?<b>.)/");
assertEarlyError("/((?<a>.)|((?<b>.)|(?<c>.))(?<b>.)/");
assertEarlyError("/((?<a>.)|((?<b>.)|(?<c>.))(?<c>.)/");
assertEarlyError("/x(?<a>.)((((?<a>.)|(?<a>.))|(?<a>.)|(?<a>.))|(?<a>.))|(?<a>.)y/");
assertEarlyError("/x((?<a>.)(((?<a>.)|(?<a>.))|(?<a>.)|(?<a>.))|(?<a>.))|(?<a>.)y/");
assertEarlyError("/x(((?<a>.)((?<a>.)|(?<a>.))|(?<a>.)|(?<a>.))|(?<a>.))|(?<a>.)y/");
assertEarlyError("/x((((?<a>.)(?<a>.)|(?<a>.))|(?<a>.)|(?<a>.))|(?<a>.))|(?<a>.)y/");
assertEarlyError("/x(?<a>.)|((?<a>.)|(?<a>.)|((?<a>.)|((?<a>.)|(?<a>.)(?<a>.))))y/");
assertEarlyError("/x(?<a>.)|((?<a>.)|(?<a>.)|((?<a>.)|((?<a>.)|(?<a>.))(?<a.)))y/");
assertEarlyError("/x(?<a>.)|((?<a>.)|(?<a>.)|((?<a>.)|((?<a>.)|(?<a>.)))(?<a>.))y/");
assertEarlyError("/x(?<a>.)|((?<a>.)|(?<a>.)|((?<a>.)|((?<a>.)|(?<a>.))))(?<a>.)y/");
assertEarlyError("/(?<a>.)|(?<b>.)(?:(?<b>.)|(?<d>.)(?:(?<e>.)|(?<f>.)))/");
assertEarlyError("/(?<a>.)|(?<b>.)(?:(?<c>.)|(?<b>.)(?:(?<e>.)|(?<f>.)))/");
assertEarlyError("/(?<a>.)|(?<b>.)(?:(?<c>.)|(?<d>.)(?:(?<b>.)|(?<f>.)))/");
assertEarlyError("/(?<a>.)|(?<b>.)(?:(?<c>.)|(?<d>.)(?:(?<e>.)|(?<b>.)))/");
assertEarlyError("/(?<a>.)|(?<b>.)(?:(?<c>.)|(?<d>.)(?:(?<d>.)|(?<f>.)))/");
assertEarlyError("/(?<a>.)|(?<b>.)(?:(?<c>.)|(?<d>.)(?:(?<e>.)|(?<d>.)))/");
// Cases with valid duplicate names.
new RegExp("(?<a>.)|(?<a>.)");
new RegExp("(?<a>.)|(?<a>.)|(?<a>.)|(?<a>.)");
new RegExp("(?<a>.)|(.)|(?<a>.)");
new RegExp("(?<a>.(?<b>.))|(?<a>.)");
new RegExp("(?<a>.)|(?<a>.(?<b>.)|.)");
new RegExp("(?<a>.)|(?<b>.(?<a>.)|.)");
new RegExp("(?<a>.(?<b>.))|(?<a>.)|(?<b>.)");
new RegExp("((?<a>.)|((?<b>.)|(?<c>.))(?<a>.))");
new RegExp("((?<a>.)|(?<b>.))|(?<c>.)(?<a>.)");
new RegExp("((?<a>.)|(?<b>.))|(?<c>.)(?<b>.)");
new RegExp("(?:(?<a>.)|(?<a>.))|(?<a>.)");
new RegExp("(?<a>.)|(?:(?<a>.)|(?<a>.))");
new RegExp("x((((?<a>.)|(?<a>.))|(?<a>.)|(?<a>.))|(?<a>.))|(?<a>.)y");
new RegExp("x(?<a>.)|((?<a>.)|(?<a>.)|((?<a>.)|((?<a>.)|(?<a>.))))y");
new RegExp("(?<a>.)|(?<b>.)(?:(?<c>.)|(?<d>.)(?:(?<e>.)|(?<f>.)))");
new RegExp("(?<a>.)|(?<a>.)(?:(?<c>.)|(?<d>.)(?:(?<e>.)|(?<f>.)))");
new RegExp("(?<a>.)|(?<b>.)(?:(?<a>.)|(?<d>.)(?:(?<e>.)|(?<f>.)))");
new RegExp("(?<a>.)|(?<b>.)(?:(?<c>.)|(?<a>.)(?:(?<e>.)|(?<f>.)))");
new RegExp("(?<a>.)|(?<b>.)(?:(?<c>.)|(?<d>.)(?:(?<a>.)|(?<f>.)))");
new RegExp("(?<a>.)|(?<b>.)(?:(?<c>.)|(?<d>.)(?:(?<e>.)|(?<a>.)))");
new RegExp("(?<a>.)|(?<b>.)(?:(?<c>.)|(?<c>.)(?:(?<e>.)|(?<f>.)))");
new RegExp("(?<a>.)|(?<b>.)(?:(?<c>.)|(?<d>.)(?:(?<c>.)|(?<f>.)))");
new RegExp("(?<a>.)|(?<b>.)(?:(?<c>.)|(?<d>.)(?:(?<e>.)|(?<c>.)))");
new RegExp("(?<a>.)|(?<b>.)(?:(?<a>.)|(?<d>.)(?:(?<e>.)|(?<f>.)))");
new RegExp("(?<a>.)|(?<b>.)(?:(?<a>.)|(?<a>.)(?:(?<e>.)|(?<f>.)))");
new RegExp("(?<a>.)|(?<b>.)(?:(?<a>.)|(?<d>.)(?:(?<a>.)|(?<f>.)))");
new RegExp("(?<a>.)|(?<b>.)(?:(?<a>.)|(?<d>.)(?:(?<e>.)|(?<a>.)))");
// Test different functions with duplicate names in alteration.
assertEquals(["xxyy", undefined, "y"], /(?:(?:(?<a>x)|(?<a>y))\k<a>){2}/.exec("xxyy"));
assertEquals(
["zzyyxx", "x", undefined, undefined, undefined, undefined],
/(?:(?:(?<a>x)|(?<a>y)|(a)|(?<b>b)|(?<a>z))\k<a>){3}/.exec("xzzyyxxy")
);
assertEquals(["xxyy", undefined, "y"], "xxyy".match(/(?:(?:(?<a>x)|(?<a>y))\k<a>){2}/));
assertEquals(
["zzyyxx", "x", undefined, undefined, undefined, undefined],
"xzzyyxxy".match(/(?:(?:(?<a>x)|(?<a>y)|(a)|(?<b>b)|(?<a>z))\k<a>){3}/)
);
assertTrue(/(?:(?:(?<a>x)|(?<a>y))\k<a>){2}/.test("xxyy"));
assertTrue(/(?:(?:(?<a>x)|(?<a>y)|(a)|(?<b>b)|(?<a>z))\k<a>){3}/.test("xzzyyxxy"));
assertFalse(/(?:(?:(?<a>x)|(?<a>y))\k<a>){2}/.test("xyxy"));
assertFalse(/(?:(?:(?<a>x)|(?<a>y)|(a)|(?<b>b)|(?<a>z))\k<a>){3}/.test("xyzxyzxyz"));
assertEquals(3, "abcxyz".search(/(?<a>x)|(?<a>y)/));
assertEquals(3, "abcxyz".search(/(?<a>y)|(?<a>x)/));
assertEquals(1, "aybcxyz".search(/(?<a>x)|(?<a>y)/));
assertEquals(1, "aybcxyz".search(/(?<a>y)|(?<a>x)/));
assertEquals("2xyy", "xxyy".replace(/(?:(?:(?<a>x)|(?<a>y))\k<a>)/, "2$<a>"));
assertEquals("x2zyyxxy", "xzzyyxxy".replace(/(?:(?:(?<a>x)|(?<a>y)|(a)|(?<b>b)|(?<a>z))\k<a>)/, "2$<a>"));
assertEquals("2x(x,)yy", "xxyy".replace(/(?:(?:(?<a>x)|(?<a>y))\k<a>)/, "2$<a>($1,$2)"));
assertEquals(
"x2z(,,,,z)yyxxy",
"xzzyyxxy".replace(/(?:(?:(?<a>x)|(?<a>y)|(a)|(?<b>b)|(?<a>z))\k<a>)/, "2$<a>($1,$2,$3,$4,$5)")
);
assertEquals("2x2y", "xxyy".replace(/(?:(?:(?<a>x)|(?<a>y))\k<a>)/g, "2$<a>"));
assertEquals("x2z2y2xy", "xzzyyxxy".replace(/(?:(?:(?<a>x)|(?<a>y)|(a)|(?<b>b)|(?<a>z))\k<a>)/g, "2$<a>"));
assertEquals("2x&2y", "xx&yy".replaceAll(/(?:(?:(?<a>x)|(?<a>y))\k<a>)/g, "2$<a>"));
assertEquals(
"x&2z&2y&2x&y",
"x&zz&yy&xx&y".replaceAll(/(?:(?:(?<a>x)|(?<a>y)|(a)|(?<b>b)|(?<a>z))\k<a>)/g, "2$<a>")
);
assertEquals(["", "x", undefined, "", undefined, "y", ""], "xxyy".split(/(?:(?:(?<a>x)|(?<a>y))\k<a>)/));
assertEquals(
[
"x",
undefined,
undefined,
undefined,
undefined,
"z",
"",
undefined,
"y",
undefined,
undefined,
undefined,
"",
"x",
undefined,
undefined,
undefined,
undefined,
"y",
],
"xzzyyxxy".split(/(?:(?:(?<a>x)|(?<a>y)|(a)|(?<b>b)|(?<a>z))\k<a>)/)
);
function assertMatchAll(matches, expected) {
let i = 0;
for (match of matches) {
assertEquals(expected[i], match);
i++;
}
}
assertMatchAll("xyx".matchAll(/(?<a>x)|(?<a>y)/g), [
["x", "x", undefined],
["y", undefined, "y"],
["x", "x", undefined],
]);
assertMatchAll("xyx".matchAll(/(?<a>y)|(?<a>x)/g), [
["x", undefined, "x"],
["y", "y", undefined],
["x", undefined, "x"],
]);
// Property enumeration order of the groups object is based on source order, not
// match order.
assertEquals(["b", "a"], Object.keys(/(?<b>x)(?<a>x)|(?<a>y)(?<b>y)/.exec("xx").groups));
assertEquals(["b", "a"], Object.keys(/(?<b>x)(?<a>x)|(?<a>y)(?<b>y)/.exec("yy").groups));
// Test match indices with duplicate groups.
assertEquals([2, 3], "abxy".match(/(?<a>x)|(?<a>y)/d).indices.groups.a);
assertEquals([2, 3], "bayx".match(/(?<a>x)|(?<a>y)/d).indices.groups.a);
// Replace with function as replacement.
function testReplaceWithCallback(global) {
let replace_callback_cnt = 0;
function checkReplace(match, c1, c2, offset, string, groups) {
replace_callback_cnt++;
if (offset == 0) {
// First callback for match 'xx'.
assertEquals("xx", match);
assertEquals("x", c1);
assertEquals(undefined, c2);
assertEquals("xxyy", string);
} else {
// Second callback for match 'yy'.
assertTrue(global);
assertEquals(2, offset);
assertEquals("yy", match);
assertEquals(undefined, c1);
assertEquals("y", c2);
assertEquals("xxyy", string);
}
return "2" + groups.a;
}
let re = new RegExp("(?:(?:(?<a>x)|(?<a>y))\\k<a>)", global ? "g" : "");
let expected = global ? "2x2y" : "2xyy";
assertEquals(expected, "xxyy".replace(re, checkReplace));
assertEquals(global ? 2 : 1, replace_callback_cnt);
}
testReplaceWithCallback(false);
testReplaceWithCallback(true);
});