Repository: avocado-framework/avocado

Issue:
Test timeout: Could not find a way to timeout a test with nrunner
Today, we have a few options for setting a job/test timeout:

1. job.run.timeout
2. runner.timeout.process_alive

While the first one is working fine, I could not set working properly on how to set a timeout for tests when using the 'nrunner'.


Discussion:

Resolving patch:
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
@@ -134,7 +134,7 @@ async def monitor(self):
                 if runtime_task.execution_timeout is None:
                     remaining = None
                 else:
-                    remaining = runtime_task.timeout - time.monotonic()
+                    remaining = runtime_task.execution_timeout - time.monotonic()
                 await asyncio.wait_for(self._spawner.wait_task(runtime_task),
                                        remaining)
                 runtime_task.status = 'FINISHED'
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
@@ -94,6 +94,13 @@ def initialize(self):
                                  default='process',
                                  help_msg=help_msg)
 
+        help_msg = "The amount of time a test has to complete in seconds."
+        settings.register_option(section='task.timeout',
+                                 key='running',
+                                 default=None,
+                                 key_type=int,
+                                 help_msg=help_msg)
+
 
 class RunnerCLI(CLI):
 
@@ -266,7 +273,11 @@ def run_suite(self, job, test_suite):
         spawner = SpawnerDispatcher(test_suite.config)[spawner_name].obj
         max_running = min(test_suite.config.get('nrunner.max_parallel_tasks'),
                           len(self.tasks))
-        workers = [Worker(tsm, spawner, max_running=max_running).run()
+        timeout = test_suite.config.get('task.timeout.running')
+        workers = [Worker(state_machine=tsm,
+                          spawner=spawner,
+                          max_running=max_running,
+                          task_timeout=timeout).run()
                    for _ in range(max_running)]
         asyncio.ensure_future(self._update_status(job))
         loop = asyncio.get_event_loop()


Test patch:
diff --git a/selftests/functional/test_task_timeout.py b/selftests/functional/test_task_timeout.py
new file mode 100644
--- /dev/null
+++ b/selftests/functional/test_task_timeout.py
@@ -0,0 +1,46 @@
+import tempfile
+
+from avocado.core.job import Job
+from avocado.utils import script
+from avocado.utils.network.ports import find_free_port
+
+from .. import TestCaseTmpDir, skipUnlessPathExists, temp_dir_prefix
+
+SCRIPT_CONTENT = """#!/bin/bash
+/bin/sleep 30
+"""
+
+
+class TaskTimeOutTest(TestCaseTmpDir):
+
+    def setUp(self):
+        super(TaskTimeOutTest, self).setUp()
+        self.script = script.TemporaryScript(
+            'sleep.sh',
+            SCRIPT_CONTENT,
+            'avocado_timeout_functional')
+        self.script.save()
+
+        prefix = temp_dir_prefix(__name__, self, 'setUp')
+        self.tmpdir = tempfile.TemporaryDirectory(prefix=prefix)
+
+    @skipUnlessPathExists('/bin/sleep')
+    def test_sleep_longer_timeout(self):
+        status_server = '127.0.0.1:%u' % find_free_port()
+        config = {'run.references': [self.script.path],
+                  'nrunner.status_server_listen': status_server,
+                  'nrunner.status_server_uri': status_server,
+                  'run.results_dir': self.tmpdir.name,
+                  'run.keep_tmp': True,
+                  'task.timeout.running': 2,
+                  'run.test_runner': 'nrunner'}
+
+        with Job.from_config(job_config=config) as job:
+            job.run()
+
+        self.assertEqual(1, job.result.skipped)
+        self.assertEqual(0, job.result.passed)
+
+    def tearDown(self):
+        super(TaskTimeOutTest, self).tearDown()
+        self.script.remove()