From db6604bdff086dd9acb1751b115976c0e8212bc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milo=C5=A1=20Prchl=C3=ADk?= Date: Thu, 29 Aug 2024 20:02:51 +0200 Subject: [PATCH] Move timestamp/duration formatting to utils [Step results #5] (#3019) We will need them from other places, it's not longer just `execute` who will be dealign with times. Co-authored-by: Martin Hoyer --- tests/unit/test_utils.py | 4 ++-- tmt/checks/avc.py | 12 +++++++---- tmt/checks/dmesg.py | 6 ++---- tmt/checks/watchdog.py | 5 ++--- tmt/steps/execute/__init__.py | 38 ++++++++++++++++------------------- tmt/steps/execute/internal.py | 8 +++++--- tmt/utils/__init__.py | 18 +++++++++++++++++ 7 files changed, 54 insertions(+), 37 deletions(-) diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index 43372e1338..5671487a5f 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -1176,9 +1176,9 @@ def test_flatten(lists: list[list[Any]], unique: bool, expected: list[Any]) -> N ] ) def test_format_duration(duration, expected): - from tmt.steps.execute import ExecutePlugin + from tmt.utils import format_duration - assert ExecutePlugin.format_duration(duration) == expected + assert format_duration(duration) == expected def test_filter_paths(source_dir): diff --git a/tmt/checks/avc.py b/tmt/checks/avc.py index 7782cf5a9c..59f6120898 100644 --- a/tmt/checks/avc.py +++ b/tmt/checks/avc.py @@ -8,7 +8,13 @@ import tmt.utils from tmt.checks import Check, CheckPlugin, provides_check from tmt.result import CheckResult, ResultOutcome -from tmt.utils import CommandOutput, Path, ShellScript, render_run_exception_streams +from tmt.utils import ( + CommandOutput, + Path, + ShellScript, + format_timestamp, + render_run_exception_streams, + ) if TYPE_CHECKING: import tmt.base @@ -48,12 +54,10 @@ def _save_report( :returns: path to the report file. """ - from tmt.steps.execute import ExecutePlugin - report_filepath = invocation.check_files_path / TEST_POST_AVC_FILENAME report = [ - f'# Reported at {ExecutePlugin.format_timestamp(timestamp)}', + f'# Reported at {format_timestamp(timestamp)}', *report ] diff --git a/tmt/checks/dmesg.py b/tmt/checks/dmesg.py index 4730effbb2..540b167a3e 100644 --- a/tmt/checks/dmesg.py +++ b/tmt/checks/dmesg.py @@ -11,7 +11,7 @@ from tmt.checks import Check, CheckEvent, CheckPlugin, _RawCheck, provides_check from tmt.result import CheckResult, ResultOutcome from tmt.steps.provision import GuestCapability -from tmt.utils import Path, field, render_run_exception_streams +from tmt.utils import Path, field, format_timestamp, render_run_exception_streams if TYPE_CHECKING: import tmt.base @@ -95,11 +95,9 @@ def _save_dmesg( event: CheckEvent, logger: tmt.log.Logger) -> tuple[ResultOutcome, Path]: - from tmt.steps.execute import ExecutePlugin - assert invocation.phase.step.workdir is not None # narrow type - timestamp = ExecutePlugin.format_timestamp(datetime.datetime.now(datetime.timezone.utc)) + timestamp = format_timestamp(datetime.datetime.now(datetime.timezone.utc)) path = invocation.check_files_path / TEST_POST_DMESG_FILENAME.format(event=event.value) diff --git a/tmt/checks/watchdog.py b/tmt/checks/watchdog.py index f803bd8dda..9d61560b69 100644 --- a/tmt/checks/watchdog.py +++ b/tmt/checks/watchdog.py @@ -18,7 +18,7 @@ import tmt.utils from tmt.checks import Check, CheckPlugin, provides_check from tmt.result import CheckResult, ResultOutcome -from tmt.utils import Path, field, render_run_exception_streams +from tmt.utils import Path, field, format_timestamp, render_run_exception_streams if TYPE_CHECKING: from tmt.steps.execute import TestInvocation @@ -68,8 +68,7 @@ def report_progress( ``report`` lines are written into it. """ - timestamp = tmt.steps.execute.ExecutePlugin.format_timestamp( - datetime.datetime.now(datetime.timezone.utc)) + timestamp = format_timestamp(datetime.datetime.now(datetime.timezone.utc)) with open(log, mode='a') as f: f.write(f'# {check_name} reported at {timestamp}\n') diff --git a/tmt/steps/execute/__init__.py b/tmt/steps/execute/__init__.py index cd960779a1..f1da4e7d52 100644 --- a/tmt/steps/execute/__init__.py +++ b/tmt/steps/execute/__init__.py @@ -1,6 +1,5 @@ import copy import dataclasses -import datetime import functools import json import os @@ -27,7 +26,14 @@ from tmt.steps import Action, ActionTask, PhaseQueue, PluginTask, Step from tmt.steps.discover import Discover, DiscoverPlugin, DiscoverStepData from tmt.steps.provision import Guest -from tmt.utils import Path, ShellScript, Stopwatch, field +from tmt.utils import ( + Path, + ShellScript, + Stopwatch, + field, + format_duration, + format_timestamp, + ) if TYPE_CHECKING: import tmt.cli @@ -904,23 +910,13 @@ def extract_results( return invocation.test.test_framework.extract_results(invocation, logger) - @staticmethod - def format_timestamp(timestamp: datetime.datetime) -> str: - """ Convert timestamp to a human readable format """ - - return timestamp.isoformat() - - @staticmethod - def format_duration(duration: datetime.timedelta) -> str: - """ Convert duration to a human readable format """ - - # A helper variable to hold the duration while we cut away days, hours and seconds. - counter = int(duration.total_seconds()) - - hours, counter = divmod(counter, 3600) - minutes, seconds = divmod(counter, 60) + def check_abort_file(self, invocation: TestInvocation) -> bool: + """ + Check for an abort file created by tmt-abort - return f'{hours:02}:{minutes:02}:{seconds:02}' + Returns whether an abort file is present (i.e. abort occurred). + """ + return (invocation.test_data_path / TMT_ABORT_SCRIPT.created_file).exists() def timeout_hint(self, invocation: TestInvocation) -> None: """ Append a duration increase hint to the test output """ @@ -957,9 +953,9 @@ def _run_checks_for_test( for result in check_results: result.event = event - result.start_time = self.format_timestamp(timer.start_time) - result.end_time = self.format_timestamp(timer.end_time) - result.duration = self.format_duration(timer.duration) + result.start_time = format_timestamp(timer.start_time) + result.end_time = format_timestamp(timer.end_time) + result.duration = format_duration(timer.duration) results += check_results diff --git a/tmt/steps/execute/internal.py b/tmt/steps/execute/internal.py index e501be7b18..595d7b68e8 100644 --- a/tmt/steps/execute/internal.py +++ b/tmt/steps/execute/internal.py @@ -26,6 +26,8 @@ ShellScript, Stopwatch, field, + format_duration, + format_timestamp, ) TEST_PIDFILE_FILENAME = 'tmt-test.pid' @@ -387,7 +389,7 @@ def _save_process( # Execute the test, save the output and return code with Stopwatch() as timer: - invocation.start_time = self.format_timestamp(timer.start_time) + invocation.start_time = format_timestamp(timer.start_time) timeout: Optional[int] @@ -429,8 +431,8 @@ def _save_process( with invocation.process_lock: invocation.process = None - invocation.end_time = self.format_timestamp(timer.end_time) - invocation.real_duration = self.format_duration(timer.duration) + invocation.end_time = format_timestamp(timer.end_time) + invocation.real_duration = format_duration(timer.duration) # Save the captured output. Do not let the follow-up pulls # overwrite it. diff --git a/tmt/utils/__init__.py b/tmt/utils/__init__.py index 6a8b2ae134..942039e09f 100644 --- a/tmt/utils/__init__.py +++ b/tmt/utils/__init__.py @@ -6375,6 +6375,24 @@ def duration(self) -> datetime.timedelta: return self.end_time - self.start_time +def format_timestamp(timestamp: datetime.datetime) -> str: + """ Convert timestamp to a human readable format """ + + return timestamp.isoformat() + + +def format_duration(duration: datetime.timedelta) -> str: + """ Convert duration to a human readable format """ + + # A helper variable to hold the duration while we cut away days, hours and seconds. + counter = int(duration.total_seconds()) + + hours, counter = divmod(counter, 3600) + minutes, seconds = divmod(counter, 60) + + return f'{hours:02}:{minutes:02}:{seconds:02}' + + def retry( func: Callable[..., T], attempts: int,