Bug 1424287 - [mozlog] added color dictionary and functionality for overriding formatter r=jgraham

The default color dictionary is implemented,to avoid hardcode color information. The functionality for overridding the values in formatter, is hitherto partially
 implemented

Differential Revision: https://phabricator.services.mozilla.com/D23134

--HG--
extra : moz-landing-system : lando
This commit is contained in:
vrinda 2019-03-19 16:23:10 +00:00
parent ab23ebfb0e
commit ae793e05f0

View File

@ -5,10 +5,8 @@
from __future__ import absolute_import
import time
from mozterm import Terminal
import time
from . import base
from .process import strstatus
from .tbplformatter import TbplFormatter
@ -16,6 +14,23 @@ from ..handlers import SummaryHandler
import six
from functools import reduce
color_dict = {
'log_test_status_fail': 'yellow',
'log_process_output': 'blue',
'log_test_status_pass': 'green',
'log_test_status_unexpected_fail': 'red',
'time': 'cyan',
'action': 'yellow',
'pid': 'cyan',
'heading': 'bold_yellow',
'error': 'red',
'warning': 'yellow',
'bold': 'bold',
'grey': 'grey',
'normal': 'normal',
'dim': 'dim'
}
def format_seconds(total):
"""Format number of seconds to MM:SS.DD form."""
@ -23,6 +38,12 @@ def format_seconds(total):
return '%2d:%05.2f' % (minutes, seconds)
class TerminalColors(object):
def __init__(self, term, color_dict):
for key, value in color_dict.iteritems():
setattr(self, key, getattr(term, value))
class MachFormatter(base.BaseFormatter):
def __init__(self, start_time=None, write_interval=False, write_times=True,
@ -39,12 +60,12 @@ class MachFormatter(base.BaseFormatter):
self.status_buffer = {}
self.has_unexpected = {}
self.last_time = None
self.term = Terminal(disable_styling=disable_colors)
self.color_formatter = TerminalColors(
Terminal(disable_styling=disable_colors), color_dict)
self.verbose = verbose
self._known_pids = set()
self.tbpl_formatter = None
self.enable_screenshot = enable_screenshot
self.summary = SummaryHandler()
self.summary_on_shutdown = summary_on_shutdown
@ -55,7 +76,7 @@ class MachFormatter(base.BaseFormatter):
if s is None:
return
time = self.term.dim_blue(format_seconds(self._time(data)))
time = self.color_formatter.time(format_seconds(self._time(data)))
return "%s %s\n" % (time, s)
def _get_test_id(self, data):
@ -75,27 +96,30 @@ class MachFormatter(base.BaseFormatter):
def suite_start(self, data):
num_tests = reduce(lambda x, y: x + len(y), six.itervalues(data['tests']), 0)
action = self.term.yellow(data['action'].upper())
action = self.color_formatter.action(data['action'].upper())
name = ""
if 'name' in data:
name = " %s -" % (data['name'],)
return "%s:%s running %i tests" % (action, name, num_tests)
def suite_end(self, data):
action = self.term.yellow(data['action'].upper())
action = self.color_formatter.action(data['action'].upper())
rv = [action]
if not self.summary_on_shutdown:
rv.append(self._format_suite_summary(self.summary.current_suite, self.summary.current))
rv.append(
self._format_suite_summary(
self.summary.current_suite,
self.summary.current))
return "\n".join(rv)
def _format_expected(self, status, expected):
if status == expected:
color = self.term.green
color = self.color_formatter.log_test_status_pass
if expected not in ("PASS", "OK"):
color = self.term.yellow
color = self.color_formatter.log_test_status_fail
status = "EXPECTED-%s" % status
else:
color = self.term.red
color = self.color_formatter.log_test_status_pass
if status in ("PASS", "OK"):
status = "UNEXPECTED-%s" % status
return color(status)
@ -111,18 +135,22 @@ class MachFormatter(base.BaseFormatter):
return rv
def _format_stack(self, stack):
return "\n%s\n" % self.term.dim(stack.strip("\n"))
return "\n%s\n" % self.color_formatter.dim(stack.strip("\n"))
def _format_suite_summary(self, suite, summary):
count = summary['counts']
logs = summary['unexpected_logs']
rv = ["", self.term.yellow(suite), self.term.yellow("~" * len(suite))]
rv = [
"",
self.color_formatter.log_test_status_fail(suite),
self.color_formatter.log_test_status_fail(
"~" * len(suite))]
# Format check counts
checks = self.summary.aggregate('count', count)
rv.append("Ran {} checks ({})".format(sum(checks.values()),
', '.join(['{} {}s'.format(v, k) for k, v in sorted(checks.items()) if v])))
rv.append("Ran {} checks ({})".format(sum(checks.values()), ', '.join(
['{} {}s'.format(v, k) for k, v in sorted(checks.items()) if v])))
# Format expected counts
checks = self.summary.aggregate('expected', count, include_skip=False)
@ -152,14 +180,15 @@ class MachFormatter(base.BaseFormatter):
# Format status
if not any(count[key]["unexpected"] for key in ('test', 'subtest', 'assert')):
rv.append(self.term.green("OK"))
rv.append(self.color_formatter.log_test_status_pass("OK"))
else:
heading = "Unexpected Results"
rv.extend(["", self.term.yellow(heading), self.term.yellow("-" * len(heading))])
rv.extend(["", self.color_formatter.heading(heading),
self.color_formatter.heading("-" * len(heading))])
if count['subtest']['count']:
for test_id, results in logs.items():
test = self._get_file_name(test_id)
rv.append(self.term.bold(test))
rv.append(self.color_formatter.bold(test))
for data in results:
rv.append(" %s" % self._format_status(test, data).rstrip())
else:
@ -173,7 +202,7 @@ class MachFormatter(base.BaseFormatter):
return "\n".join(rv)
def test_start(self, data):
action = self.term.yellow(data['action'].upper())
action = self.color_formatter.action(data['action'].upper())
return "%s: %s" % (action, self._get_test_id(data))
def test_end(self, data):
@ -215,9 +244,9 @@ class MachFormatter(base.BaseFormatter):
rv += self._format_status(data['test'], d)
if "expected" not in data and not bool(subtests['unexpected']):
color = self.term.green
color = self.color_formatter.log_test_status_pass
else:
color = self.term.red
color = self.color_formatter.log_test_status_pass
action = color(data['action'].upper())
rv = "%s: %s" % (action, rv)
@ -238,18 +267,18 @@ class MachFormatter(base.BaseFormatter):
def lsan_leak(self, data):
allowed = data.get("allowed_match")
if allowed:
prefix = self.term.yellow("FAIL")
prefix = self.color_formatter.log_test_status_fail("FAIL")
else:
prefix = self.term.red("UNEXPECTED-FAIL")
prefix = self.color_formatter.log_test_status_unexpected_fail("UNEXPECTED-FAIL")
return "%s LeakSanitizer: leak at %s" % (prefix, ", ".join(data["frames"]))
def lsan_summary(self, data):
allowed = data.get("allowed", False)
if allowed:
prefix = self.term.yellow("WARNING")
prefix = self.color_formatter.warning("WARNING")
else:
prefix = self.term.red("ERROR")
prefix = self.color_formatter.error("ERROR")
return ("%s | LeakSanitizer | "
"SUMMARY: AddressSanitizer: %d byte(s) leaked in %d allocation(s)." %
@ -275,21 +304,24 @@ class MachFormatter(base.BaseFormatter):
return ("%s ignoring missing output line for total leaks\n" %
data["process"])
status = self.term.red("FAIL")
status = self.color_formatter.log_test_status_pass("FAIL")
return ("%s leakcheck: "
"%s missing output line for total leaks!\n" %
(status, data["process"]))
if data["bytes"] == 0:
return ("%s leakcheck: %s no leaks detected!\n" %
(self.term.green("PASS"), data["process"]))
return (
"%s leakcheck: %s no leaks detected!\n" %
(self.color_formatter.log_test_status_pass("PASS"),
data["process"]))
message = "leakcheck: %s %d bytes leaked\n" % (data["process"], data["bytes"])
# data["bytes"] will include any expected leaks, so it can be off
# by a few thousand bytes.
failure = data["bytes"] > data["threshold"]
status = self.term.red("UNEXPECTED-FAIL") if failure else self.term.yellow("FAIL")
status = self.color_formatter.log_test_status_pass(
"UNEXPECTED-FAIL") if failure else self.color_formatter.log_test_status_fail("FAIL")
return "%s %s\n" % (status, message)
def test_status(self, data):
@ -316,9 +348,9 @@ class MachFormatter(base.BaseFormatter):
else:
expected = "%i" % data["min_expected"]
action = self.term.red("ASSERT")
action = self.color_formatter.log_test_status_pass("ASSERT")
return "%s: Assertion count %i, expected %s assertions\n" % (
action, data["count"], expected)
action, data["count"], expected)
def process_output(self, data):
rv = []
@ -326,7 +358,7 @@ class MachFormatter(base.BaseFormatter):
pid = data['process']
if pid.isdigit():
pid = 'pid:%s' % pid
pid = self.term.dim_cyan(pid)
pid = self.color_formatter.pid(pid)
if "command" in data and data["process"] not in self._known_pids:
self._known_pids.add(data["process"])
@ -366,7 +398,7 @@ class MachFormatter(base.BaseFormatter):
if not rv[-1] == "\n":
rv += "\n"
action = self.term.red(data['action'].upper())
action = self.color_formatter.action(data['action'].upper())
return "%s: %s" % (action, rv)
def process_start(self, data):
@ -383,11 +415,11 @@ class MachFormatter(base.BaseFormatter):
level = data.get("level").upper()
if level in ("CRITICAL", "ERROR"):
level = self.term.red(level)
level = self.color_formatter.error(level)
elif level == "WARNING":
level = self.term.yellow(level)
level = self.color_formatter.warning(level)
elif level == "INFO":
level = self.term.blue(level)
level = self.color_formatter.log_process_output(level)
if data.get('component'):
rv = " ".join([data["component"], level, data["message"]])
@ -404,9 +436,10 @@ class MachFormatter(base.BaseFormatter):
" {c1}{rule}({linter}){normal}"
message = fmt.format(
path=data["path"],
normal=self.term.normal,
c1=self.term.grey,
c2=self.term.red if data["level"] == 'error' else self.term.yellow,
normal=self.color_formatter.normal,
c1=self.color_formatter.grey,
c2=self.color_formatter.error if data["level"] == 'error' else (
self.color_formatter.log_test_status_fail),
lineno=str(data["lineno"]),
column=(":" + str(data["column"])) if data.get("column") else "",
level=data["level"],
@ -422,7 +455,9 @@ class MachFormatter(base.BaseFormatter):
return
heading = "Overall Summary"
rv = ["", self.term.bold_yellow(heading), self.term.bold_yellow("=" * len(heading))]
rv = [
"", self.color_formatter.heading(heading), self.color_formatter.heading(
"=" * len(heading))]
for suite, summary in self.summary:
rv.append(self._format_suite_summary(suite, summary))
return "\n".join(rv)