Repository: avocado-framework/avocado

Issue:
nrunner failfast differences
I've noticed two differences between the failfast behavior on the runner and nrunner implementations:

1. A message to the user about what happened `Interrupting job (failfast).`
2. The setting of the `AVOCADO_JOB_INTERRUPTED` exit code

Unless there's a very good reason for changing the behavior, we should implement the same on nrunner to allow for a smoother transition.


Discussion:
@clebergnu thanks I will double check that.

Resolving patch:
diff --git a/avocado/core/exceptions.py b/avocado/core/exceptions.py
--- a/avocado/core/exceptions.py
+++ b/avocado/core/exceptions.py
@@ -155,6 +155,17 @@ class TestFail(TestBaseException, AssertionError):
     status = "FAIL"
 
 
+class TestFailFast(TestBaseException):
+
+    """
+    Indicates that the test has failed because failfast is enabled.
+
+    Should be thrown when a test has failed and failfast is enabled. This will
+    indicate that other tests will be skipped.
+    """
+    status = "SKIP"
+
+
 class TestWarn(TestBaseException):
 
     """
diff --git a/avocado/core/task/statemachine.py b/avocado/core/task/statemachine.py
--- a/avocado/core/task/statemachine.py
+++ b/avocado/core/task/statemachine.py
@@ -3,6 +3,8 @@
 import multiprocessing
 import time
 
+from avocado.core.exceptions import TestFailFast
+
 
 class TaskStateMachine:
     """Represents all phases that a task can go through its life."""
@@ -248,6 +250,7 @@ async def monitor(self):
         result_stats = self._state_machine._status_repo.result_stats
         if failfast and 'fail' in result_stats:
             await self._state_machine.abort("FAILFAST is enabled")
+            raise TestFailFast("Interrupting job (failfast).")
 
         await self._state_machine.finish_task(runtime_task)
 
diff --git a/avocado/plugins/runner_nrunner.py b/avocado/plugins/runner_nrunner.py
--- a/avocado/plugins/runner_nrunner.py
+++ b/avocado/plugins/runner_nrunner.py
@@ -22,7 +22,9 @@
 
 from avocado.core import nrunner
 from avocado.core.dispatcher import SpawnerDispatcher
+from avocado.core.exceptions import TestFailFast
 from avocado.core.messages import MessageHandler
+from avocado.core.output import LOG_JOB
 from avocado.core.plugin_interfaces import CLI, Init
 from avocado.core.plugin_interfaces import Runner as RunnerInterface
 from avocado.core.requirements.resolver import RequirementsResolver
@@ -303,7 +305,9 @@ def run_suite(self, job, test_suite):
         try:
             loop.run_until_complete(asyncio.wait_for(asyncio.gather(*workers),
                                                      job.timeout or None))
-        except (KeyboardInterrupt, asyncio.TimeoutError):
+        except (KeyboardInterrupt, asyncio.TimeoutError, TestFailFast) as ex:
+            LOG_JOB.info(str(ex))
+            job.interrupted_reason = str(ex)
             summary.add("INTERRUPTED")
 
         # Wait until all messages may have been processed by the


Test patch:
diff --git a/selftests/functional/test_nrunner.py b/selftests/functional/test_nrunner.py
--- a/selftests/functional/test_nrunner.py
+++ b/selftests/functional/test_nrunner.py
@@ -39,7 +39,7 @@ def test_failfast(self):
                   'nrunner.status_server_uri': status_server,
                   'nrunner.max_parallel_tasks': 1}
         with Job.from_config(job_config=config) as job:
-            self.assertEqual(job.run(), 1)
+            self.assertEqual(job.run(), 9)
             self.assertEqual(job.result.passed, 1)
             self.assertEqual(job.result.errors, 0)
             self.assertEqual(job.result.failed, 1)