Skip to content

Commit da8fe49

Browse files
authored
Generate BSF CSVs both with and without /third_party tests (#275)
Adds an --include-third-party flag to browser-specific-failures.js (off by default), and extends build.sh to produce four CSVs per run: the existing {stable,experimental}-browser-specific-failures.csv files will be updated to exclude /third_party while the new -with-third-party.csv variants will include them. This will let wpt.fyi expose a UI toggle to switch between different BSF chart views. wpt.fyi will be updated in a separate change to include the third_party toggle. /third_party/ introduces Temporal tests as individual tests instead of using the subtest WPT convention. These test failures misrepresent the overall interoperability of the web by causing the single browser failure graph to be completely dominated by a feature not yet shipped by all browsers.
1 parent 0f777f2 commit da8fe49

4 files changed

Lines changed: 113 additions & 2 deletions

File tree

browser-specific-failures.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ flags.defineString('output', null,
2121
'{stable, experimental}-browser-specific-failures.csv');
2222
flags.defineBoolean('experimental', false,
2323
'Calculate metrics for experimental runs.');
24+
flags.defineBoolean('include-third-party', false,
25+
'Include /third_party tests in BSF scoring.');
2426
flags.parse();
2527

2628

@@ -39,6 +41,7 @@ async function main() {
3941
const from = moment(flags.get('from'));
4042
const to = moment(flags.get('to'));
4143
const experimental = flags.get('experimental');
44+
const includeThirdParty = flags.get('include-third-party');
4245
const alignedRuns = await lib.runs.fetchAlignedRunsFromServer(
4346
products, from, to, experimental);
4447

@@ -78,6 +81,10 @@ async function main() {
7881
throw new Error('Run JSON contains "tree" field; code needs changed.');
7982
}
8083
run.tree = await lib.results.getGitTree(repo, run);
84+
if (!includeThirdParty) {
85+
run.tree = lib.browserSpecific.pruneExcludedPaths(
86+
run.tree, ['/third_party']);
87+
}
8188
}
8289
}
8390
after = Date.now();

build.sh

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,21 @@ update_bsf_csv() {
2727
if [[ $1 == *"experimental"* ]]; then
2828
EXPERIMENTAL_FLAG="--experimental"
2929
fi
30+
local INCLUDE_THIRD_PARTY_FLAG=""
31+
if [[ $1 == *"with-third-party"* ]]; then
32+
INCLUDE_THIRD_PARTY_FLAG="--include-third-party"
33+
fi
3034

3135
node browser-specific-failures.js \
32-
${EXPERIMENTAL_FLAG} --from=${FROM_DATE} --to=${TO_DATE} \
36+
${EXPERIMENTAL_FLAG} ${INCLUDE_THIRD_PARTY_FLAG} \
37+
--from=${FROM_DATE} --to=${TO_DATE} \
3338
--output=${OUTPUT}
3439
}
3540

3641
update_bsf_csv out/data/stable-browser-specific-failures.csv
3742
update_bsf_csv out/data/experimental-browser-specific-failures.csv
43+
update_bsf_csv out/data/stable-browser-specific-failures-with-third-party.csv
44+
update_bsf_csv out/data/experimental-browser-specific-failures-with-third-party.csv
3845

