blob: 57a6259d4686a73227a4f3f85b336b39fea6a44e [file] [edit]
// With verbose set to false, this test is successful if there is no output. Set verbose to true to see expected matches.
let verbose = false;
let errors = 0;
function arrayToString(arr)
{
let str = '';
arr.forEach(function(v, index) {
if (typeof v == "string")
str += "\"" + v + "\"";
else
str += v;
if (index != (arr.length - 1))
str += ',';
});
return str;
}
function objectToString(obj)
{
let str = "";
firstEntry = true;
for (const [key, value] of Object.entries(obj)) {
if (!firstEntry)
str += ", ";
str += key + ": " + dumpValue(value);
firstEntry = false;
}
return "{ " + str + " }";
}
function dumpValue(v)
{
if (v === null)
return "<null>";
if (v === undefined)
return "<undefined>";
if (typeof v == "string")
return "\"" + v + "\"";
let str = "";
if (v.length)
str += arrayToString(v);
if (v.groups) {
groupStr = objectToString(v.groups);
if (str.length) {
if ( groupStr.length)
str += ", " + groupStr;
} else
str = groupStr;
}
return "[ " + str + " ]";
}
function compareArray(expected, actual)
{
if (expected === null && actual === null)
return true;
if (expected === null) {
print("### expected is null, actual is not null");
return false;
}
if (actual === null) {
print("### expected is not null, actual is null");
return false;
}
if (expected.length !== actual.length) {
print("### expected.length: " + expected.length + ", actual.length: " + actual.length);
return false;
}
for (var i = 0; i < expected.length; i++) {
if (expected[i] !== actual[i]) {
print("### expected[" + i + "]: \"" + expected[i] + "\" !== actual[" + i + "]: \"" + actual[i] + "\"");
return false;
}
}
return true;
}
function compareGroups(expected, actual)
{
if (expected === null && actual === null)
return true;
if (expected === null) {
print("### expected group is null, actual group is not null");
return false;
}
if (actual === null) {
print("### expected group is not null, actual group is null");
return false;
}
for (const key in expected) {
if (expected[key] !== actual[key]) {
print("### expected." + key + ": " + dumpValue(expected[key]) + " !== actual." + key + ": " + dumpValue(actual[key]));
return false;
}
}
return true;
}
let testNumber = 0;
function testRegExp(re, str, exp, groups)
{
testNumber++;
if (groups)
exp.groups = groups;
let actual = re.exec(str);
let result = compareArray(exp, actual);;
if (exp && exp.groups) {
if (!compareGroups(exp.groups, actual.groups))
result = false;
}
if (result) {
if (verbose)
print("Test#" + testNumber + " " + re.toString() + ".exec(" + dumpValue(str) + "), passed ", dumpValue(exp));
} else {
print(re.toString() + ".exec(" + dumpValue(str) + "), FAILED test #" + testNumber + ", Expected ", dumpValue(exp), " got ", dumpValue(actual));
errors++;
}
}
function testRegExpSyntaxError(reString, flags, expError)
{
testNumber++;
try {
let re = new RegExp(reString, flags);
print("FAILED test #" + testNumber + ", Expected /" + reString + "/" + flags + " to throw \"" + expError + "\", but it didn't");
errors++;
} catch (e) {
if (e != expError)
print("FAILED test #" + testNumber + ", Expected /" + reString + "/" + flags + " to throw \"" + expError + "\" got \"" + e + "\"");
else if (verbose)
print("/" + reString + "/" + flags + " passed, it threw \"" + expError + "\" as expected");
}
}
function printErrors()
{
if (errors)
throw "Test had " + errors + " errors";
}
// Tests 1-5
testRegExp(/ID\d+/, "ID123", ["ID123"]);
testRegExp(/abcde[12]f/, "abcde1f", ["abcde1f"]);
testRegExp(/a[12]b/, "a2b", ["a2b"]);
testRegExp(/c.lef/u, "c\u{1C345}lef", ["c\u{1C345}lef"]);
testRegExp(/\sbrown\s/, "The quick brown fox jumped.", [" brown "]);
// Tests 6-8
testRegExp(/h(?i:e)l(?i:l)o world/, "hello world", ["hello world"]);
testRegExp(/h(?i:e)l(?i:l)o world/, "Hello world", null);
testRegExp(/h(?i:e)l(?i:l)o world/, "hElLo world", ["hElLo world"]);
// Tests 9-41
let re = /^(?:break|case|catch|continue|debugger|default|do|else|finally|for|function|if|return|switch|throw|try|var|while|with|null|true|false|instanceof|typeof|void|delete|new|in|this)/;
testRegExp(re, "break", ["break"]);
testRegExp(re, "case", ["case"]);
testRegExp(re, "catch", ["catch"]);
testRegExp(re, "continue", ["continue"]);
testRegExp(re, "debugger", ["debugger"]);
testRegExp(re, "default", ["default"]);
testRegExp(re, "do", ["do"]);
testRegExp(re, "else", ["else"]);
testRegExp(re, "finally", ["finally"]);
testRegExp(re, "for", ["for"]);
testRegExp(re, "function", ["function"]);
testRegExp(re, "if", ["if"]);
testRegExp(re, "return", ["return"]);
testRegExp(re, "switch", ["switch"]);
testRegExp(re, "throw", ["throw"]);
testRegExp(re, "try", ["try"]);
testRegExp(re, "var", ["var"]);
testRegExp(re, "while", ["while"]);
testRegExp(re, "with", ["with"]);
testRegExp(re, "null", ["null"]);
testRegExp(re, "false", ["false"]);
testRegExp(re, "instanceof", ["instanceof"]);
testRegExp(re, "typeof", ["typeof"]);
testRegExp(re, "void", ["void"]);
testRegExp(re, "delete", ["delete"]);
testRegExp(re, "new", ["new"]);
testRegExp(re, "in", ["in"]);
testRegExp(re, "this", ["this"]);
testRegExp(re, "bre", null);
testRegExp(re, "c", null);
testRegExp(re, "cont", null);
testRegExp(re, " finally", null);
testRegExp(re, "v", null);
// Tests 42-50
// ЛЕВЫЙ | ПРАВЫЙ | ЦЕНТР | left | right | center
// note: ЛЕВЫЙ is Russian for left, ПРАВЫЙ is Russian for right and ЦЕНТР is Russian for center
let re2 = /\u{041b}\u{0415}\u{0412}\u{042b}\u{0419}|\u{041f}\u{0420}\u{0410}\u{0412}\u{042b}\u{0419}|\u{0426}\u{0415}\u{041d}\u{0422}\u{0420}|left|right|center/u;
testRegExp(re2, "\u{041b}\u{0415}\u{0412}\u{042b}\u{0419}", ["\u{041b}\u{0415}\u{0412}\u{042b}\u{0419}"]);
testRegExp(re2, " \u{041f}\u{0420}\u{0410}\u{0412}\u{042b}\u{0419}", ["\u{041f}\u{0420}\u{0410}\u{0412}\u{042b}\u{0419}"]);
testRegExp(re2, "\u{0426}\u{0415}\u{041d}\u{0422}\u{0420} ", ["\u{0426}\u{0415}\u{041d}\u{0422}\u{0420}"]);
testRegExp(re2, "\u{041b}\u{0415}\u{0412}\u{042b} ", null);
testRegExp(re2, "\u{0421}\u{0415}\u{0420}\u{0415}\u{0414}\u{0418}\u{041d}\u{0410}", null);
testRegExp(re2, " left", ["left"]);
testRegExp(re2, " right ", ["right"]);
testRegExp(re2, "in the center of the ring", ["center"]);
testRegExp(re2, "middle", null);
// Tests 51-55
testRegExp(/a\db\dc\d/, "a5b4c2", ["a5b4c2"]);
testRegExp(/a\db\dc\d/, "abc", null);
testRegExp(/a\db\dc\d/, "a5bc2", null);
testRegExp(/a\db\dc\d/, "ab1c2", null);
testRegExp(/a\db\dc\d/, "a3b1c", null);
// Tests 56-61
testRegExp(/a\db\dc\d/i, "a5b4c2", ["a5b4c2"]);
testRegExp(/a\db\dc\d/i, "A1B3C5", ["A1B3C5"]);
testRegExp(/a\db\dc\d/i, "A5b4c2", ["A5b4c2"]);
testRegExp(/a\db\dc\d/i, "a5B4c2", ["a5B4c2"]);
testRegExp(/a\db\dc\d/i, "a5b4C2", ["a5b4C2"]);
testRegExp(/a\db\dc\d/i, "aBc", null);
// Tests 62-67
testRegExp(/letters\d{12}numbers/, "letters123456789012numbers", ["letters123456789012numbers"]);
testRegExp(/letters\d{12}numbers/, "letters12345678901numbers", null);
testRegExp(/letters\d{12}numbers/, "letters1234567890123numbers", null);
testRegExp(/letters\d\d\d\d\d\d\d\d\d\d\d\dnumbers/, "letters123456789012numbers", ["letters123456789012numbers"]);
testRegExp(/letters\d\d\d\d\d\d\d\d\d\d\d\dnumbers/, "letters12345678901numbers", null);
testRegExp(/letters\d\d\d\d\d\d\d\d\d\d\d\dnumbers/, "letters1234567890123numbers", null);
// Tests 68-75
testRegExp(/ab\scd/, "ab cd", ["ab cd"]);
testRegExp(/ab\scd/, "ab\tcd", ["ab\tcd"]);
testRegExp(/ab\scd/, "abcd", null);
testRegExp(/ab\scd/, "ab cd", null);
testRegExp(/ab\scd/, "AB CD", null);
testRegExp(/ab\scd/, "aB\tcD", null);
testRegExp(/ab\scd/, "ABCD", null);
testRegExp(/ab\scd/, "ab Cd", null);
// Tests 76-83
testRegExp(/ab\scd/i, "AB CD", ["AB CD"]);
testRegExp(/ab\scd/i, "Ab\tCd", ["Ab\tCd"]);
testRegExp(/ab\scd/i, "ABCD", null);
testRegExp(/ab\scd/i, "ab cd", null);
testRegExp(/ab\scd/i, "AB CD", ["AB CD"]);
testRegExp(/ab\scd/i, "aB\tcd", ["aB\tcd"]);
testRegExp(/ab\scd/i, "ABCD", null);
testRegExp(/ab\scd/i, "ab Cd", null);
// Tests 84-91
testRegExp(/ab\s+cd/i, "AB CD", ["AB CD"]);
testRegExp(/ab\s+cd/i, "Ab\tCd", ["Ab\tCd"]);
testRegExp(/ab\s+cd/i, "ABCD", null);
testRegExp(/ab\s+cd/i, "ab cd", ["ab cd"]);
testRegExp(/ab\s+cd/i, "AB CD", ["AB CD"]);
testRegExp(/ab\s+cd/i, "aB\tcd", ["aB\tcd"]);
testRegExp(/ab\s+cd/i, "ABCD", null);
testRegExp(/ab\s+cd/i, "ab Cd", ["ab Cd"]);
// Tests 92-
class RegExpTest {
constructor(re, expect) {
this.re = re;
this.expect = expect;
}
runTest(subjectString) {
testRegExp(this.re, subjectString, this.expect);
}
};
class RegExpTestList {
#allTests;
constructor() {
this.#allTests = [];
}
addTest(re, expect) {
this.#allTests.push(new RegExpTest(re, expect));
}
runTests(subjectString) {
for (let test of this.#allTests)
test.runTest(subjectString);
}
};
let reTests = new RegExpTestList;
let ACharCode = "A".charCodeAt(0);
let aCharCode = "a".charCodeAt(0);
let MCharCode = "M".charCodeAt(0);
let mCharCode = "m".charCodeAt(0);
let spaces = " ";
let maxSubStrLen = 12;
let strUpper = ""
let strLower = ""
let goodCharClasses = [""];
let badCharClasses = [""];
for (let len = 1; len <= spaces.length; ++len) {
let goodCharClass = "";
let badCharClass = "";
for (let i = 0; i < len; ++i) {
goodCharClass += "\\s";
if (i == (len >> 1))
badCharClass += "\\d";
else
badCharClass += "\\s";
}
goodCharClasses.push(goodCharClass);
badCharClasses.push(badCharClass);
}
for (let len = 1; len < maxSubStrLen; ++len) {
let subStrUpperPrefix = "";
let subStrLowerPrefix = "";
let subStrUpperSuffix = "";
let subStrLowerSuffix = "";
for (let charOffset = 0; charOffset < len; ++charOffset) {
subStrUpperPrefix += String.fromCharCode(ACharCode + charOffset);
subStrLowerPrefix += String.fromCharCode(aCharCode + charOffset);
subStrUpperSuffix += String.fromCharCode(MCharCode + charOffset);
subStrLowerSuffix += String.fromCharCode(mCharCode + charOffset);
}
for (let numSpaces = 0; numSpaces < spaces.length; ++numSpaces) {
let subStrUpper = subStrUpperPrefix + spaces.slice(0, numSpaces) + subStrUpperSuffix;
let subStrLower = subStrLowerPrefix + spaces.slice(0, numSpaces) + subStrLowerSuffix;
reTests.addTest(new RegExp(subStrUpperPrefix + goodCharClasses[numSpaces] + subStrUpperSuffix), [subStrUpper]);
reTests.addTest(new RegExp(subStrLowerPrefix + goodCharClasses[numSpaces] + subStrLowerSuffix), [subStrLower]);
reTests.addTest(new RegExp(subStrUpperPrefix + goodCharClasses[numSpaces] + subStrLowerSuffix, "i"), [subStrUpper]);
reTests.addTest(new RegExp(subStrLowerPrefix + goodCharClasses[numSpaces] + subStrUpperSuffix, "i"), [subStrUpper]);
if (numSpaces) {
reTests.addTest(new RegExp(subStrUpperPrefix + badCharClasses[numSpaces] + subStrUpperSuffix), null);
reTests.addTest(new RegExp(subStrLowerPrefix + badCharClasses[numSpaces] + subStrLowerSuffix), null);
reTests.addTest(new RegExp(subStrUpperPrefix + badCharClasses[numSpaces] + subStrLowerSuffix, "i"), null);
reTests.addTest(new RegExp(subStrLowerPrefix + badCharClasses[numSpaces] + subStrUpperSuffix, "i"), null);
let badLowerPrefix = "z";
if (subStrLowerPrefix.length > 1){
indexToChange = subStrLowerPrefix.length >> 1;
badLowerPrefix = subStrLowerPrefix.slice(0, indexToChange) + "z" + subStrLowerPrefix.slice(indexToChange + 1);
}
reTests.addTest(new RegExp(badLowerPrefix + goodCharClasses[numSpaces] + subStrUpperSuffix), null);
let badUpperSuffix = "z";
if (subStrUpperSuffix.length > 1){
indexToChange = subStrUpperSuffix.length >> 1;
badUpperSuffix = subStrUpperSuffix.slice(0, indexToChange) + "z" + subStrUpperSuffix.slice(indexToChange + 1);
}
reTests.addTest(new RegExp(subStrLowerPrefix + goodCharClasses[numSpaces] + badUpperSuffix, "i"), null);
}
strUpper += subStrUpper + "-";
strLower += subStrLower + "-";
}
}
let str = "12345 " + strUpper + strLower;
reTests.runTests(str);
printErrors()