test262/harness/regExpUtils.js

114 lines
3.5 KiB
JavaScript

// Copyright (C) 2017 Mathias Bynens. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: |
Collection of functions used to assert the correctness of RegExp objects.
defines: [buildString, testPropertyEscapes, testPropertyOfStrings, testExtendedCharacterClass, matchValidator]
---*/
function buildString(args) {
// Use member expressions rather than destructuring `args` for improved
// compatibility with engines that only implement assignment patterns
// partially or not at all.
const loneCodePoints = args.loneCodePoints;
const ranges = args.ranges;
const CHUNK_SIZE = 10000;
let result = String.fromCodePoint.apply(null, loneCodePoints);
for (let i = 0; i < ranges.length; i++) {
let range = ranges[i];
let start = range[0];
let end = range[1];
let codePoints = [];
for (let length = 0, codePoint = start; codePoint <= end; codePoint++) {
codePoints[length++] = codePoint;
if (length === CHUNK_SIZE) {
result += String.fromCodePoint.apply(null, codePoints);
codePoints.length = length = 0;
}
}
result += String.fromCodePoint.apply(null, codePoints);
}
return result;
}
function printCodePoint(codePoint) {
const hex = codePoint
.toString(16)
.toUpperCase()
.padStart(6, "0");
return `U+${hex}`;
}
function printStringCodePoints(string) {
const buf = [];
for (let symbol of string) {
let formatted = printCodePoint(symbol.codePointAt(0));
buf.push(formatted);
}
return buf.join(' ');
}
function testPropertyEscapes(regExp, string, expression) {
if (!regExp.test(string)) {
for (let symbol of string) {
let formatted = printCodePoint(symbol.codePointAt(0));
assert(
regExp.test(symbol),
`\`${ expression }\` should match ${ formatted } (\`${ symbol }\`)`
);
}
}
}
function testPropertyOfStrings(args) {
// Use member expressions rather than destructuring `args` for improved
// compatibility with engines that only implement assignment patterns
// partially or not at all.
const regExp = args.regExp;
const expression = args.expression;
const matchStrings = args.matchStrings;
const nonMatchStrings = args.nonMatchStrings;
const allStrings = matchStrings.join('');
if (!regExp.test(allStrings)) {
for (let string of matchStrings) {
assert(
regExp.test(string),
`\`${ expression }\` should match ${ string } (${ printStringCodePoints(string) })`
);
}
}
if (!nonMatchStrings) return;
const allNonMatchStrings = nonMatchStrings.join('');
if (regExp.test(allNonMatchStrings)) {
for (let string of nonMatchStrings) {
assert(
!regExp.test(string),
`\`${ expression }\` should not match ${ string } (${ printStringCodePoints(string) })`
);
}
}
}
// The exact same logic can be used to test extended character classes
// as enabled through the RegExp `v` flag. This is useful to test not
// just standalone properties of strings, but also string literals, and
// set operations.
const testExtendedCharacterClass = testPropertyOfStrings;
// Returns a function that validates a RegExp match result.
//
// Example:
//
// var validate = matchValidator(['b'], 1, 'abc');
// validate(/b/.exec('abc'));
//
function matchValidator(expectedEntries, expectedIndex, expectedInput) {
return function(match) {
assert.compareArray(match, expectedEntries, 'Match entries');
assert.sameValue(match.index, expectedIndex, 'Match index');
assert.sameValue(match.input, expectedInput, 'Match input');
}
}