1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
11 from mozlog
import commandline
16 "processor": "x86_64",
17 "version": "Ubuntu 18.04",
18 "os_version": "18.04",
22 "linux_distro": "Ubuntu",
23 "apple_silicon": False,
28 "buildapp": "browser",
29 "buildtype_guess": "pgo",
32 "crashreporter": True,
33 "datareporting": True,
36 "early_beta_or_earlier": True,
38 "nightly_build": True,
39 "non_native_theme": True,
43 "platform_guess": "linux64",
44 "release_or_beta": False,
45 "require_signing": False,
49 "tests_enabled": True,
61 "sessionHistoryInParent": True,
68 "processor": "x86_64",
69 "version": "Ubuntu 18.04",
70 "os_version": "18.04",
74 "linux_distro": "Ubuntu",
75 "apple_silicon": False,
80 "buildapp": "browser",
81 "buildtype_guess": "debug",
84 "crashreporter": True,
85 "datareporting": True,
88 "early_beta_or_earlier": True,
90 "nightly_build": True,
91 "non_native_theme": True,
95 "platform_guess": "linux64",
96 "release_or_beta": False,
97 "require_signing": False,
101 "tests_enabled": True,
107 "product": "firefox",
113 "sessionHistoryInParent": False,
120 "processor": "x86_64",
121 "version": "10.0.17134",
122 "os_version": "10.0",
127 "apple_silicon": False,
128 "appname": "firefox",
131 "bin_suffix": ".exe",
132 "buildapp": "browser",
133 "buildtype_guess": "pgo",
134 "cc_type": "clang-cl",
136 "crashreporter": True,
137 "datareporting": True,
140 "early_beta_or_earlier": True,
141 "healthreport": True,
142 "nightly_build": True,
143 "non_native_theme": False,
147 "platform_guess": "win64",
148 "release_or_beta": False,
149 "require_signing": False,
153 "tests_enabled": True,
154 "toolkit": "windows",
159 "product": "firefox",
165 "sessionHistoryInParent": False,
173 # RE that checks for anything containing a three+ digit number
174 maybe_bug_re
= re
.compile(r
".*\d\d\d+")
178 parser
= argparse
.ArgumentParser()
180 "--all-json", type=os
.path
.abspath
, help="Path to write json output to"
184 type=os
.path
.abspath
,
185 help="Path to write list of regressions with no associated bug",
191 choices
=list(run_infos
.keys()),
192 help="Configurations to compute fission changes for",
194 commandline
.add_logging_group(parser
)
198 def allowed_results(test
, subtest
=None):
199 return test
.expected(subtest
), test
.known_intermittent(subtest
)
202 def is_worse(baseline_result
, new_result
):
203 if new_result
== baseline_result
:
206 if new_result
in ("PASS", "OK"):
209 if baseline_result
in ("PASS", "OK"):
212 # A crash -> not crash isn't a regression
213 if baseline_result
== "CRASH":
219 def is_regression(baseline_result
, new_result
):
220 if baseline_result
== new_result
:
223 baseline_expected
, baseline_intermittent
= baseline_result
224 new_expected
, new_intermittent
= new_result
226 baseline_all
= {baseline_expected} |
set(baseline_intermittent
)
227 new_all
= {new_expected} |
set(new_intermittent
)
229 if baseline_all
== new_all
:
232 if not baseline_intermittent
and not new_intermittent
:
233 return is_worse(baseline_expected
, new_expected
)
235 # If it was intermittent and isn't now, check if the new result is
236 # worse than any of the previous results so that [PASS, FAIL] -> FAIL
237 # looks like a regression
238 if baseline_intermittent
and not new_intermittent
:
239 return any(is_worse(result
, new_expected
) for result
in baseline_all
)
241 # If it was a perma and is now intermittent, check if any new result is
242 # worse than the previous result.
243 if not baseline_intermittent
and new_intermittent
:
244 return any(is_worse(baseline_expected
, result
) for result
in new_all
)
246 # If it was an intermittent and is still an intermittent
247 # check if any new result not in the old results is worse than
249 new_results
= new_all
- baseline_all
251 is_worse(baseline_result
, new_result
)
252 for new_result
in new_results
253 for baseline_result
in baseline_all
257 def get_meta_prop(test
, subtest
, name
):
258 for meta
in test
.itermeta(subtest
):
260 value
= meta
.get(name
)
268 def include_result(result
):
269 if result
.disabled
or result
.regressions
:
272 if isinstance(result
, TestResult
):
273 for subtest_result
in result
.subtest_results
.values():
274 if subtest_result
.disabled
or subtest_result
.regressions
:
283 self
.disabled
= set()
284 self
.regressions
= {}
286 def add_regression(self
, platform
, baseline_results
, fission_results
):
287 self
.regressions
[platform
] = {
288 "baseline": [baseline_results
[0]] + baseline_results
[1],
289 "fission": [fission_results
[0]] + fission_results
[1],
293 raise NotImplementedError
295 def is_triaged(self
):
296 raise NotImplementedError
299 class TestResult(Result
):
302 self
.subtest_results
= {}
304 def add_subtest(self
, name
):
305 self
.subtest_results
[name
] = SubtestResult(self
)
311 for name
, item
in self
.subtest_results
.items()
312 if include_result(item
)
315 rv
["subtest_results"] = include_subtests
317 rv
["regressions"] = self
.regressions
319 rv
["disabled"] = list(self
.disabled
)
321 rv
["bugs"] = list(self
.bugs
)
324 def is_triaged(self
):
325 return bool(self
.bugs
) or (
328 subtest_result
.is_triaged()
329 for subtest_result
in self
.subtest_results
.values()
334 class SubtestResult(Result
):
335 def __init__(self
, parent
):
342 rv
["regressions"] = self
.regressions
344 rv
["disabled"] = list(self
.disabled
)
345 bugs
= self
.bugs
- self
.parent
.bugs
347 rv
["bugs"] = list(bugs
)
350 def is_triaged(self
):
351 return bool(not self
.regressions
or self
.parent
.bugs
or self
.bugs
)
354 def run(logger
, src_root
, obj_root
, **kwargs
):
355 commandline
.setup_logging(
356 logger
, {key
: value
for key
, value
in kwargs
.items() if key
.startswith("log_")}
359 import manifestupdate
363 os
.path
.abspath(os
.path
.join(os
.path
.dirname(__file__
), "tests", "tools")),
365 from wptrunner
import testloader
, wpttest
367 logger
.info("Loading test manifest")
368 test_manifests
= manifestupdate
.run(src_root
, obj_root
, logger
)
372 platforms
= kwargs
["platforms"]
373 if platforms
is None:
374 platforms
= run_infos
.keys()
376 for platform
in platforms
:
377 platform_run_info
= run_infos
[platform
]
378 run_info_baseline
= platform_run_info
.copy()
379 run_info_baseline
["fission"] = False
383 for kind
in ("baseline", "fission"):
384 logger
.info("Loading tests %s %s" % (platform
, kind
))
385 run_info
= platform_run_info
.copy()
386 run_info
["fission"] = kind
== "fission"
388 test_loader
= testloader
.TestLoader(
389 test_manifests
, wpttest
.enabled_tests
, run_info
, manifest_filters
=[]
393 for _
, _
, test
in test_loader
.iter_tests()
394 if test
._test
_metadata
is not None
397 for test_id
, baseline_test
in tests
["baseline"].items():
398 fission_test
= tests
["fission"][test_id
]
400 if test_id
not in test_results
:
401 test_results
[test_id
] = TestResult()
403 test_result
= test_results
[test_id
]
405 baseline_bug
= get_meta_prop(baseline_test
, None, "bug")
406 fission_bug
= get_meta_prop(fission_test
, None, "bug")
407 if fission_bug
and fission_bug
!= baseline_bug
:
408 test_result
.bugs
.add(fission_bug
)
410 if fission_test
.disabled() and not baseline_test
.disabled():
411 test_result
.disabled
.add(platform
)
412 reason
= get_meta_prop(fission_test
, None, "disabled")
413 if reason
and maybe_bug_re
.match(reason
):
414 test_result
.bugs
.add(reason
)
416 baseline_results
= allowed_results(baseline_test
)
417 fission_results
= allowed_results(fission_test
)
418 result_is_regression
= is_regression(baseline_results
, fission_results
)
420 if baseline_results
!= fission_results
:
423 % (test_id
, baseline_results
, fission_results
, result_is_regression
)
426 if result_is_regression
:
427 test_result
.add_regression(platform
, baseline_results
, fission_results
)
431 baseline_subtest_meta
,
432 ) in baseline_test
._test
_metadata
.subtests
.items():
433 fission_subtest_meta
= baseline_test
._test
_metadata
.subtests
[name
]
434 if name
not in test_result
.subtest_results
:
435 test_result
.add_subtest(name
)
437 subtest_result
= test_result
.subtest_results
[name
]
439 baseline_bug
= get_meta_prop(baseline_test
, name
, "bug")
440 fission_bug
= get_meta_prop(fission_test
, name
, "bug")
441 if fission_bug
and fission_bug
!= baseline_bug
:
442 subtest_result
.bugs
.add(fission_bug
)
444 if bool(fission_subtest_meta
.disabled
) and not bool(
445 baseline_subtest_meta
.disabled
447 subtest_result
.disabled
.add(platform
)
448 if maybe_bug_re
.match(fission_subtest_meta
.disabled
):
449 subtest_result
.bugs
.add(fission_subtest_meta
.disabled
)
451 baseline_results
= allowed_results(baseline_test
, name
)
452 fission_results
= allowed_results(fission_test
, name
)
454 result_is_regression
= is_regression(baseline_results
, fission_results
)
456 if baseline_results
!= fission_results
:
464 result_is_regression
,
468 if result_is_regression
:
469 subtest_result
.add_regression(
470 platform
, baseline_results
, fission_results
475 for test_id
, result
in test_results
.items()
476 if include_result(result
)
479 if kwargs
["all_json"] is not None:
480 write_all(test_results
, kwargs
["all_json"])
482 if kwargs
["untriaged"] is not None:
483 write_untriaged(test_results
, kwargs
["untriaged"])
486 def write_all(test_results
, path
):
487 json_data
= {test_id
: result
.to_json() for test_id
, result
in test_results
.items()}
489 dir_name
= os
.path
.dirname(path
)
490 if not os
.path
.exists(dir_name
):
491 os
.makedirs(dir_name
)
493 with
open(path
, "w") as f
:
494 json
.dump(json_data
, f
, indent
=2)
497 def write_untriaged(test_results
, path
):
498 dir_name
= os
.path
.dirname(path
)
499 if not os
.path
.exists(dir_name
):
500 os
.makedirs(dir_name
)
504 for test_id
, result
in test_results
.items()
505 if not result
.is_triaged()
508 with
open(path
, "w") as f
:
509 for test_id
, result
in data
:
510 f
.write(test_id
+ "\n")
511 for name
, subtest_result
in sorted(result
.subtest_results
.items()):
512 if not subtest_result
.is_triaged():
513 f
.write(" %s\n" % name
)