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,
65 "isolated_process": False,
69 "processor": "x86_64",
70 "version": "Ubuntu 18.04",
71 "os_version": "18.04",
75 "linux_distro": "Ubuntu",
76 "apple_silicon": False,
81 "buildapp": "browser",
82 "buildtype_guess": "debug",
85 "crashreporter": True,
86 "datareporting": True,
89 "early_beta_or_earlier": True,
91 "nightly_build": True,
92 "non_native_theme": True,
96 "platform_guess": "linux64",
97 "release_or_beta": False,
98 "require_signing": False,
102 "tests_enabled": True,
108 "product": "firefox",
114 "sessionHistoryInParent": False,
118 "isolated_process": False,
122 "processor": "x86_64",
123 "version": "10.0.17134",
124 "os_version": "10.0",
129 "apple_silicon": False,
130 "appname": "firefox",
133 "bin_suffix": ".exe",
134 "buildapp": "browser",
135 "buildtype_guess": "pgo",
136 "cc_type": "clang-cl",
138 "crashreporter": True,
139 "datareporting": True,
142 "early_beta_or_earlier": True,
143 "healthreport": True,
144 "nightly_build": True,
145 "non_native_theme": False,
149 "platform_guess": "win64",
150 "release_or_beta": False,
151 "require_signing": False,
155 "tests_enabled": True,
156 "toolkit": "windows",
161 "product": "firefox",
167 "sessionHistoryInParent": False,
171 "isolated_process": False,
176 # RE that checks for anything containing a three+ digit number
177 maybe_bug_re
= re
.compile(r
".*\d\d\d+")
181 parser
= argparse
.ArgumentParser()
183 "--all-json", type=os
.path
.abspath
, help="Path to write json output to"
187 type=os
.path
.abspath
,
188 help="Path to write list of regressions with no associated bug",
194 choices
=list(run_infos
.keys()),
195 help="Configurations to compute fission changes for",
197 commandline
.add_logging_group(parser
)
201 def allowed_results(test
, subtest
=None):
202 return test
.expected(subtest
), test
.known_intermittent(subtest
)
205 def is_worse(baseline_result
, new_result
):
206 if new_result
== baseline_result
:
209 if new_result
in ("PASS", "OK"):
212 if baseline_result
in ("PASS", "OK"):
215 # A crash -> not crash isn't a regression
216 if baseline_result
== "CRASH":
222 def is_regression(baseline_result
, new_result
):
223 if baseline_result
== new_result
:
226 baseline_expected
, baseline_intermittent
= baseline_result
227 new_expected
, new_intermittent
= new_result
229 baseline_all
= {baseline_expected} |
set(baseline_intermittent
)
230 new_all
= {new_expected} |
set(new_intermittent
)
232 if baseline_all
== new_all
:
235 if not baseline_intermittent
and not new_intermittent
:
236 return is_worse(baseline_expected
, new_expected
)
238 # If it was intermittent and isn't now, check if the new result is
239 # worse than any of the previous results so that [PASS, FAIL] -> FAIL
240 # looks like a regression
241 if baseline_intermittent
and not new_intermittent
:
242 return any(is_worse(result
, new_expected
) for result
in baseline_all
)
244 # If it was a perma and is now intermittent, check if any new result is
245 # worse than the previous result.
246 if not baseline_intermittent
and new_intermittent
:
247 return any(is_worse(baseline_expected
, result
) for result
in new_all
)
249 # If it was an intermittent and is still an intermittent
250 # check if any new result not in the old results is worse than
252 new_results
= new_all
- baseline_all
254 is_worse(baseline_result
, new_result
)
255 for new_result
in new_results
256 for baseline_result
in baseline_all
260 def get_meta_prop(test
, subtest
, name
):
261 for meta
in test
.itermeta(subtest
):
263 value
= meta
.get(name
)
271 def include_result(result
):
272 if result
.disabled
or result
.regressions
:
275 if isinstance(result
, TestResult
):
276 for subtest_result
in result
.subtest_results
.values():
277 if subtest_result
.disabled
or subtest_result
.regressions
:
286 self
.disabled
= set()
287 self
.regressions
= {}
289 def add_regression(self
, platform
, baseline_results
, fission_results
):
290 self
.regressions
[platform
] = {
291 "baseline": [baseline_results
[0]] + baseline_results
[1],
292 "fission": [fission_results
[0]] + fission_results
[1],
296 raise NotImplementedError
298 def is_triaged(self
):
299 raise NotImplementedError
302 class TestResult(Result
):
305 self
.subtest_results
= {}
307 def add_subtest(self
, name
):
308 self
.subtest_results
[name
] = SubtestResult(self
)
314 for name
, item
in self
.subtest_results
.items()
315 if include_result(item
)
318 rv
["subtest_results"] = include_subtests
320 rv
["regressions"] = self
.regressions
322 rv
["disabled"] = list(self
.disabled
)
324 rv
["bugs"] = list(self
.bugs
)
327 def is_triaged(self
):
328 return bool(self
.bugs
) or (
331 subtest_result
.is_triaged()
332 for subtest_result
in self
.subtest_results
.values()
337 class SubtestResult(Result
):
338 def __init__(self
, parent
):
345 rv
["regressions"] = self
.regressions
347 rv
["disabled"] = list(self
.disabled
)
348 bugs
= self
.bugs
- self
.parent
.bugs
350 rv
["bugs"] = list(bugs
)
353 def is_triaged(self
):
354 return bool(not self
.regressions
or self
.parent
.bugs
or self
.bugs
)
357 def run(logger
, src_root
, obj_root
, **kwargs
):
358 commandline
.setup_logging(
359 logger
, {key
: value
for key
, value
in kwargs
.items() if key
.startswith("log_")}
362 import manifestupdate
366 os
.path
.abspath(os
.path
.join(os
.path
.dirname(__file__
), "tests", "tools")),
368 from wptrunner
import testloader
, wpttest
370 logger
.info("Loading test manifest")
371 test_manifests
= manifestupdate
.run(src_root
, obj_root
, logger
)
375 platforms
= kwargs
["platforms"]
376 if platforms
is None:
377 platforms
= run_infos
.keys()
379 for platform
in platforms
:
380 platform_run_info
= run_infos
[platform
]
381 run_info_baseline
= platform_run_info
.copy()
382 run_info_baseline
["fission"] = False
386 for kind
in ("baseline", "fission"):
387 logger
.info("Loading tests %s %s" % (platform
, kind
))
388 run_info
= platform_run_info
.copy()
389 run_info
["fission"] = kind
== "fission"
391 test_loader
= testloader
.TestLoader(
392 test_manifests
, wpttest
.enabled_tests
, run_info
, manifest_filters
=[]
396 for _
, _
, test
in test_loader
.iter_tests()
397 if test
._test
_metadata
is not None
400 for test_id
, baseline_test
in tests
["baseline"].items():
401 fission_test
= tests
["fission"][test_id
]
403 if test_id
not in test_results
:
404 test_results
[test_id
] = TestResult()
406 test_result
= test_results
[test_id
]
408 baseline_bug
= get_meta_prop(baseline_test
, None, "bug")
409 fission_bug
= get_meta_prop(fission_test
, None, "bug")
410 if fission_bug
and fission_bug
!= baseline_bug
:
411 test_result
.bugs
.add(fission_bug
)
413 if fission_test
.disabled() and not baseline_test
.disabled():
414 test_result
.disabled
.add(platform
)
415 reason
= get_meta_prop(fission_test
, None, "disabled")
416 if reason
and maybe_bug_re
.match(reason
):
417 test_result
.bugs
.add(reason
)
419 baseline_results
= allowed_results(baseline_test
)
420 fission_results
= allowed_results(fission_test
)
421 result_is_regression
= is_regression(baseline_results
, fission_results
)
423 if baseline_results
!= fission_results
:
426 % (test_id
, baseline_results
, fission_results
, result_is_regression
)
429 if result_is_regression
:
430 test_result
.add_regression(platform
, baseline_results
, fission_results
)
434 baseline_subtest_meta
,
435 ) in baseline_test
._test
_metadata
.subtests
.items():
436 fission_subtest_meta
= baseline_test
._test
_metadata
.subtests
[name
]
437 if name
not in test_result
.subtest_results
:
438 test_result
.add_subtest(name
)
440 subtest_result
= test_result
.subtest_results
[name
]
442 baseline_bug
= get_meta_prop(baseline_test
, name
, "bug")
443 fission_bug
= get_meta_prop(fission_test
, name
, "bug")
444 if fission_bug
and fission_bug
!= baseline_bug
:
445 subtest_result
.bugs
.add(fission_bug
)
447 if bool(fission_subtest_meta
.disabled
) and not bool(
448 baseline_subtest_meta
.disabled
450 subtest_result
.disabled
.add(platform
)
451 if maybe_bug_re
.match(fission_subtest_meta
.disabled
):
452 subtest_result
.bugs
.add(fission_subtest_meta
.disabled
)
454 baseline_results
= allowed_results(baseline_test
, name
)
455 fission_results
= allowed_results(fission_test
, name
)
457 result_is_regression
= is_regression(baseline_results
, fission_results
)
459 if baseline_results
!= fission_results
:
467 result_is_regression
,
471 if result_is_regression
:
472 subtest_result
.add_regression(
473 platform
, baseline_results
, fission_results
478 for test_id
, result
in test_results
.items()
479 if include_result(result
)
482 if kwargs
["all_json"] is not None:
483 write_all(test_results
, kwargs
["all_json"])
485 if kwargs
["untriaged"] is not None:
486 write_untriaged(test_results
, kwargs
["untriaged"])
489 def write_all(test_results
, path
):
490 json_data
= {test_id
: result
.to_json() for test_id
, result
in test_results
.items()}
492 dir_name
= os
.path
.dirname(path
)
493 if not os
.path
.exists(dir_name
):
494 os
.makedirs(dir_name
)
496 with
open(path
, "w") as f
:
497 json
.dump(json_data
, f
, indent
=2)
500 def write_untriaged(test_results
, path
):
501 dir_name
= os
.path
.dirname(path
)
502 if not os
.path
.exists(dir_name
):
503 os
.makedirs(dir_name
)
507 for test_id
, result
in test_results
.items()
508 if not result
.is_triaged()
511 with
open(path
, "w") as f
:
512 for test_id
, result
in data
:
513 f
.write(test_id
+ "\n")
514 for name
, subtest_result
in sorted(result
.subtest_results
.items()):
515 if not subtest_result
.is_triaged():
516 f
.write(" %s\n" % name
)