From c3486d607de7925741887ea38a119e660a76cb8a Mon Sep 17 00:00:00 2001 From: Eli Zrihen Date: Wed, 11 Dec 2024 13:53:15 +0200 Subject: [PATCH 1/5] Allow graceful termination if run_if throws exception --- openhtf/core/phase_executor.py | 14 +++++++++++--- test/core/phase_executor_test.py | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/openhtf/core/phase_executor.py b/openhtf/core/phase_executor.py index d0eca616..b97a979e 100644 --- a/openhtf/core/phase_executor.py +++ b/openhtf/core/phase_executor.py @@ -303,10 +303,18 @@ def _execute_phase_once( ) -> Tuple[PhaseExecutionOutcome, Optional[pstats.Stats]]: """Executes the given phase, returning a PhaseExecutionOutcome.""" # Check this before we create a PhaseState and PhaseRecord. - if phase_desc.options.run_if and not phase_desc.options.run_if(): - self.logger.debug('Phase %s skipped due to run_if returning falsey.', + if phase_desc.options.run_if: + try: + run_phase = phase_desc.options.run_if() + except Exception: # pylint: disable=broad-except + # Allow graceful termination + return PhaseExecutionOutcome(phase_descriptor.PhaseResult.STOP), None + + if not run_phase: + self.logger.debug('Phase %s skipped due to run_if returning falsey.', phase_desc.name) - return PhaseExecutionOutcome(phase_descriptor.PhaseResult.SKIP), None + return PhaseExecutionOutcome(phase_descriptor.PhaseResult.SKIP), None + override_result = None with self.test_state.running_phase_context(phase_desc) as phase_state: diff --git a/test/core/phase_executor_test.py b/test/core/phase_executor_test.py index 86379568..8a36afbc 100644 --- a/test/core/phase_executor_test.py +++ b/test/core/phase_executor_test.py @@ -18,7 +18,15 @@ import openhtf from openhtf.core import phase_descriptor +from openhtf.core import test_record +def run_if_with_exception(): + raise Exception("run_if_with_exception") + +_tr = None +def final(tr): + global _tr + _tr = tr class PhaseExecutorTest(unittest.TestCase): @@ -54,3 +62,14 @@ def test_execute_phase_with_repeat_limit_max_exceeds_default_limit(self): openhtf.PhaseOptions(repeat_limit=phase_descriptor.MAX_REPEAT_LIMIT), expected_call_count=phase_descriptor.DEFAULT_REPEAT_LIMIT + 1, ) + + def test_execute_phase_when_run_if_throws_exception(self): + + @openhtf.PhaseOptions(run_if=run_if_with_exception) + def phase(): + pass + + test = openhtf.Test(phase) + test.add_output_callbacks(final) + test.execute() + self.assertEqual(_tr.outcome, test_record.Outcome.PASS) From 751f7706337393c3847555ebeeb2e46ebb167378 Mon Sep 17 00:00:00 2001 From: Eli Zrihen Date: Wed, 11 Dec 2024 16:37:42 +0200 Subject: [PATCH 2/5] Returning exception to make sure test is stopped on error --- openhtf/core/phase_executor.py | 4 +++- test/core/phase_executor_test.py | 17 +++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/openhtf/core/phase_executor.py b/openhtf/core/phase_executor.py index b97a979e..63396ba4 100644 --- a/openhtf/core/phase_executor.py +++ b/openhtf/core/phase_executor.py @@ -307,8 +307,10 @@ def _execute_phase_once( try: run_phase = phase_desc.options.run_if() except Exception: # pylint: disable=broad-except + self.logger.debug('Phase %s stopped due to a fault in run_if function.', + phase_desc.name) # Allow graceful termination - return PhaseExecutionOutcome(phase_descriptor.PhaseResult.STOP), None + return PhaseExecutionOutcome(ExceptionInfo(*sys.exc_info())), None if not run_phase: self.logger.debug('Phase %s skipped due to run_if returning falsey.', diff --git a/test/core/phase_executor_test.py b/test/core/phase_executor_test.py index 8a36afbc..9110fa12 100644 --- a/test/core/phase_executor_test.py +++ b/test/core/phase_executor_test.py @@ -20,13 +20,6 @@ from openhtf.core import phase_descriptor from openhtf.core import test_record -def run_if_with_exception(): - raise Exception("run_if_with_exception") - -_tr = None -def final(tr): - global _tr - _tr = tr class PhaseExecutorTest(unittest.TestCase): @@ -65,11 +58,19 @@ def test_execute_phase_with_repeat_limit_max_exceeds_default_limit(self): def test_execute_phase_when_run_if_throws_exception(self): + def run_if_with_exception(): + raise Exception("run_if_with_exception") + @openhtf.PhaseOptions(run_if=run_if_with_exception) def phase(): pass + _tr = None + def final(tr): + nonlocal _tr + _tr = tr + test = openhtf.Test(phase) test.add_output_callbacks(final) test.execute() - self.assertEqual(_tr.outcome, test_record.Outcome.PASS) + self.assertEqual(_tr.outcome, test_record.Outcome.ERROR) From a5c12a75b030f1a2b24ea02ae005950b06eaa485 Mon Sep 17 00:00:00 2001 From: Eli Zrihen Date: Wed, 11 Dec 2024 21:45:33 +0200 Subject: [PATCH 3/5] Fixed indentation --- openhtf/core/phase_executor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openhtf/core/phase_executor.py b/openhtf/core/phase_executor.py index 63396ba4..f68924cb 100644 --- a/openhtf/core/phase_executor.py +++ b/openhtf/core/phase_executor.py @@ -308,13 +308,13 @@ def _execute_phase_once( run_phase = phase_desc.options.run_if() except Exception: # pylint: disable=broad-except self.logger.debug('Phase %s stopped due to a fault in run_if function.', - phase_desc.name) + phase_desc.name) # Allow graceful termination return PhaseExecutionOutcome(ExceptionInfo(*sys.exc_info())), None if not run_phase: self.logger.debug('Phase %s skipped due to run_if returning falsey.', - phase_desc.name) + phase_desc.name) return PhaseExecutionOutcome(phase_descriptor.PhaseResult.SKIP), None From c9219a3c5825af006844845ed3e7807141851c13 Mon Sep 17 00:00:00 2001 From: Eli Zrihen Date: Wed, 11 Dec 2024 21:46:06 +0200 Subject: [PATCH 4/5] Using htf_test for unit testing --- test/core/phase_executor_test.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/test/core/phase_executor_test.py b/test/core/phase_executor_test.py index 9110fa12..be46a65b 100644 --- a/test/core/phase_executor_test.py +++ b/test/core/phase_executor_test.py @@ -19,7 +19,7 @@ import openhtf from openhtf.core import phase_descriptor from openhtf.core import test_record - +from openhtf.util import test as htf_test class PhaseExecutorTest(unittest.TestCase): @@ -56,21 +56,18 @@ def test_execute_phase_with_repeat_limit_max_exceeds_default_limit(self): expected_call_count=phase_descriptor.DEFAULT_REPEAT_LIMIT + 1, ) + +class PhaseExecuterRunIfTest(htf_test.TestCase): + def test_execute_phase_when_run_if_throws_exception(self): def run_if_with_exception(): raise Exception("run_if_with_exception") - @openhtf.PhaseOptions(run_if=run_if_with_exception) - def phase(): + def phase_excp_run_if(): pass - _tr = None - def final(tr): - nonlocal _tr - _tr = tr - - test = openhtf.Test(phase) - test.add_output_callbacks(final) - test.execute() - self.assertEqual(_tr.outcome, test_record.Outcome.ERROR) + phase = openhtf.PhaseOptions(run_if=run_if_with_exception)( + phase_excp_run_if) + record = self.execute_phase_or_test(openhtf.Test(phase)) + self.assertEqual(record.outcome, test_record.Outcome.ERROR) From 662ca1f0127c4320c00c1dc00e154a28fd267286 Mon Sep 17 00:00:00 2001 From: Eli Zrihen Date: Wed, 11 Dec 2024 21:52:31 +0200 Subject: [PATCH 5/5] using assertTestError --- test/core/phase_executor_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/core/phase_executor_test.py b/test/core/phase_executor_test.py index be46a65b..9f2b2c35 100644 --- a/test/core/phase_executor_test.py +++ b/test/core/phase_executor_test.py @@ -70,4 +70,4 @@ def phase_excp_run_if(): phase = openhtf.PhaseOptions(run_if=run_if_with_exception)( phase_excp_run_if) record = self.execute_phase_or_test(openhtf.Test(phase)) - self.assertEqual(record.outcome, test_record.Outcome.ERROR) + self.assertTestError(record)