diff --git a/avocado/job.py b/avocado/job.py
index d4d82f74abf561ac40ed8100db14a24f5eb4573e..d93ff0d2cf054e4449f38f784eed3b048fae48e3 100644
--- a/avocado/job.py
+++ b/avocado/job.py
@@ -65,6 +65,8 @@ class TestRunner(object):
"""
Resolve and load the test url from the the test shortname.
+ This method should now be called by the test runner process.
+
:param params: Dictionary with test params.
:type params: dict
:return: an instance of :class:`avocado.test.Test`.
@@ -109,7 +111,7 @@ class TestRunner(object):
return test_instance
- def run_test(self, instance, queue):
+ def run_test(self, params, queue):
"""
Run a test instance in a subprocess.
@@ -123,10 +125,13 @@ class TestRunner(object):
raise exceptions.TestTimeoutError(e_msg)
signal.signal(signal.SIGUSR1, timeout_handler)
+
+ instance = self.load_test(params)
+ self.result.start_test(instance.get_state())
try:
instance.run_avocado()
finally:
- queue.put(instance)
+ queue.put(instance.get_state())
def run(self, params_list):
"""
@@ -145,32 +150,25 @@ class TestRunner(object):
self.result.start_tests()
q = multiprocessing.Queue()
for params in params_list:
- test_instance = self.load_test(params)
- self.result.start_test(test_instance)
p = multiprocessing.Process(target=self.run_test,
- args=(test_instance, q,))
+ args=(params, q,))
p.start()
- # The test timeout can come from:
- # 1) Test params dict (params)
- # 2) Test default params dict (test_instance.params.timeout)
+ # Change in behaviour: timeout now comes *only* from test params
timeout = params.get('timeout')
- if timeout is None:
- if hasattr(test_instance.params, 'timeout'):
- timeout = test_instance.params.timeout
if timeout is not None:
timeout = float(timeout)
# Wait for the test to end for [timeout] s
try:
- test_instance = q.get(timeout=timeout)
+ test_state = q.get(timeout=timeout)
except Exception:
# If there's nothing inside the queue after timeout, the process
# must be terminated.
send_signal(p, signal.SIGUSR1)
- test_instance = q.get()
+ test_state = q.get()
- self.result.check_test(test_instance)
- if not status.mapping[test_instance.status]:
- failures.append(test_instance.name)
+ self.result.check_test(test_state)
+ if not status.mapping[test_state['status']]:
+ failures.append(test_state['name'])
self.result.end_tests()
return failures
diff --git a/avocado/plugins/jsonresult.py b/avocado/plugins/jsonresult.py
index d0b1d8f06a381f832cb37f6d371d9f21600a3969..a002cbc690de4e650e0a8d922e6a269ab6abbbe5 100644
--- a/avocado/plugins/jsonresult.py
+++ b/avocado/plugins/jsonresult.py
@@ -42,18 +42,19 @@ class JSONTestResult(TestResult):
self.json = {'debuglog': self.stream.logfile,
'tests': []}
- def end_test(self, test):
+ def end_test(self, state):
"""
Called when the given test has been run.
- :param test: an instance of :class:`avocado.test.Test`.
+ :param state: result of :class:`avocado.test.Test.get_state`.
+ :type state: dict
"""
- TestResult.end_test(self, test)
- t = {'test': test.tagged_name,
- 'url': test.name,
- 'time': test.time_elapsed,
- 'status': test.status,
- 'whiteboard': test.whiteboard,
+ TestResult.end_test(self, state)
+ t = {'test': state['tagged_name'],
+ 'url': state['name'],
+ 'time': state['time_elapsed'],
+ 'status': state['status'],
+ 'whiteboard': state['whiteboard'],
}
self.json['tests'].append(t)
diff --git a/avocado/plugins/xunit.py b/avocado/plugins/xunit.py
index 34d20d4cc40a32115cbbb1a00f6c3a30c0bbb074..68ce45e8b9da616a25e62b14e9f33243c7f76978 100644
--- a/avocado/plugins/xunit.py
+++ b/avocado/plugins/xunit.py
@@ -83,68 +83,72 @@ class XmlResult(object):
self.xml.append(tc)
self.xml.append('')
- def add_success(self, test):
+ def add_success(self, state):
"""
Add a testcase node of kind succeed.
- :param test: an instance of :class:`avocado.test.Test`.
+ :param state: result of :class:`avocado.test.Test.get_state`.
+ :type state: dict
"""
tc = '\t'
- values = {'class': self._escape_attr(test.__class__.__name__),
- 'name': self._escape_attr(test.tagged_name),
- 'time': test.time_elapsed}
+ values = {'class': self._escape_attr(state['class_name']),
+ 'name': self._escape_attr(state['tagged_name']),
+ 'time': state['time_elapsed']}
self.testcases.append(tc.format(**values))
- def add_skip(self, test):
+ def add_skip(self, state):
"""
Add a testcase node of kind skipped.
- :param test: an instance of :class:`avocado.test.Test`.
+ :param state: result of :class:`avocado.test.Test.get_state`.
+ :type state: dict
"""
tc = '''\t
\t\t
\t'''
- values = {'class': self._escape_attr(test.__class__.__name__),
- 'name': self._escape_attr(test.tagged_name),
- 'time': test.time_elapsed}
+ values = {'class': self._escape_attr(state['class_name']),
+ 'name': self._escape_attr(state['tagged_name']),
+ 'time': state['time_elapsed']}
self.testcases.append(tc.format(**values))
- def add_failure(self, test):
+ def add_failure(self, state):
"""
Add a testcase node of kind failed.
- :param test: an instance of :class:`avocado.test.Test`.
+ :param state: result of :class:`avocado.test.Test.get_state`.
+ :type state: dict
"""
tc = '''\t
\t\t
\t\t
\t'''
- values = {'class': self._escape_attr(test.__class__.__name__),
- 'name': self._escape_attr(test.tagged_name),
- 'time': test.time_elapsed,
- 'type': self._escape_attr(test.fail_class),
- 'traceback': self._escape_cdata(test.traceback),
- 'systemout': self._escape_cdata(test.text_output),
- 'reason': self._escape_attr(str(test.fail_reason))}
+ values = {'class': self._escape_attr(state['class_name']),
+ 'name': self._escape_attr(state['tagged_name']),
+ 'time': state['time_elapsed'],
+ 'type': self._escape_attr(state['fail_class']),
+ 'traceback': self._escape_cdata(state['traceback']),
+ 'systemout': self._escape_cdata(state['text_output']),
+ 'reason': self._escape_attr(str(state['fail_reason']))}
self.testcases.append(tc.format(**values))
- def add_error(self, test):
+ def add_error(self, state):
"""
Add a testcase node of kind error.
- :param test: an instance of :class:`avocado.test.Test`.
+ :param state: result of :class:`avocado.test.Test.get_state`.
+ :type state: dict
"""
tc = '''\t
\t\t
\t\t
\t'''
- values = {'class': self._escape_attr(test.__class__.__name__),
- 'name': self._escape_attr(test.tagged_name),
- 'time': test.time_elapsed,
- 'type': self._escape_attr(test.fail_class),
- 'traceback': self._escape_cdata(test.traceback),
- 'systemout': self._escape_cdata(test.text_output),
- 'reason': self._escape_attr(str(test.fail_reason))}
+ values = {'class': self._escape_attr(state['class_name']),
+ 'name': self._escape_attr(state['tagged_name']),
+ 'time': state['time_elapsed'],
+ 'type': self._escape_attr(state['fail_class']),
+ 'traceback': self._escape_cdata(state['traceback']),
+ 'systemout': self._escape_cdata(state['text_output']),
+ 'reason': self._escape_attr(str(state['fail_reason']))}
self.testcases.append(tc.format(**values))
@@ -183,19 +187,22 @@ class xUnitTestResult(TestResult):
"""
TestResult.start_test(self, test)
- def end_test(self, test):
+ def end_test(self, state):
"""
Record an end test event, accord to the given test status.
- """
- TestResult.end_test(self, test)
- if test.status == 'PASS':
- self.xml.add_success(test)
- if test.status == 'TEST_NA':
- self.xml.add_skip(test)
- if test.status == 'FAIL':
- self.xml.add_failure(test)
- if test.status == 'ERROR':
- self.xml.add_error(test)
+
+ :param state: result of :class:`avocado.test.Test.get_state`.
+ :type state: dict
+ """
+ TestResult.end_test(self, state)
+ if state['status'] == 'PASS':
+ self.xml.add_success(state)
+ elif state['status'] == 'TEST_NA':
+ self.xml.add_skip(state)
+ elif state['status'] == 'FAIL':
+ self.xml.add_failure(state)
+ elif state['status'] == 'ERROR':
+ self.xml.add_error(state)
def end_tests(self):
"""
diff --git a/avocado/result.py b/avocado/result.py
index 4b2828edd3f6eb06d078172160304ce8f3cc43ae..385d0ef36b045d7ee10a1d51b3b43a5fa8bc81e3 100644
--- a/avocado/result.py
+++ b/avocado/result.py
@@ -52,37 +52,37 @@ class TestResultProxy(object):
for output_plugin in self.output_plugins:
output_plugin.end_tests()
- def start_test(self, test):
+ def start_test(self, state):
for output_plugin in self.output_plugins:
- output_plugin.start_test(test)
+ output_plugin.start_test(state)
- def end_test(self, test):
+ def end_test(self, state):
for output_plugin in self.output_plugins:
- output_plugin.end_test(test)
+ output_plugin.end_test(state)
- def add_pass(self, test):
+ def add_pass(self, state):
for output_plugin in self.output_plugins:
- output_plugin.add_pass(test)
+ output_plugin.add_pass(state)
- def add_error(self, test):
+ def add_error(self, state):
for output_plugin in self.output_plugins:
- output_plugin.add_error(test)
+ output_plugin.add_error(state)
- def add_fail(self, test):
+ def add_fail(self, state):
for output_plugin in self.output_plugins:
- output_plugin.add_fail(test)
+ output_plugin.add_fail(state)
- def add_skip(self, test):
+ def add_skip(self, state):
for output_plugin in self.output_plugins:
- output_plugin.add_skip(test)
+ output_plugin.add_skip(state)
- def add_warn(self, test):
+ def add_warn(self, state):
for output_plugin in self.output_plugins:
- output_plugin.add_warn(test)
+ output_plugin.add_warn(state)
- def check_test(self, test):
+ def check_test(self, state):
for output_plugin in self.output_plugins:
- output_plugin.check_test(test)
+ output_plugin.check_test(state)
class TestResult(object):
@@ -149,64 +149,70 @@ class TestResult(object):
"""
pass
- def start_test(self, test):
+ def start_test(self, state):
"""
Called when the given test is about to run.
- :param test: an instance of :class:`avocado.test.Test`.
+ :param state: result of :class:`avocado.test.Test.get_state`.
+ :type state: dict
"""
pass
- def end_test(self, test):
+ def end_test(self, state):
"""
Called when the given test has been run.
- :param test: an instance of :class:`avocado.test.Test`.
+ :param state: result of :class:`avocado.test.Test.get_state`.
+ :type state: dict
"""
self.tests_run += 1
- self.total_time += test.time_elapsed
+ self.total_time += state['time_elapsed']
- def add_pass(self, test):
+ def add_pass(self, state):
"""
Called when a test succeeded.
- :param test: an instance of :class:`avocado.test.Test`.
+ :param state: result of :class:`avocado.test.Test.get_state`.
+ :type state: dict
"""
- self.passed.append(test)
+ self.passed.append(state)
- def add_error(self, test):
+ def add_error(self, state):
"""
Called when a test had a setup error.
- :param test: an instance of :class:`avocado.test.Test`.
+ :param state: result of :class:`avocado.test.Test.get_state`.
+ :type state: dict
"""
- self.errors.append(test)
+ self.errors.append(state)
- def add_fail(self, test):
+ def add_fail(self, state):
"""
Called when a test fails.
- :param test: an instance of :class:`avocado.test.Test`.
+ :param state: result of :class:`avocado.test.Test.get_state`.
+ :type state: dict
"""
- self.failed.append(test)
+ self.failed.append(state)
- def add_skip(self, test):
+ def add_skip(self, state):
"""
Called when a test is skipped.
:param test: an instance of :class:`avocado.test.Test`.
"""
- self.skipped.append(test)
+ self.skipped.append(state)
- def add_warn(self, test):
+ def add_warn(self, state):
"""
Called when a test had a warning.
- :param test: an instance of :class:`avocado.test.Test`.
+ :param state: result of :class:`avocado.test.Test.get_state`.
+ :type state: dict
"""
- self.warned.append(test)
+ self.warned.append(state)
- def check_test(self, test):
+ def check_test(self, state):
"""
Called once for a test to check status and report.
@@ -217,9 +223,9 @@ class TestResult(object):
'FAIL': self.add_fail,
'TEST_NA': self.add_skip,
'WARN': self.add_warn}
- add = status_map[test.status]
- add(test)
- self.end_test(test)
+ add = status_map[state['status']]
+ add(state)
+ self.end_test(state)
class HumanTestResult(TestResult):
@@ -247,66 +253,73 @@ class HumanTestResult(TestResult):
self.stream.log_header("TOTAL WARNED: %d" % len(self.warned))
self.stream.log_header("ELAPSED TIME: %.2f s" % self.total_time)
- def start_test(self, test):
+ def start_test(self, state):
"""
Called when the given test is about to run.
- :param test: an instance of :class:`avocado.test.Test`.
+ :param state: result of :class:`avocado.test.Test.get_state`.
+ :type state: dict
"""
self.test_label = '(%s/%s) %s: ' % (self.tests_run,
self.tests_total,
- test.tagged_name)
+ state['tagged_name'])
self.stream.info(msg=self.test_label, skip_newline=True)
- def end_test(self, test):
+ def end_test(self, state):
"""
Called when the given test has been run.
- :param test: an instance of :class:`avocado.test.Test`.
+ :param state: result of :class:`avocado.test.Test.get_state`.
+ :type state: dict
"""
- TestResult.end_test(self, test)
+ TestResult.end_test(self, state)
- def add_pass(self, test):
+ def add_pass(self, state):
"""
Called when a test succeeded.
- :param test: an instance of :class:`avocado.test.Test`.
+ :param state: result of :class:`avocado.test.Test.get_state`.
+ :type state: dict
"""
- TestResult.add_pass(self, test)
- self.stream.log_pass(test.time_elapsed)
+ TestResult.add_pass(self, state)
+ self.stream.log_pass(state['time_elapsed'])
- def add_error(self, test):
+ def add_error(self, state):
"""
Called when a test had a setup error.
- :param test: an instance of :class:`avocado.test.Test`.
+ :param state: result of :class:`avocado.test.Test.get_state`.
+ :type state: dict
"""
- TestResult.add_error(self, test)
- self.stream.log_error(test.time_elapsed)
+ TestResult.add_error(self, state)
+ self.stream.log_error(state['time_elapsed'])
- def add_fail(self, test):
+ def add_fail(self, state):
"""
Called when a test fails.
- :param test: an instance of :class:`avocado.test.Test`.
+ :param state: result of :class:`avocado.test.Test.get_state`.
+ :type state: dict
"""
- TestResult.add_fail(self, test)
- self.stream.log_fail(test.time_elapsed)
+ TestResult.add_fail(self, state)
+ self.stream.log_fail(state['time_elapsed'])
- def add_skip(self, test):
+ def add_skip(self, state):
"""
Called when a test is skipped.
- :param test: an instance of :class:`avocado.test.Test`.
+ :param state: result of :class:`avocado.test.Test.get_state`.
+ :type state: dict
"""
- TestResult.add_skip(self, test)
- self.stream.log_skip(test.time_elapsed)
+ TestResult.add_skip(self, state)
+ self.stream.log_skip(state['time_elapsed'])
- def add_warn(self, test):
+ def add_warn(self, state):
"""
Called when a test had a warning.
- :param test: an instance of :class:`avocado.test.Test`.
+ :param state: result of :class:`avocado.test.Test.get_state`.
+ :type state: dict
"""
- TestResult.add_warn(self, test)
- self.stream.log_warn(test.time_elapsed)
+ TestResult.add_warn(self, state)
+ self.stream.log_warn(state['time_elapsed'])
diff --git a/avocado/test.py b/avocado/test.py
index a3c797c6b803d8be08b71218b4b11bda0c114a1e..14b728f2f5334c7ddf8700bc79eec8d175fdfb41 100644
--- a/avocado/test.py
+++ b/avocado/test.py
@@ -187,12 +187,12 @@ class Test(unittest.TestCase):
def __repr__(self):
return "Test(%r)" % self.tagged_name
- def __getstate__(self):
+ def get_state(self):
"""
- Pickle only selected attributes of the class for serialization.
+ Serialize selected attributes representing the test state
- The fact we serialize the class means you'll have to modify this
- class if you intend to make significant changes to its structure.
+ :returns: a dictionary containing relevant test state data
+ :rtype: dict
"""
orig = dict(self.__dict__)
d = {}
@@ -205,6 +205,7 @@ class Test(unittest.TestCase):
if key in preserve_attr:
d[key] = orig[key]
d['params'] = orig['_raw_params']
+ d['class_name'] = self.__class__.__name__
return d
def _set_default(self, key, default):