diff --git a/.gitlab-ci/lava/lava_job_submitter.py b/.gitlab-ci/lava/lava_job_submitter.py index 4c5fba51eba..8a1968b7eb9 100755 --- a/.gitlab-ci/lava/lava_job_submitter.py +++ b/.gitlab-ci/lava/lava_job_submitter.py @@ -72,7 +72,8 @@ CONSOLE_LOG_COLOR_RESET = "\x1b[0m" def print_log(msg): - print("{}: {}".format(datetime.now(), msg)) + # Reset color from timestamp, since `msg` can tint the terminal color + print(f"{CONSOLE_LOG_COLOR_RESET}{datetime.now()}: {msg}") def fatal_err(msg): @@ -370,9 +371,22 @@ def show_job_data(job): print("{}\t: {}".format(field, value)) +def fix_lava_color_log(line): + """This function is a temporary solution for the color escape codes mangling + problem. There is some problem in message passing between the LAVA + dispatcher and the device under test (DUT). Here \x1b character is missing + before `[:digit::digit:?m` ANSI TTY color codes. When this problem is fixed + on the LAVA side, one should remove this function. + """ + line["msg"] = re.sub(r"(\[\d{1,2}m)", "\x1b" + r"\1", line["msg"]) + + def parse_lava_lines(new_lines) -> list[str]: parsed_lines: list[str] = [] for line in new_lines: + prefix = "" + suffix = "" + if line["lvl"] in ["results", "feedback"]: continue elif line["lvl"] in ["warning", "error"]: @@ -381,9 +395,10 @@ def parse_lava_lines(new_lines) -> list[str]: elif line["lvl"] == "input": prefix = "$ " suffix = "" - else: - prefix = "" - suffix = "" + elif line["lvl"] == "target": + fix_lava_color_log(line) + fix_lava_gitlab_section_log(line) + line = f'{prefix}{line["msg"]}{suffix}' parsed_lines.append(line) diff --git a/.gitlab-ci/tests/test_lava_job_submitter.py b/.gitlab-ci/tests/test_lava_job_submitter.py index ee9684a12e3..cfb670a9e68 100644 --- a/.gitlab-ci/tests/test_lava_job_submitter.py +++ b/.gitlab-ci/tests/test_lava_job_submitter.py @@ -26,7 +26,7 @@ import xmlrpc.client from contextlib import nullcontext as does_not_raise from datetime import datetime from itertools import cycle, repeat -from typing import Generator, Iterable, Tuple, Union +from typing import Callable, Generator, Iterable, Tuple, Union from unittest.mock import MagicMock, patch import pytest @@ -37,6 +37,7 @@ from lava.lava_job_submitter import ( DEVICE_HANGING_TIMEOUT_SEC, NUMBER_OF_RETRIES_TIMEOUT_DETECTION, LAVAJob, + fix_lava_color_log, follow_job_execution, hide_sensitive_data, retriable_follow_job, @@ -45,6 +46,12 @@ from lava.lava_job_submitter import ( NUMBER_OF_MAX_ATTEMPTS = NUMBER_OF_RETRIES_TIMEOUT_DETECTION + 1 +def create_lava_yaml_msg( + dt: Callable = datetime.now, msg="test", lvl="target" +) -> dict[str, str]: + return {"dt": str(dt()), "msg": msg, "lvl": lvl} + + def jobs_logs_response(finished=False, msg=None, lvl="target", result=None) -> Tuple[bool, str]: timed_msg = {"dt": str(datetime.now()), "msg": "New message", "lvl": lvl} if result: @@ -342,3 +349,29 @@ def test_log_corruption(mock_sleep, data_sequence, expected_exception, mock_prox proxy_logs_mock.side_effect = data_sequence with expected_exception: retriable_follow_job(proxy_mock, "") + + +COLOR_MANGLED_SCENARIOS = { + "Mangled error message at target level": ( + create_lava_yaml_msg(msg="[0m[0m[31mERROR - dEQP error: ", lvl="target"), + "\x1b[0m\x1b[0m\x1b[31mERROR - dEQP error: ", + ), + "Mangled pass message at target level": ( + create_lava_yaml_msg( + msg="[0mPass: 26718, ExpectedFail: 95, Skip: 25187, Duration: 8:18, Remaining: 13", + lvl="target", + ), + "\x1b[0mPass: 26718, ExpectedFail: 95, Skip: 25187, Duration: 8:18, Remaining: 13", + ), +} + + +@pytest.mark.parametrize( + "message, fixed_message", + COLOR_MANGLED_SCENARIOS.values(), + ids=COLOR_MANGLED_SCENARIOS.keys(), +) +def test_fix_lava_color_log(message, fixed_message): + fix_lava_color_log(message) + + assert message["msg"] == fixed_message