From c53bfe2a4ced3087e824fe626cc469239e94b47a Mon Sep 17 00:00:00 2001 From: Pablo Goitia Date: Mon, 8 Jul 2024 15:05:19 +0200 Subject: [PATCH] Fixed bug and changed tests to pytest --- autosubmit/autosubmit.py | 19 +++---- autosubmit/profiler/profiler.py | 44 ++++++++++++---- test/unit/test_profiler.py | 93 +++++++++++++++++---------------- 3 files changed, 92 insertions(+), 64 deletions(-) diff --git a/autosubmit/autosubmit.py b/autosubmit/autosubmit.py index 627c2bb31..786ce1695 100644 --- a/autosubmit/autosubmit.py +++ b/autosubmit/autosubmit.py @@ -2625,14 +2625,18 @@ class Autosubmit: expid, as_conf, notransitive=notransitive, monitor=True, new=False) Log.debug("Job list restored from {0} files", pkl_dir) except AutosubmitError as e: + if profile: + profiler.stop() raise AutosubmitCritical(e.message, e.code, e.trace) except AutosubmitCritical as e: + if profile: + profiler.stop() raise except BaseException as e: - raise - finally: if profile: profiler.stop() + raise + try: jobs = [] if not isinstance(job_list, type([])): @@ -2690,10 +2694,9 @@ class Autosubmit: else: jobs = job_list.get_job_list() except BaseException as e: - raise AutosubmitCritical("Issues during the job_list generation. Maybe due I/O error", 7040, str(e)) - finally: if profile: profiler.stop() + raise AutosubmitCritical("Issues during the job_list generation. Maybe due I/O error", 7040, str(e)) # WRAPPERS try: @@ -2718,10 +2721,9 @@ class Autosubmit: packages = JobPackagePersistence(os.path.join(BasicConfig.LOCAL_ROOT_DIR, expid, "pkl"), "job_packages_" + expid).load() except BaseException as e: - raise AutosubmitCritical("Issues during the wrapper loading, may be related to IO issues", 7040, str(e)) - finally: if profile: profiler.stop() + raise AutosubmitCritical("Issues during the wrapper loading, may be related to IO issues", 7040, str(e)) groups_dict = dict() try: @@ -2735,12 +2737,11 @@ class Autosubmit: jobs), job_list, expand_list=expand, expanded_status=status) groups_dict = job_grouping.group_jobs() except BaseException as e: + if profile: + profiler.stop() raise AutosubmitCritical( "Jobs can't be grouped, perhaps you're using an invalid format. Take a look into readthedocs", 7011, str(e)) - finally: - if profile: - profiler.stop() monitor_exp = Monitor() try: diff --git a/autosubmit/profiler/profiler.py b/autosubmit/profiler/profiler.py index b73ccea09..a16aa7633 100644 --- a/autosubmit/profiler/profiler.py +++ b/autosubmit/profiler/profiler.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2015-2023 Earth Sciences Department, BSC-CNS +# Copyright 2015-2024 Earth Sciences Department, BSC-CNS # This file is part of Autosubmit. @@ -21,6 +21,7 @@ import cProfile import io import os import pstats +from enum import Enum from datetime import datetime from pathlib import Path from pstats import SortKey @@ -33,6 +34,11 @@ from log.log import Log, AutosubmitCritical _UNITS = ["B", "KiB", "MiB", "GiB", "TiB", "PiB"] +class ProfilerState(Enum): + """Enumeration of profiler states""" + STOPPED = "stopped" + STARTED = "started" + class Profiler: """Class to profile the execution of experiments.""" @@ -44,28 +50,47 @@ class Profiler: self._mem_init = 0 self._mem_final = 0 - # Error handling flags - self._started = False - self._finished = False + # Error handling + self._state = ProfilerState.STOPPED + + @property + def started(self): + """ + Check if the profiler is in the started state. + + Returns: + bool: True if the profiler is in the started state, False otherwise. + """ + return self._state == ProfilerState.STARTED + + @property + def stopped(self): + """ + Check if the profiler is in the stopped state. + + Returns: + bool: True if the profiler is in the stopped state, False otherwise. + """ + return self._state == ProfilerState.STOPPED def start(self) -> None: """Function to start the profiling process.""" - if self._started: + if self.started: raise AutosubmitCritical('The profiling process was already started.', 7074) - self._started = True + self._state = ProfilerState.STARTED self._profiler.enable() self._mem_init += _get_current_memory() def stop(self) -> None: """Function to finish the profiling process.""" - if not self._started or self._finished: - raise AutosubmitCritical('Cannot stop the profiler because was not running.', 7074) + if not self.started or self.stopped: + raise AutosubmitCritical('Cannot stop the profiler because it was not running.', 7074) self._profiler.disable() self._mem_final += _get_current_memory() self._report() - self._finished = True + self._state = ProfilerState.STOPPED def _report(self) -> None: """Function to print the final report into the stdout, log and filesystem.""" @@ -128,7 +153,6 @@ def _generate_title(title="") -> str: message = title.center(max_len) return "\n".join([separator, message, separator]) - def _get_current_memory() -> int: """ Return the current memory consumption of the process in Bytes. diff --git a/test/unit/test_profiler.py b/test/unit/test_profiler.py index cf99067ea..c4d25783b 100644 --- a/test/unit/test_profiler.py +++ b/test/unit/test_profiler.py @@ -1,48 +1,51 @@ -from unittest import TestCase, mock +import pytest from autosubmit.profiler.profiler import Profiler from log.log import AutosubmitCritical - -class TestProfiler(TestCase): - def setUp(self): - self.profiler = Profiler("a000") - - # Black box techniques for status machine based software - # - # O---->__init__------> start - # | - # | - # stop ----> report --->0 - - # Transition coverage - def test_transitions(self): - # __init__ -> start - self.profiler.start() - - # start -> stop - self.profiler.stop() - - def test_transitions_fail_cases(self): - # __init__ -> stop - self.assertRaises(AutosubmitCritical, self.profiler.stop) - - # start -> start - self.profiler.start() - self.assertRaises(AutosubmitCritical, self.profiler.start) - - # stop -> stop - self.profiler.stop() - self.assertRaises(AutosubmitCritical, self.profiler.stop) - - # White box tests - @mock.patch("os.access") - def test_writing_permission_check_fails(self, mock_response): - mock_response.return_value = False - - self.profiler.start() - self.assertRaises(AutosubmitCritical, self.profiler.stop) - - def test_memory_profiling_loop(self): - self.profiler.start() - bytearray(1024*1024) - self.profiler.stop() +@pytest.fixture +def profiler(): + """ Creates a profiler object and yields it to the test. """ + yield Profiler("a000") + +# Black box techniques for status machine based software +# +# O--->__init__----> start +# | +# | +# stop (----> report) --->0 + +# Transition coverage +def test_transitions(profiler): + # __init__ -> start + profiler.start() + + # start -> stop + profiler.stop() + +def test_transitions_fail_cases(profiler): + # __init__ -> stop + with pytest.raises(AutosubmitCritical): + profiler.stop() + + # start -> start + profiler.start() + with pytest.raises(AutosubmitCritical): + profiler.start() + + # stop -> stop + profiler.stop() + with pytest.raises(AutosubmitCritical): + profiler.stop() + +# White box tests +def test_writing_permission_check_fails(profiler, mocker): + mocker.patch("os.access", return_value=False) + + profiler.start() + with pytest.raises(AutosubmitCritical): + profiler.stop() + +def test_memory_profiling_loop(profiler): + profiler.start() + bytearray(1024*1024) + profiler.stop() -- GitLab