Skip to content

Commit 7e0dbc6

Browse files
joyeecheungBethGriggs
authored andcommitted
test: improve WPT runner name matching
This patch: - Support wildcards(*) in WPT runner name matching (needed by e.g. encoding where all the tests requires i18n support in the build) - Print failure reasons when encountering an expected failure - Fix a bug in copyGlobalsFromObject (previously it copies properties from `global` instead of the given `obj`) Previously an expected failure is printed as ``` [EXPECTED_FAILURE] response.formData() with input: %61+%4d%4D= ``` Now it is printed as ``` [EXPECTED_FAILURE] response.formData() with input: %61+%4d%4D= missing Request and Response ``` PR-URL: #24826 Refs: #24823 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Daijiro Wachi <daijiro.wachi@gmail.com>
1 parent da984be commit 7e0dbc6

File tree

1 file changed

+99
-40
lines changed

1 file changed

+99
-40
lines changed

test/common/wpt.js

+99-40
Original file line numberDiff line numberDiff line change
@@ -66,20 +66,92 @@ class ResourceLoader {
6666
}
6767
}
6868

69+
class StatusRule {
70+
constructor(key, value, pattern = undefined) {
71+
this.key = key;
72+
this.requires = value.requires || [];
73+
this.fail = value.fail;
74+
this.skip = value.skip;
75+
if (pattern) {
76+
this.pattern = this.transformPattern(pattern);
77+
}
78+
// TODO(joyeecheung): implement this
79+
this.scope = value.scope;
80+
this.comment = value.comment;
81+
}
82+
83+
/**
84+
* Transform a filename pattern into a RegExp
85+
* @param {string} pattern
86+
* @returns {RegExp}
87+
*/
88+
transformPattern(pattern) {
89+
const result = pattern.replace(/[-/\\^$+?.()|[\]{}]/g, '\\$&');
90+
return new RegExp(result.replace('*', '.*'));
91+
}
92+
}
93+
94+
class StatusRuleSet {
95+
constructor() {
96+
// We use two sets of rules to speed up matching
97+
this.exactMatch = {};
98+
this.patternMatch = [];
99+
}
100+
101+
/**
102+
* @param {object} rules
103+
*/
104+
addRules(rules) {
105+
for (const key of Object.keys(rules)) {
106+
if (key.includes('*')) {
107+
this.patternMatch.push(new StatusRule(key, rules[key], key));
108+
} else {
109+
this.exactMatch[key] = new StatusRule(key, rules[key]);
110+
}
111+
}
112+
}
113+
114+
match(file) {
115+
const result = [];
116+
const exact = this.exactMatch[file];
117+
if (exact) {
118+
result.push(exact);
119+
}
120+
for (const item of this.patternMatch) {
121+
if (item.pattern.test(file)) {
122+
result.push(item);
123+
}
124+
}
125+
return result;
126+
}
127+
}
128+
69129
class WPTTest {
70130
/**
71131
* @param {string} mod
72132
* @param {string} filename
73-
* @param {string[]} requires
74-
* @param {string | undefined} failReason
75-
* @param {string | undefined} skipReason
133+
* @param {StatusRule[]} rules
76134
*/
77-
constructor(mod, filename, requires, failReason, skipReason) {
135+
constructor(mod, filename, rules) {
78136
this.module = mod; // name of the WPT module, e.g. 'url'
79137
this.filename = filename; // name of the test file
80-
this.requires = requires;
81-
this.failReason = failReason;
82-
this.skipReason = skipReason;
138+
139+
this.requires = new Set();
140+
this.failReasons = [];
141+
this.skipReasons = [];
142+
for (const item of rules) {
143+
if (item.requires.length) {
144+
for (const req of item.requires) {
145+
this.requires.add(req);
146+
}
147+
}
148+
if (item.fail) {
149+
this.failReasons.push(item.fail);
150+
}
151+
if (item.skip) {
152+
this.skipReasons.push(item.skip);
153+
}
154+
}
83155
}
84156

85157
getAbsolutePath() {
@@ -90,54 +162,37 @@ class WPTTest {
90162
return fs.readFileSync(this.getAbsolutePath(), 'utf8');
91163
}
92164

93-
shouldSkip() {
94-
return this.failReason || this.skipReason;
95-
}
96-
97165
requireIntl() {
98-
return this.requires.includes('intl');
166+
return this.requires.has('intl');
99167
}
100168
}
101169

102170
class StatusLoader {
103171
constructor(path) {
104172
this.path = path;
105173
this.loaded = false;
106-
this.status = null;
174+
this.rules = new StatusRuleSet();
107175
/** @type {WPTTest[]} */
108176
this.tests = [];
109177
}
110178

111-
loadTest(file) {
112-
let requires = [];
113-
let failReason;
114-
let skipReason;
115-
if (this.status[file]) {
116-
requires = this.status[file].requires || [];
117-
failReason = this.status[file].fail;
118-
skipReason = this.status[file].skip;
119-
}
120-
return new WPTTest(this.path, file, requires,
121-
failReason, skipReason);
122-
}
123-
124179
load() {
125180
const dir = path.join(__dirname, '..', 'wpt');
126181
const statusFile = path.join(dir, 'status', `${this.path}.json`);
127182
const result = JSON.parse(fs.readFileSync(statusFile, 'utf8'));
128-
this.status = result;
183+
this.rules.addRules(result);
129184

130185
const list = fs.readdirSync(fixtures.path('wpt', this.path));
131186

132187
for (const file of list) {
133-
this.tests.push(this.loadTest(file));
188+
if (!(/\.\w+\.js$/.test(file))) {
189+
continue;
190+
}
191+
const match = this.rules.match(file);
192+
this.tests.push(new WPTTest(this.path, file, match));
134193
}
135194
this.loaded = true;
136195
}
137-
138-
get jsTests() {
139-
return this.tests.filter((test) => test.filename.endsWith('.js'));
140-
}
141196
}
142197

143198
const PASSED = 1;
@@ -156,7 +211,7 @@ class WPTRunner {
156211
this.status = new StatusLoader(path);
157212
this.status.load();
158213
this.tests = new Map(
159-
this.status.jsTests.map((item) => [item.filename, item])
214+
this.status.tests.map((item) => [item.filename, item])
160215
);
161216

162217
this.results = new Map();
@@ -171,7 +226,10 @@ class WPTRunner {
171226
*/
172227
copyGlobalsFromObject(obj, names) {
173228
for (const name of names) {
174-
const desc = Object.getOwnPropertyDescriptor(global, name);
229+
const desc = Object.getOwnPropertyDescriptor(obj, name);
230+
if (!desc) {
231+
assert.fail(`${name} does not exist on the object`);
232+
}
175233
this.globals.set(name, desc);
176234
}
177235
}
@@ -328,8 +386,9 @@ class WPTRunner {
328386
for (const item of items) {
329387
switch (item.type) {
330388
case FAILED: {
331-
if (test.failReason) {
389+
if (test.failReasons.length) {
332390
console.log(`[EXPECTED_FAILURE] ${item.test.name}`);
391+
console.log(test.failReasons.join('; '));
333392
} else {
334393
console.log(`[UNEXPECTED_FAILURE] ${item.test.name}`);
335394
unexpectedFailures.push([title, filename, item]);
@@ -386,10 +445,10 @@ class WPTRunner {
386445
});
387446
}
388447

389-
skip(filename, reason) {
448+
skip(filename, reasons) {
390449
this.addResult(filename, {
391450
type: SKIPPED,
392-
reason
451+
reason: reasons.join('; ')
393452
});
394453
}
395454

@@ -435,13 +494,13 @@ class WPTRunner {
435494
const queue = [];
436495
for (const test of this.tests.values()) {
437496
const filename = test.filename;
438-
if (test.skipReason) {
439-
this.skip(filename, test.skipReason);
497+
if (test.skipReasons.length > 0) {
498+
this.skip(filename, test.skipReasons);
440499
continue;
441500
}
442501

443502
if (!common.hasIntl && test.requireIntl()) {
444-
this.skip(filename, 'missing Intl');
503+
this.skip(filename, [ 'missing Intl' ]);
445504
continue;
446505
}
447506

0 commit comments

Comments
 (0)