3946
update_interop_year() {
4047
local YEAR="${1}"

lib/browser-specific.js

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,4 +386,27 @@ function scoreBrowserSpecificFailures(runs, expectedBrowsers) {
386386
return new Map(scores.map((score, i) => [runs[i].browser_name, score]));
387387
}
388388

389-
module.exports = {scoreBrowserSpecificFailures};
389+
// Returns a copy of |tree| with any subtrees whose path appears in
390+
// |excludedPaths| removed. Subtrees not on a path to an excluded entry are
391+
// shared by reference (no deep copy), so the operation is cheap.
392+
function pruneExcludedPaths(tree, excludedPaths, currentPath = '') {
393+
const relevant = excludedPaths.filter(p =>
394+
p.startsWith(currentPath + '/'));
395+
if (relevant.length === 0) {
396+
return tree;
397+
}
398+
399+
const newTrees = {};
400+
for (const [name, child] of Object.entries(tree.trees)) {
401+
const childPath = `${currentPath}/${name}`;
402+
if (relevant.includes(childPath)) {
403+
continue;
404+
}
405+
newTrees[name] = relevant.some(p => p.startsWith(childPath + '/')) ?
406+
pruneExcludedPaths(child, relevant, childPath) :
407+
child;
408+
}
409+
return {...tree, trees: newTrees};
410+
}
411+
412+
module.exports = {scoreBrowserSpecificFailures, pruneExcludedPaths};

test/browser-specific.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,4 +547,78 @@ describe('browser-specific.js', () => {
547547
assert.deepEqual(scores, new Map([['chrome', 0.5], ['firefox', 0.5], ['safari', 0.0]]));
548548
});
549549
});
550+
551+
describe('pruneExcludedPaths', () => {
552+
function collectTestPaths(tree, currentPath = '') {
553+
const paths = [];
554+
for (const name of Object.keys(tree.tests || {})) {
555+
paths.push(`${currentPath}/${name}`);
556+
}
557+
for (const [name, child] of Object.entries(tree.trees || {})) {
558+
paths.push(...collectTestPaths(child, `${currentPath}/${name}`));
559+
}
560+
return paths.sort();
561+
}
562+
563+
it('removes a top-level excluded subtree', () => {
564+
const tree = new TreeBuilder()
565+
.addTest('third_party/test.html', 'PASS')
566+
.addTest('foo/test.html', 'PASS')
567+
.build();
568+
569+
const pruned = browserSpecific.pruneExcludedPaths(tree, ['/third_party']);
570+
571+
assert.deepEqual(collectTestPaths(pruned), ['/foo/test.html']);
572+
});
573+
574+
it('does not remove a same-named subtree under a different parent', () => {
575+
// /foo/third_party/ is NOT on the excluded list and must be retained.
576+
const tree = new TreeBuilder()
577+
.addTest('third_party/a.html', 'PASS')
578+
.addTest('foo/third_party/b.html', 'PASS')
579+
.addTest('bar/baz.html', 'PASS')
580+
.build();
581+
582+
const pruned = browserSpecific.pruneExcludedPaths(tree, ['/third_party']);
583+
584+
assert.deepEqual(collectTestPaths(pruned), [
585+
'/bar/baz.html',
586+
'/foo/third_party/b.html',
587+
]);
588+
});
589+
590+
it('does not remove a sibling that shares a name prefix', () => {
591+
// /third_party_extra is a sibling of /third_party and must be retained;
592+
// the exclusion is by exact path segment, not string prefix.
593+
const tree = new TreeBuilder()
594+
.addTest('third_party/a.html', 'PASS')
595+
.addTest('third_party_extra/b.html', 'PASS')
596+
.build();
597+
598+
const pruned = browserSpecific.pruneExcludedPaths(tree, ['/third_party']);
599+
600+
assert.deepEqual(collectTestPaths(pruned), ['/third_party_extra/b.html']);
601+
});
602+
603+
it('preserves all tests when no excluded path matches', () => {
604+
const tree = new TreeBuilder()
605+
.addTest('foo/a.html', 'PASS')
606+
.addTest('bar/b.html', 'PASS')
607+
.build();
608+
609+
const pruned = browserSpecific.pruneExcludedPaths(tree, ['/third_party']);
610+
611+
assert.deepEqual(collectTestPaths(pruned), ['/bar/b.html', '/foo/a.html']);
612+
});
613+
614+
it('handles an empty exclusion list', () => {
615+
const tree = new TreeBuilder()
616+
.addTest('third_party/test.html', 'PASS')
617+
.build();
618+
619+
const pruned = browserSpecific.pruneExcludedPaths(tree, []);
620+
621+
assert.strictEqual(pruned, tree);
622+
});
623+
});
550624
});

0 commit comments

Comments
 (0)