This option allows to set timeout for run_test. If timeout expires
before run_test completes, the test will be killed (test fails).
If no timeout parameter is present or if it is None, no timeout
will be enforced.
Example:
job.run_test('sleeptest', timeout=5, seconds=60)
Signed-off-by: Jan Stancek <[email protected]>
---
client/bin/job.py | 16 +++++++++++++---
client/bin/job_unittest.py | 35 +++++++++++++++++++++++++++++++++--
client/bin/parallel.py | 29 ++++++++++++++++++++++++++++-
3 files changed, 74 insertions(+), 6 deletions(-)
diff --git a/client/bin/job.py b/client/bin/job.py
index 8b8997e..3731e23 100644
--- a/client/bin/job.py
+++ b/client/bin/job.py
@@ -515,11 +515,17 @@ class base_client_job(base_job.base_job):
logging.info('Dependency %s successfuly built', dep)
- def _runtest(self, url, tag, args, dargs):
+ def _runtest(self, url, tag, timeout, args, dargs):
try:
l = lambda : test.runtest(self, url, tag, args, dargs)
pid = parallel.fork_start(self.resultdir, l)
- parallel.fork_waitfor(self.resultdir, pid)
+
+ if timeout:
+ logging.debug('Waiting for pid %d for %d seconds', pid,
timeout)
+ parallel.fork_waitfor_timed(self.resultdir, pid, timeout)
+ else:
+ parallel.fork_waitfor(self.resultdir, pid)
+
except error.TestBaseException:
# These are already classified with an error type (exit_status)
raise
@@ -549,12 +555,16 @@ class base_client_job(base_job.base_job):
testname, subdir, tag = self._build_tagged_test_name(testname, dargs)
outputdir = self._make_test_outputdir(subdir)
+ timeout = dargs.pop('timeout', None)
+ if timeout:
+ logging.debug('Test has timeout: %d sec.', timeout)
+
def log_warning(reason):
self.record("WARN", subdir, testname, reason)
@disk_usage_monitor.watch(log_warning, "/", self._max_disk_usage_rate)
def group_func():
try:
- self._runtest(url, tag, args, dargs)
+ self._runtest(url, tag, timeout, args, dargs)
except error.TestBaseException, detail:
# The error is already classified, record it properly.
self.record(detail.exit_status, subdir, testname, str(detail))
diff --git a/client/bin/job_unittest.py b/client/bin/job_unittest.py
index 88fa272..6b50ed0 100755
--- a/client/bin/job_unittest.py
+++ b/client/bin/job_unittest.py
@@ -505,7 +505,7 @@ class test_base_job(unittest.TestCase):
testname, 'test').and_return(("", testname))
os.path.exists.expect_call(outputdir).and_return(False)
self.job.record.expect_call("START", testname, testname)
- self.job._runtest.expect_call(testname, "", (), {}).and_raises(
+ self.job._runtest.expect_call(testname, "", None, (), {}).and_raises(
unhandled_error)
self.job.record.expect_call("ERROR", testname, testname,
first_line_comparator(str(real_error)))
@@ -539,7 +539,7 @@ class test_base_job(unittest.TestCase):
testname, 'test').and_return(("", testname))
os.path.exists.expect_call(outputdir).and_return(False)
self.job.record.expect_call("START", testname, testname)
- self.job._runtest.expect_call(testname, "", (), {}).and_raises(
+ self.job._runtest.expect_call(testname, "", None, (), {}).and_raises(
unhandled_error)
self.job.record.expect_call("ERROR", testname, testname, reason)
self.job.record.expect_call("END ERROR", testname, testname)
@@ -713,5 +713,36 @@ class test_base_job(unittest.TestCase):
self.assertEqual(parsed_args, expected_args)
+ def test_run_test_timeout_parameter_is_propagated(self):
+ self.construct_job(True)
+
+ # set up stubs
+ self.god.stub_function(self.job.pkgmgr, 'get_package_name')
+ self.god.stub_function(self.job, "_runtest")
+
+ # create an unhandled error object
+ #class MyError(error.TestError):
+ # pass
+ #real_error = MyError("this is the real error message")
+ #unhandled_error = error.UnhandledTestError(real_error)
+
+ # set up the recording
+ testname = "sleeptest"
+ outputdir = os.path.join(self.job.resultdir, testname)
+ self.job.pkgmgr.get_package_name.expect_call(
+ testname, 'test').and_return(("", testname))
+ os.path.exists.expect_call(outputdir).and_return(False)
+ self.job.record.expect_call("START", testname, testname)
+ self.job._runtest.expect_call(testname, "", 60, (), {})
+ self.job.record.expect_call("GOOD", testname, testname, 'completed
successfully')
+ self.job.record.expect_call("END GOOD", testname, testname)
+ self.job.harness.run_test_complete.expect_call()
+ utils.drop_caches.expect_call()
+
+ # run and check
+ self.job.run_test(testname, timeout=60)
+ self.god.check_playback()
+
+
if __name__ == "__main__":
unittest.main()
diff --git a/client/bin/parallel.py b/client/bin/parallel.py
index 47dd5cd..c426968 100644
--- a/client/bin/parallel.py
+++ b/client/bin/parallel.py
@@ -2,7 +2,7 @@
__author__ = """Copyright Andy Whitcroft 2006"""
-import sys, logging, os, pickle, traceback, gc
+import sys, logging, os, pickle, traceback, gc, time
from autotest_lib.client.common_lib import error, utils
def fork_start(tmp, l):
@@ -76,6 +76,33 @@ def fork_waitfor(tmp, pid):
if status:
raise error.TestError("Test subprocess failed rc=%d" % (status))
+def fork_waitfor_timed(tmp, pid, timeout):
+ """
+ Waits for pid until it terminates or timeout expires.
+ If timeout expires, test subprocess is killed.
+ """
+ timer_expired = True
+ poll_time = 2
+ time_passed = 0
+ while time_passed < timeout:
+ time.sleep(poll_time)
+ (child_pid, status) = os.waitpid(pid, os.WNOHANG)
+ if (child_pid, status) == (0, 0):
+ time_passed = time_passed + poll_time
+ else:
+ timer_expired = False
+ break
+
+ if timer_expired:
+ logging.info('Timer expired (%d sec.), nuking pid %d', timeout, pid)
+ utils.nuke_pid(pid)
+ (child_pid, status) = os.waitpid(pid, 0)
+ raise error.TestError("Test timeout expired, rc=%d" % (status))
+ else:
+ _check_for_subprocess_exception(tmp, pid)
+
+ if status:
+ raise error.TestError("Test subprocess failed rc=%d" % (status))
def fork_nuke_subprocess(tmp, pid):
utils.nuke_pid(pid)
--
1.7.1
_______________________________________________
Autotest mailing list
[email protected]
http://test.kernel.org/cgi-bin/mailman/listinfo/autotest