diff --git a/avocado/core/task/runtime.py b/avocado/core/task/runtime.py index e78c84bdde..7bfe277295 100644 --- a/avocado/core/task/runtime.py +++ b/avocado/core/task/runtime.py @@ -1,4 +1,5 @@ from copy import deepcopy +from enum import Enum from itertools import chain from avocado.core.dispatcher import TestPreDispatcher @@ -7,6 +8,23 @@ from avocado.core.varianter import dump_variant +class RuntimeTaskStatus(Enum): + wait_dependencies = 'WAITING DEPENDENCIES' + wait = 'WAITING' + finished = 'FINISHED' + timeout = 'FINISHED TIMEOUT' + in_cache = 'FINISHED IN CACHE' + failfast = 'FINISHED FAILFAST' + fail_triage = 'FAILED ON TRIAGE' + fail_start = 'FAILED ON START' + started = 'STARTED ' + + @staticmethod + def finished_statuses(): + return [status for _, status in RuntimeTaskStatus.__members__.items() + if "FINISHED" in status.value] + + class RuntimeTask: """Task with extra status information on its life cycle status. @@ -57,14 +75,14 @@ def __eq__(self, other): def are_dependencies_finished(self): for dependency in self.dependencies: - if not dependency.status or "FINISHED" not in dependency.status: + if dependency.status not in RuntimeTaskStatus.finished_statuses(): return False return True def get_finished_dependencies(self): """Returns all dependencies which already finished.""" return [dep for dep in self.dependencies if - dep.status and "FINISHED" in dep.status] + dep.status in RuntimeTaskStatus.finished_statuses()] def can_run(self): if not self.are_dependencies_finished(): diff --git a/avocado/core/task/statemachine.py b/avocado/core/task/statemachine.py index db60e67d3e..98227e5ef2 100644 --- a/avocado/core/task/statemachine.py +++ b/avocado/core/task/statemachine.py @@ -5,6 +5,7 @@ import time from avocado.core.exceptions import TestFailFast +from avocado.core.task.runtime import RuntimeTaskStatus from avocado.core.teststatus import STATUSES_NOT_OK LOG = logging.getLogger(__name__) @@ -110,7 +111,7 @@ async def finish_task(self, runtime_task, status_reason=None): if runtime_task not in self.finished: if status_reason: runtime_task.status = status_reason - LOG.debug('Task "%s" finished: %s', + LOG.debug('Task "%s" finished with status: %s', runtime_task.task.identifier, status_reason) else: LOG.debug('Task "%s" finished', runtime_task.task.identifier) @@ -164,7 +165,7 @@ async def triage(self): return # a task waiting requirements already checked its requirements - if runtime_task.status != 'WAITING DEPENDENCIES': + if runtime_task.status != RuntimeTaskStatus.wait_dependencies: # check for requirements a task may have requirements_ok = ( await self._spawner.check_task_requirements(runtime_task)) @@ -172,8 +173,8 @@ async def triage(self): LOG.debug('Task "%s": requirements OK (will proceed to check ' 'dependencies)', runtime_task.task.identifier) else: - await self._state_machine.finish_task(runtime_task, - "FAILED ON TRIAGE") + await self._state_machine.finish_task( + runtime_task, RuntimeTaskStatus.fail_triage) return # handle task dependencies @@ -182,7 +183,7 @@ async def triage(self): if not runtime_task.are_dependencies_finished(): async with self._state_machine.lock: self._state_machine.triaging.append(runtime_task) - runtime_task.status = 'WAITING DEPENDENCIES' + runtime_task.status = RuntimeTaskStatus.wait_dependencies await asyncio.sleep(0.1) return @@ -200,13 +201,13 @@ async def triage(self): if is_task_in_cache is None: async with self._state_machine.lock: self._state_machine.triaging.append(runtime_task) - runtime_task.status = 'WAITING' + runtime_task.status = RuntimeTaskStatus.wait await asyncio.sleep(0.1) return if is_task_in_cache: await self._state_machine.finish_task( - runtime_task, "FINISHED: Task in cache") + runtime_task, RuntimeTaskStatus.in_cache) runtime_task.result = 'pass' return @@ -232,7 +233,7 @@ async def start(self): async with self._state_machine.lock: if len(self._state_machine.started) >= self._max_running: self._state_machine.ready.insert(0, runtime_task) - runtime_task.status = 'WAITING' + runtime_task.status = RuntimeTaskStatus.wait should_wait = True if should_wait: await asyncio.sleep(0.1) @@ -244,14 +245,14 @@ async def start(self): if start_ok: LOG.debug('Task "%s": spawned successfully', runtime_task.task.identifier) - runtime_task.status = None + runtime_task.status = RuntimeTaskStatus.started if self._task_timeout is not None: runtime_task.execution_timeout = time.monotonic() + self._task_timeout async with self._state_machine.lock: self._state_machine.started.append(runtime_task) else: await self._state_machine.finish_task(runtime_task, - "FAILED ON START") + RuntimeTaskStatus.fail_start) async def monitor(self): """Reads from started, moves into finished.""" @@ -270,7 +271,7 @@ async def monitor(self): await asyncio.wait_for(self._spawner.wait_task(runtime_task), remaining) except asyncio.TimeoutError: - runtime_task.status = 'FINISHED W/ TIMEOUT' + runtime_task.status = RuntimeTaskStatus.timeout await self._spawner.terminate_task(runtime_task) message = {'status': 'finished', 'result': 'interrupted', @@ -298,10 +299,11 @@ async def monitor(self): result_stats = set(key.upper()for key in self._state_machine._status_repo.result_stats.keys()) if self._failfast and not result_stats.isdisjoint(STATUSES_NOT_OK): - await self._state_machine.abort("FAILFAST is enabled") + await self._state_machine.abort(RuntimeTaskStatus.failfast) raise TestFailFast("Interrupting job (failfast).") - await self._state_machine.finish_task(runtime_task, "FINISHED") + await self._state_machine.finish_task(runtime_task, + RuntimeTaskStatus.finished) async def run(self): """Pushes Tasks forward and makes them do something with their lives."""