# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from collections import defaultdict, namedtuple
RunSummary = namedtuple(
"RunSummary",
(
"unexpected_statuses",
"expected_statuses",
"known_intermittent_statuses",
"log_level_counts",
"action_counts",
),
)
class StatusHandler(object):
"""A handler used to determine an overall status for a test run according
to a sequence of log messages."""
def __init__(self):
# The count of each type of unexpected result status (includes tests and subtests)
self.unexpected_statuses = defaultdict(int)
# The count of each type of expected result status (includes tests and subtests)
self.expected_statuses = defaultdict(int)
# The count of known intermittent result statuses (includes tests and subtests)
self.known_intermittent_statuses = defaultdict(int)
# The count of actions logged
self.action_counts = defaultdict(int)
# The count of messages logged at each log level
self.log_level_counts = defaultdict(int)
# The count of "No tests run" error messages seen
self.no_tests_run_count = 0
def __call__(self, data):
action = data["action"]
known_intermittent = data.get("known_intermittent", [])
self.action_counts[action] += 1
if action == "log":
if data["level"] == "ERROR" and data["message"] == "No tests ran":
self.no_tests_run_count += 1
self.log_level_counts[data["level"]] += 1
if action in ("test_status", "test_end"):
status = data["status"]
# Don't count known_intermittent status as unexpected
if "expected" in data and status not in known_intermittent:
self.unexpected_statuses[status] += 1
else:
self.expected_statuses[status] += 1
# Count known_intermittent as expected and intermittent.
if status in known_intermittent:
self.known_intermittent_statuses[status] += 1
if action == "assertion_count":
if data["count"] < data["min_expected"]:
self.unexpected_statuses["PASS"] += 1
elif data["count"] > data["max_expected"]:
self.unexpected_statuses["FAIL"] += 1
elif data["count"]:
self.expected_statuses["FAIL"] += 1
else:
self.expected_statuses["PASS"] += 1
if action == "lsan_leak":
if not data.get("allowed_match"):
self.unexpected_statuses["FAIL"] += 1
if action == "lsan_summary":
if not data.get("allowed", False):
self.unexpected_statuses["FAIL"] += 1
if action == "mozleak_total":
if data["bytes"] is not None and data["bytes"] > data.get("threshold", 0):
self.unexpected_statuses["FAIL"] += 1
def summarize(self):
return RunSummary(
dict(self.unexpected_statuses),
dict(self.expected_statuses),
dict(self.known_intermittent_statuses),
dict(self.log_level_counts),
dict(self.action_counts),
)