提交 5973e898 编写于 作者: L Lukáš Doktor 提交者: Amador Pahim

avocado: Implement serialized test ids

This commit implements the serialized test ids described in the
Introduce proper test IDs RFC.

https://www.redhat.com/archives/avocado-devel/2016-March/msg00024.html

It implements `TestName` class, which contains the test uid, test name
and the variant uid and allows querying for the file-system-friendly
name.

The workflow is:

1. tests are discovered, name is translated to "Test Name" by loader
2. test_suite is passed to the runner (new) along with the number of
   tests+variants to be executed (used to get number of digits)
3. the Mux (params generator) yields the template + (new) variant id
4. the runner replaces template['name'] to TestName(uid, test_name,
   variant_id); where uid is currently no executed tests, test_name is
   the original name from Loader and variant_id is either None or the
   variant index.

This commit makes the `tag` argument unused. To avoid problems a warning
is issued on it's usage so we can remove it in the upcoming releases.
Signed-off-by: NLukáš Doktor <ldoktor@redhat.com>
上级 c7b6b68b
...@@ -217,7 +217,7 @@ class HTMLTestResult(TestResult): ...@@ -217,7 +217,7 @@ class HTMLTestResult(TestResult):
:type state: dict :type state: dict
""" """
TestResult.end_test(self, state) TestResult.end_test(self, state)
t = {'test': state.get('tagged_name', "<unknown>"), t = {'test': str(state.get('name', "<unknown>")),
'url': state.get('name', "<unknown>"), 'url': state.get('name', "<unknown>"),
'time_start': state.get('time_start', -1), 'time_start': state.get('time_start', -1),
'time_end': state.get('time_end', -1), 'time_end': state.get('time_end', -1),
......
...@@ -499,9 +499,9 @@ class Job(object): ...@@ -499,9 +499,9 @@ class Job(object):
self._log_job_debug_info(mux) self._log_job_debug_info(mux)
replay.record(self.args, self.logdir, mux, self.urls) replay.record(self.args, self.logdir, mux, self.urls)
replay_map = getattr(self.args, 'replay_map', None) replay_map = getattr(self.args, 'replay_map', None)
summary = self.test_runner.run_suite(test_suite, mux, summary = self.test_runner.run_suite(test_suite, mux, self.timeout,
timeout=self.timeout, replay_map,
replay_map=replay_map) self.args.test_result_total)
self.__stop_job_logging() self.__stop_job_logging()
# If it's all good so far, set job status to 'PASS' # If it's all good so far, set job status to 'PASS'
if self.status == 'RUNNING': if self.status == 'RUNNING':
......
...@@ -61,8 +61,8 @@ class JSONTestResult(TestResult): ...@@ -61,8 +61,8 @@ class JSONTestResult(TestResult):
TestResult.end_test(self, state) TestResult.end_test(self, state)
if 'job_id' not in self.json: if 'job_id' not in self.json:
self.json['job_id'] = state.get('job_unique_id', "<unknown>") self.json['job_id'] = state.get('job_unique_id', "<unknown>")
t = {'test': state.get('tagged_name', "<unknown>"), t = {'test': str(state.get('name', "<unknown>")),
'url': state.get('name', "<unknown>"), 'url': str(state.get('name', "<unknown>")),
'start': state.get('time_start', -1), 'start': state.get('time_start', -1),
'end': state.get('time_end', -1), 'end': state.get('time_end', -1),
'time': state.get('time_elapsed', -1), 'time': state.get('time_elapsed', -1),
......
...@@ -415,18 +415,16 @@ class Mux(object): ...@@ -415,18 +415,16 @@ class Mux(object):
""" """
if self.variants: # Copy template and modify it's params if self.variants: # Copy template and modify it's params
i = None i = None
for i, variant in enumerate(self.variants): for i, variant in enumerate(self.variants, 1):
test_factory = [template[0], template[1].copy()] test_factory = [template[0], template[1].copy()]
if self._has_multiple_variants:
test_factory[1]['tag'] = "variant%s" % (i + 1)
if "params" in test_factory[1]: if "params" in test_factory[1]:
msg = ("Unable to multiplex test %s, params are already " msg = ("Unable to multiplex test %s, params are already "
"present in test factory: %s" "present in test factory: %s"
% (test_factory[0], test_factory[1])) % (test_factory[0], test_factory[1]))
raise ValueError(msg) raise ValueError(msg)
test_factory[1]['params'] = (variant, self._mux_path) test_factory[1]['params'] = (variant, self._mux_path)
yield test_factory yield test_factory, i if self._has_multiple_variants else None
if i is None: # No variants, use template if i is None: # No variants, use template
yield template yield template, None
else: # No variants, use template else: # No variants, use template
yield template yield template, None
...@@ -29,6 +29,7 @@ from .. import virt ...@@ -29,6 +29,7 @@ from .. import virt
from .. import exceptions from .. import exceptions
from .. import status from .. import status
from ..runner import TestRunner from ..runner import TestRunner
from ..test import TestName
from ...utils import astring from ...utils import astring
from ...utils import archive from ...utils import archive
from ...utils import stacktrace from ...utils import stacktrace
...@@ -180,7 +181,7 @@ class RemoteTestRunner(TestRunner): ...@@ -180,7 +181,7 @@ class RemoteTestRunner(TestRunner):
return json_result return json_result
def run_suite(self, test_suite, mux, timeout, replay_map=None): def run_suite(self, test_suite, mux, timeout, replay_map=None, test_result_total=0):
""" """
Run one or more tests and report with test result. Run one or more tests and report with test result.
...@@ -191,6 +192,7 @@ class RemoteTestRunner(TestRunner): ...@@ -191,6 +192,7 @@ class RemoteTestRunner(TestRunner):
""" """
del test_suite # using self.job.urls instead del test_suite # using self.job.urls instead
del mux # we're not using multiplexation here del mux # we're not using multiplexation here
del test_result_total # evaluated by the remote avocado
if not timeout: # avoid timeout = 0 if not timeout: # avoid timeout = 0
timeout = None timeout = None
summary = set() summary = set()
...@@ -229,7 +231,10 @@ class RemoteTestRunner(TestRunner): ...@@ -229,7 +231,10 @@ class RemoteTestRunner(TestRunner):
remote_log_dir = os.path.dirname(results['debuglog']) remote_log_dir = os.path.dirname(results['debuglog'])
self.result.start_tests() self.result.start_tests()
for tst in results['tests']: for tst in results['tests']:
test = RemoteTest(name=tst['test'], name = tst['test'].split('-', 1)
name = [name[0]] + name[1].split(';')
name = TestName(*name, no_digits=-1)
test = RemoteTest(name=name,
time=tst['time'], time=tst['time'],
start=tst['start'], start=tst['start'],
end=tst['end'], end=tst['end'],
......
...@@ -25,7 +25,6 @@ class RemoteTest(object): ...@@ -25,7 +25,6 @@ class RemoteTest(object):
logfile): logfile):
note = "Not supported yet" note = "Not supported yet"
self.name = name self.name = name
self.tagged_name = name
self.status = status self.status = status
self.time_elapsed = time self.time_elapsed = time
self.time_start = start self.time_start = start
......
...@@ -235,8 +235,14 @@ class HumanTestResult(TestResult): ...@@ -235,8 +235,14 @@ class HumanTestResult(TestResult):
def start_test(self, state): def start_test(self, state):
super(HumanTestResult, self).start_test(state) super(HumanTestResult, self).start_test(state)
self.log.debug(' (%s/%s) %s: ', self.tests_run, self.tests_total, if "name" in state:
state.get("tagged_name", "<missing>"), name = state["name"]
uid = name.str_uid
name = name.name + name.str_variant
else:
name = "<unknown>"
uid = '?'
self.log.debug(' (%s/%s) %s: ', uid, self.tests_total, name,
extra={"skip_newline": True}) extra={"skip_newline": True})
def end_test(self, state): def end_test(self, state):
......
...@@ -403,7 +403,8 @@ class TestRunner(object): ...@@ -403,7 +403,8 @@ class TestRunner(object):
return False return False
return True return True
def run_suite(self, test_suite, mux, timeout=0, replay_map=None): def run_suite(self, test_suite, mux, timeout=0, replay_map=None,
test_result_total=0):
""" """
Run one or more tests and report with test result. Run one or more tests and report with test result.
...@@ -423,14 +424,20 @@ class TestRunner(object): ...@@ -423,14 +424,20 @@ class TestRunner(object):
else: else:
deadline = None deadline = None
no_digits = len(str(test_result_total))
index = -1 index = -1
for test_template in test_suite: for test_template in test_suite:
test_template[1]['base_logdir'] = self.job.logdir test_template[1]['base_logdir'] = self.job.logdir
test_template[1]['job'] = self.job test_template[1]['job'] = self.job
break_loop = False break_loop = False
for test_factory in mux.itertests(test_template): for test_factory, variant in mux.itertests(test_template):
index += 1 index += 1
test_parameters = test_factory[1] test_parameters = test_factory[1]
name = test_parameters.get("name")
test_parameters["name"] = test.TestName(index + 1, name,
variant,
no_digits)
if deadline is not None and time.time() > deadline: if deadline is not None and time.time() > deadline:
summary.add('INTERRUPTED') summary.add('INTERRUPTED')
if 'methodName' in test_parameters: if 'methodName' in test_parameters:
......
...@@ -46,6 +46,65 @@ else: ...@@ -46,6 +46,65 @@ else:
import unittest import unittest
class TestName(object):
"""
Test name representation
"""
def __init__(self, uid, name, variant=None, no_digits=None):
"""
Test name according to avocado specification
:param uid: unique test id (within the job)
:param name: test name (identifies the executed test)
:param variant: variant id
:param no_digits: number of digits of the test uid
"""
self.uid = uid
if no_digits >= 0:
self.str_uid = str(uid).zfill(no_digits if no_digits else 3)
else:
self.str_uid = str(uid)
self.name = name or "<unknown>"
self.variant = variant
self.str_variant = "" if variant is None else ";" + str(variant)
def __str__(self):
return "%s-%s%s" % (self.str_uid, self.name, self.str_variant)
def __repr__(self):
return repr(str(self))
def __eq__(self, other):
if isinstance(other, basestring):
return str(self) == other
else:
return self.__dict__ == other.__dict__
def str_filesystem(self):
"""
File-system friendly representation of the test name
"""
name = str(self)
fsname = astring.string_to_safe_path(name)
if len(name) == len(fsname): # everything fits in
return fsname
# 001-mytest;aaa
# 001-mytest;a
# 001-myte;aaa
idx_fit_variant = len(fsname) - len(self.str_variant)
if idx_fit_variant > len(self.str_uid): # full uid+variant
return (fsname[:idx_fit_variant] +
astring.string_to_safe_path(self.str_variant))
elif len(self.str_uid) <= len(fsname): # full uid
return astring.string_to_safe_path(self.str_uid + self.str_variant)
else: # not even uid could be stored in fs
raise AssertionError("Test uid is too long to be stored on the "
"filesystem: %s\nFull test name is %s"
% (self.str_uid, str(self)))
class Test(unittest.TestCase): class Test(unittest.TestCase):
""" """
...@@ -81,10 +140,14 @@ class Test(unittest.TestCase): ...@@ -81,10 +140,14 @@ class Test(unittest.TestCase):
self.__log_warn_used = True self.__log_warn_used = True
return original_log_warn(*args, **kwargs) return original_log_warn(*args, **kwargs)
if name is not None: _incorrect_name = None
if isinstance(name, basestring): # TODO: Remove in release 0.37
_incorrect_name = True
self.name = TestName(0, name)
elif name is not None:
self.name = name self.name = name
else: else:
self.name = self.__class__.__name__ self.name = TestName(0, self.__class__.__name__)
self.tag = tag self.tag = tag
self.job = job self.job = job
...@@ -101,7 +164,12 @@ class Test(unittest.TestCase): ...@@ -101,7 +164,12 @@ class Test(unittest.TestCase):
if base_logdir is None: if base_logdir is None:
base_logdir = data_dir.create_job_logs_dir() base_logdir = data_dir.create_job_logs_dir()
base_logdir = os.path.join(base_logdir, 'test-results') base_logdir = os.path.join(base_logdir, 'test-results')
self.tagged_name, self.logdir = self._init_logdir(base_logdir) logdir = os.path.join(base_logdir, self.name.str_filesystem())
if os.path.exists(logdir):
raise exceptions.TestSetupFail("Log dir already exists, this "
"should never happen: %s"
% logdir)
self.logdir = utils_path.init_dir(logdir)
# Replace '/' with '_' to avoid splitting name into multiple dirs # Replace '/' with '_' to avoid splitting name into multiple dirs
genio.set_log_file_dir(self.logdir) genio.set_log_file_dir(self.logdir)
...@@ -119,6 +187,13 @@ class Test(unittest.TestCase): ...@@ -119,6 +187,13 @@ class Test(unittest.TestCase):
original_log_warn = self.log.warning original_log_warn = self.log.warning
self.__log_warn_used = False self.__log_warn_used = False
self.log.warn = self.log.warning = record_and_warn self.log.warn = self.log.warning = record_and_warn
if _incorrect_name is not None:
self.log.warn("The 'name' argument has to be TestName instance, "
"not string. In the upcomming releases this will "
"become an exception. (%s)", self.name.name)
if tag is not None: # TODO: Remove in release 0.37
self.log.warn("The 'tag' argument is not supported and will be "
"removed in the upcoming releases. (%s)", tag)
mux_path = ['/test/*'] mux_path = ['/test/*']
if isinstance(params, dict): if isinstance(params, dict):
...@@ -135,7 +210,7 @@ class Test(unittest.TestCase): ...@@ -135,7 +210,7 @@ class Test(unittest.TestCase):
default_timeout = getattr(self, "timeout", None) default_timeout = getattr(self, "timeout", None)
self.timeout = self.params.get("timeout", default=default_timeout) self.timeout = self.params.get("timeout", default=default_timeout)
self.log.info('START %s', self.tagged_name) self.log.info('START %s', self.name)
self.debugdir = None self.debugdir = None
self.resultsdir = None self.resultsdir = None
...@@ -209,7 +284,7 @@ class Test(unittest.TestCase): ...@@ -209,7 +284,7 @@ class Test(unittest.TestCase):
return str(self.name) return str(self.name)
def __repr__(self): def __repr__(self):
return "Test(%r)" % self.tagged_name return "Test(%r)" % self.name
def _tag_start(self): def _tag_start(self):
self.running = True self.running = True
...@@ -245,7 +320,7 @@ class Test(unittest.TestCase): ...@@ -245,7 +320,7 @@ class Test(unittest.TestCase):
preserve_attr = ['basedir', 'debugdir', 'depsdir', preserve_attr = ['basedir', 'debugdir', 'depsdir',
'fail_reason', 'logdir', 'logfile', 'name', 'fail_reason', 'logdir', 'logfile', 'name',
'resultsdir', 'srcdir', 'status', 'sysinfodir', 'resultsdir', 'srcdir', 'status', 'sysinfodir',
'tag', 'tagged_name', 'text_output', 'time_elapsed', 'tag', 'text_output', 'time_elapsed',
'traceback', 'workdir', 'whiteboard', 'time_start', 'traceback', 'workdir', 'whiteboard', 'time_start',
'time_end', 'running', 'paused', 'paused_msg', 'time_end', 'running', 'paused', 'paused_msg',
'fail_class', 'params', "timeout"] 'fail_class', 'params', "timeout"]
...@@ -296,39 +371,6 @@ class Test(unittest.TestCase): ...@@ -296,39 +371,6 @@ class Test(unittest.TestCase):
self.log.removeHandler(self.file_handler) self.log.removeHandler(self.file_handler)
logging.getLogger('paramiko').removeHandler(self._ssh_fh) logging.getLogger('paramiko').removeHandler(self._ssh_fh)
def _init_logdir(self, logdir):
"""
Initialize log dir
Combines name + tag (if present) to obtain unique name. When associated
directory already exists, appends ".$number" until unused name
is generated to avoid clashes.
:param logdir: Log directory being in use for result storage.
:return: Unique test name and the logdir
"""
name = self.name
if self.tag is not None:
name += ".%s" % self.tag
tag = 0
tagged_name = name
# The maximal length on ext4+python2.7 is 255 chars.
safe_tagged_name = astring.string_to_safe_path(tagged_name[:250])
for i in xrange(9999):
if not os.path.isdir(os.path.join(logdir, safe_tagged_name)):
break
tag += 1
tagged_name = "%s.%s" % (name, tag)
safe_tagged_name = astring.string_to_safe_path("%s.%s"
% (name[:250], tag))
else:
raise exceptions.TestSetupFail("Unable to find unique name in %s "
"iterations (%s).", i,
safe_tagged_name)
self.tag = "%s.%s" % (self.tag, tag) if self.tag else str(tag)
return tagged_name, utils_path.init_dir(logdir, safe_tagged_name)
def _record_reference_stdout(self): def _record_reference_stdout(self):
if self.datadir is not None: if self.datadir is not None:
utils_path.init_dir(self.datadir) utils_path.init_dir(self.datadir)
...@@ -520,7 +562,7 @@ class Test(unittest.TestCase): ...@@ -520,7 +562,7 @@ class Test(unittest.TestCase):
""" """
if self.fail_reason is not None: if self.fail_reason is not None:
self.log.error("%s %s -> %s: %s", self.status, self.log.error("%s %s -> %s: %s", self.status,
self.tagged_name, self.name,
self.fail_class, self.fail_class,
self.fail_reason) self.fail_reason)
...@@ -528,7 +570,7 @@ class Test(unittest.TestCase): ...@@ -528,7 +570,7 @@ class Test(unittest.TestCase):
if self.status is None: if self.status is None:
self.status = 'INTERRUPTED' self.status = 'INTERRUPTED'
self.log.info("%s %s", self.status, self.log.info("%s %s", self.status,
self.tagged_name) self.name)
def fail(self, message=None): def fail(self, message=None):
""" """
...@@ -608,7 +650,7 @@ class SimpleTest(Test): ...@@ -608,7 +650,7 @@ class SimpleTest(Test):
""" """
Returns the name of the file (path) that holds the current test Returns the name of the file (path) that holds the current test
""" """
return os.path.abspath(self.name) return os.path.abspath(self.name.name)
def _log_detailed_cmd_info(self, result): def _log_detailed_cmd_info(self, result):
""" """
...@@ -654,7 +696,7 @@ class ExternalRunnerTest(SimpleTest): ...@@ -654,7 +696,7 @@ class ExternalRunnerTest(SimpleTest):
self.external_runner = external_runner self.external_runner = external_runner
super(ExternalRunnerTest, self).__init__(name, params, base_logdir, super(ExternalRunnerTest, self).__init__(name, params, base_logdir,
tag, job) tag, job)
self._command = external_runner.runner + " " + name self._command = external_runner.runner + " " + self.name.name
@property @property
def filename(self): def filename(self):
......
...@@ -88,7 +88,7 @@ class XmlResult(object): ...@@ -88,7 +88,7 @@ class XmlResult(object):
""" """
tc = '\t<testcase classname={class} name={name} time="{time}"/>' tc = '\t<testcase classname={class} name={name} time="{time}"/>'
values = {'class': self._escape_attr(state.get('class_name', "<unknown>")), values = {'class': self._escape_attr(state.get('class_name', "<unknown>")),
'name': self._escape_attr(state.get('tagged_name', "<unknown>")), 'name': self._escape_attr(state.get('name', "<unknown>")),
'time': state.get('time_elapsed', -1)} 'time': state.get('time_elapsed', -1)}
self.testcases.append(tc.format(**values)) self.testcases.append(tc.format(**values))
...@@ -103,7 +103,7 @@ class XmlResult(object): ...@@ -103,7 +103,7 @@ class XmlResult(object):
\t\t<skipped /> \t\t<skipped />
\t</testcase>''' \t</testcase>'''
values = {'class': self._escape_attr(state.get('class_name', "<unknown>")), values = {'class': self._escape_attr(state.get('class_name', "<unknown>")),
'name': self._escape_attr(state.get('tagged_name', "<unknown>")), 'name': self._escape_attr(state.get('name', "<unknown>")),
'time': state.get('time_elapsed', -1)} 'time': state.get('time_elapsed', -1)}
self.testcases.append(tc.format(**values)) self.testcases.append(tc.format(**values))
...@@ -119,7 +119,7 @@ class XmlResult(object): ...@@ -119,7 +119,7 @@ class XmlResult(object):
\t\t<system-out><![CDATA[{systemout}]]></system-out> \t\t<system-out><![CDATA[{systemout}]]></system-out>
\t</testcase>''' \t</testcase>'''
values = {'class': self._escape_attr(state.get('class_name', "<unknown>")), values = {'class': self._escape_attr(state.get('class_name', "<unknown>")),
'name': self._escape_attr(state.get('tagged_name', "<unknown>")), 'name': self._escape_attr(state.get('name', "<unknown>")),
'time': state.get('time_elapsed', -1), 'time': state.get('time_elapsed', -1),
'type': self._escape_attr(state.get('fail_class', "<unknown>")), 'type': self._escape_attr(state.get('fail_class', "<unknown>")),
'traceback': self._escape_cdata(state.get('traceback', "<unknown>")), 'traceback': self._escape_cdata(state.get('traceback', "<unknown>")),
...@@ -139,7 +139,7 @@ class XmlResult(object): ...@@ -139,7 +139,7 @@ class XmlResult(object):
\t\t<system-out><![CDATA[{systemout}]]></system-out> \t\t<system-out><![CDATA[{systemout}]]></system-out>
\t</testcase>''' \t</testcase>'''
values = {'class': self._escape_attr(state.get('class_name', "<unknown>")), values = {'class': self._escape_attr(state.get('class_name', "<unknown>")),
'name': self._escape_attr(state.get('tagged_name', "<unknown>")), 'name': self._escape_attr(state.get('name', "<unknown>")),
'time': state.get('time_elapsed', -1), 'time': state.get('time_elapsed', -1),
'type': self._escape_attr(state.get('fail_class', "<unknown>")), 'type': self._escape_attr(state.get('fail_class', "<unknown>")),
'traceback': self._escape_cdata(state.get('traceback', "<unknown>")), 'traceback': self._escape_cdata(state.get('traceback', "<unknown>")),
......
...@@ -91,7 +91,7 @@ class TestResultJournal(TestResult): ...@@ -91,7 +91,7 @@ class TestResultJournal(TestResult):
status = None status = None
self.journal_cursor.execute(sql, self.journal_cursor.execute(sql,
(state['tagged_name'], (str(state['name']),
datetime.datetime(1, 1, 1).now().isoformat(), datetime.datetime(1, 1, 1).now().isoformat(),
action, action,
status)) status))
......
...@@ -385,7 +385,8 @@ class RunnerOperationTest(unittest.TestCase): ...@@ -385,7 +385,8 @@ class RunnerOperationTest(unittest.TestCase):
self.assertEqual(result.exit_status, expected_rc, self.assertEqual(result.exit_status, expected_rc,
"Avocado did not return rc %d:\n%s" % "Avocado did not return rc %d:\n%s" %
(expected_rc, result)) (expected_rc, result))
self.assertIn('MyTest.test_my_name -> TestError', result.stdout) self.assertIn('1-%s:MyTest.test_my_name -> TestError' % test,
result.stdout)
def tearDown(self): def tearDown(self):
shutil.rmtree(self.tmpdir) shutil.rmtree(self.tmpdir)
...@@ -452,7 +453,7 @@ class RunnerHumanOutputTest(unittest.TestCase): ...@@ -452,7 +453,7 @@ class RunnerHumanOutputTest(unittest.TestCase):
self.assertIn('[stdout] foo', result.stdout, result) self.assertIn('[stdout] foo', result.stdout, result)
self.assertIn('[stdout] \'"', result.stdout, result) self.assertIn('[stdout] \'"', result.stdout, result)
self.assertIn('[stdout] bar/baz', result.stdout, result) self.assertIn('[stdout] bar/baz', result.stdout, result)
self.assertIn('PASS /bin/echo -ne foo\\\\n\\\'\\"\\\\nbar/baz', self.assertIn('PASS 1-/bin/echo -ne foo\\\\n\\\'\\"\\\\nbar/baz',
result.stdout, result) result.stdout, result)
# logdir name should escape special chars (/) # logdir name should escape special chars (/)
test_dirs = glob.glob(os.path.join(self.tmpdir, 'latest', test_dirs = glob.glob(os.path.join(self.tmpdir, 'latest',
...@@ -461,7 +462,7 @@ class RunnerHumanOutputTest(unittest.TestCase): ...@@ -461,7 +462,7 @@ class RunnerHumanOutputTest(unittest.TestCase):
" test-results dir, but only one test was executed: " " test-results dir, but only one test was executed: "
"%s" % (test_dirs)) "%s" % (test_dirs))
self.assertEqual(os.path.basename(test_dirs[0]), self.assertEqual(os.path.basename(test_dirs[0]),
'_bin_echo -ne foo\\\\n\\\'\\"\\\\nbar_baz') '1-_bin_echo -ne foo\\\\n\\\'\\"\\\\nbar_baz')
def test_replay_skip_skipped(self): def test_replay_skip_skipped(self):
result = process.run("./scripts/avocado run skiponsetup --json -") result = process.run("./scripts/avocado run skiponsetup --json -")
...@@ -913,10 +914,10 @@ class PluginsJSONTest(AbsPluginsTest, unittest.TestCase): ...@@ -913,10 +914,10 @@ class PluginsJSONTest(AbsPluginsTest, unittest.TestCase):
0, 0) 0, 0)
# The executed test should be this # The executed test should be this
self.assertEqual(data['tests'][0]['url'], self.assertEqual(data['tests'][0]['url'],
'/bin/echo -ne foo\\\\n\\\'\\"\\\\nbar/baz') '1-/bin/echo -ne foo\\\\n\\\'\\"\\\\nbar/baz')
# logdir name should escape special chars (/) # logdir name should escape special chars (/)
self.assertEqual(os.path.basename(data['tests'][0]['logdir']), self.assertEqual(os.path.basename(data['tests'][0]['logdir']),
'_bin_echo -ne foo\\\\n\\\'\\"\\\\nbar_baz') '1-_bin_echo -ne foo\\\\n\\\'\\"\\\\nbar_baz')
def tearDown(self): def tearDown(self):
shutil.rmtree(self.tmpdir) shutil.rmtree(self.tmpdir)
......
...@@ -82,8 +82,9 @@ class StreamsTest(unittest.TestCase): ...@@ -82,8 +82,9 @@ class StreamsTest(unittest.TestCase):
result.stderr) result.stderr)
self.assertIn("Command line: %s" % cmd, self.assertIn("Command line: %s" % cmd,
result.stdout) result.stdout)
self.assertIn("START passtest", result.stdout) self.assertIn("\nSTART 1-passtest.py:PassTest.test",
self.assertIn("PASS passtest", result.stdout) result.stdout)
self.assertIn("PASS 1-passtest.py:PassTest.test", result.stdout)
self.assertEqual('', result.stderr) self.assertEqual('', result.stderr)
def test_none_success(self): def test_none_success(self):
......
...@@ -148,6 +148,7 @@ class LoaderTest(unittest.TestCase): ...@@ -148,6 +148,7 @@ class LoaderTest(unittest.TestCase):
test_class, test_parameters = ( test_class, test_parameters = (
self.loader.discover(simple_test.path, True)[0]) self.loader.discover(simple_test.path, True)[0])
self.assertTrue(test_class == test.SimpleTest, test_class) self.assertTrue(test_class == test.SimpleTest, test_class)
test_parameters['name'] = test.TestName(0, test_parameters['name'])
tc = test_class(**test_parameters) tc = test_class(**test_parameters)
tc.test() tc.test()
# Load with params # Load with params
...@@ -165,6 +166,7 @@ class LoaderTest(unittest.TestCase): ...@@ -165,6 +166,7 @@ class LoaderTest(unittest.TestCase):
test_class, test_parameters = ( test_class, test_parameters = (
self.loader.discover(simple_test.path, True)[0]) self.loader.discover(simple_test.path, True)[0])
self.assertTrue(test_class == test.NotATest, test_class) self.assertTrue(test_class == test.NotATest, test_class)
test_parameters['name'] = test.TestName(0, test_parameters['name'])
tc = test_class(**test_parameters) tc = test_class(**test_parameters)
self.assertRaises(exceptions.NotATestError, tc.test) self.assertRaises(exceptions.NotATestError, tc.test)
simple_test.remove() simple_test.remove()
...@@ -188,6 +190,7 @@ class LoaderTest(unittest.TestCase): ...@@ -188,6 +190,7 @@ class LoaderTest(unittest.TestCase):
test_class, test_parameters = ( test_class, test_parameters = (
self.loader.discover(avocado_not_a_test.path, True)[0]) self.loader.discover(avocado_not_a_test.path, True)[0])
self.assertTrue(test_class == test.NotATest, test_class) self.assertTrue(test_class == test.NotATest, test_class)
test_parameters['name'] = test.TestName(0, test_parameters['name'])
tc = test_class(**test_parameters) tc = test_class(**test_parameters)
self.assertRaises(exceptions.NotATestError, tc.test) self.assertRaises(exceptions.NotATestError, tc.test)
avocado_not_a_test.remove() avocado_not_a_test.remove()
...@@ -199,6 +202,7 @@ class LoaderTest(unittest.TestCase): ...@@ -199,6 +202,7 @@ class LoaderTest(unittest.TestCase):
test_class, test_parameters = ( test_class, test_parameters = (
self.loader.discover(avocado_not_a_test.path, True)[0]) self.loader.discover(avocado_not_a_test.path, True)[0])
self.assertTrue(test_class == test.SimpleTest, test_class) self.assertTrue(test_class == test.SimpleTest, test_class)
test_parameters['name'] = test.TestName(0, test_parameters['name'])
tc = test_class(**test_parameters) tc = test_class(**test_parameters)
# The test can't be executed (no shebang), raising an OSError # The test can't be executed (no shebang), raising an OSError
# (OSError: [Errno 8] Exec format error) # (OSError: [Errno 8] Exec format error)
...@@ -213,6 +217,7 @@ class LoaderTest(unittest.TestCase): ...@@ -213,6 +217,7 @@ class LoaderTest(unittest.TestCase):
test_class, test_parameters = ( test_class, test_parameters = (
self.loader.discover(avocado_simple_test.path, True)[0]) self.loader.discover(avocado_simple_test.path, True)[0])
self.assertTrue(test_class == test.SimpleTest) self.assertTrue(test_class == test.SimpleTest)
test_parameters['name'] = test.TestName(0, test_parameters['name'])
tc = test_class(**test_parameters) tc = test_class(**test_parameters)
tc.test() tc.test()
avocado_simple_test.remove() avocado_simple_test.remove()
...@@ -226,6 +231,7 @@ class LoaderTest(unittest.TestCase): ...@@ -226,6 +231,7 @@ class LoaderTest(unittest.TestCase):
test_class, test_parameters = ( test_class, test_parameters = (
self.loader.discover(avocado_simple_test.path, True)[0]) self.loader.discover(avocado_simple_test.path, True)[0])
self.assertTrue(test_class == test.NotATest) self.assertTrue(test_class == test.NotATest)
test_parameters['name'] = test.TestName(0, test_parameters['name'])
tc = test_class(**test_parameters) tc = test_class(**test_parameters)
self.assertRaises(exceptions.NotATestError, tc.test) self.assertRaises(exceptions.NotATestError, tc.test)
avocado_simple_test.remove() avocado_simple_test.remove()
......
...@@ -13,7 +13,7 @@ import logging ...@@ -13,7 +13,7 @@ import logging
cwd = os.getcwd() cwd = os.getcwd()
JSON_RESULTS = ('Something other than json\n' JSON_RESULTS = ('Something other than json\n'
'{"tests": [{"test": "sleeptest.1", "url": "sleeptest", ' '{"tests": [{"test": "1-sleeptest;0", "url": "sleeptest", '
'"fail_reason": "None", ' '"fail_reason": "None", '
'"status": "PASS", "time": 1.23, "start": 0, "end": 1.23}],' '"status": "PASS", "time": 1.23, "start": 0, "end": 1.23}],'
'"debuglog": "/home/user/avocado/logs/run-2014-05-26-15.45.' '"debuglog": "/home/user/avocado/logs/run-2014-05-26-15.45.'
...@@ -44,7 +44,7 @@ class RemoteTestRunnerTest(unittest.TestCase): ...@@ -44,7 +44,7 @@ class RemoteTestRunnerTest(unittest.TestCase):
log.should_receive("info") log.should_receive("info")
job = flexmock(args=Args, log=log, job = flexmock(args=Args, log=log,
urls=['/tests/sleeptest', '/tests/other/test', urls=['/tests/sleeptest', '/tests/other/test',
'passtest'], unique_id='sleeptest.1', 'passtest'], unique_id='1-sleeptest;0',
logdir="/local/path") logdir="/local/path")
flexmock(remote.RemoteTestRunner).should_receive('__init__') flexmock(remote.RemoteTestRunner).should_receive('__init__')
...@@ -56,7 +56,7 @@ class RemoteTestRunnerTest(unittest.TestCase): ...@@ -56,7 +56,7 @@ class RemoteTestRunnerTest(unittest.TestCase):
flexmock(logging).should_receive("FileHandler").and_return(filehandler) flexmock(logging).should_receive("FileHandler").and_return(filehandler)
test_results = flexmock(stdout=JSON_RESULTS, exit_status=0) test_results = flexmock(stdout=JSON_RESULTS, exit_status=0)
stream = flexmock(job_unique_id='sleeptest.1', stream = flexmock(job_unique_id='1-sleeptest;0',
debuglog='/local/path/dirname') debuglog='/local/path/dirname')
Remote = flexmock() Remote = flexmock()
Remoter = flexmock(remoter.Remote) Remoter = flexmock(remoter.Remote)
...@@ -102,7 +102,7 @@ _=/usr/bin/env''', exit_status=0) ...@@ -102,7 +102,7 @@ _=/usr/bin/env''', exit_status=0)
.with_args(args, ignore_status=True, timeout=60) .with_args(args, ignore_status=True, timeout=60)
.once().and_return(urls_result)) .once().and_return(urls_result))
args = ("cd ~/avocado/tests; avocado run --force-job-id sleeptest.1 " args = ("cd ~/avocado/tests; avocado run --force-job-id 1-sleeptest;0 "
"--json - --archive /tests/sleeptest /tests/other/test " "--json - --archive /tests/sleeptest /tests/other/test "
"passtest --multiplex ~/avocado/tests/foo.yaml " "passtest --multiplex ~/avocado/tests/foo.yaml "
"~/avocado/tests/bar/baz.yaml --dry-run") "~/avocado/tests/bar/baz.yaml --dry-run")
...@@ -116,14 +116,14 @@ _=/usr/bin/env''', exit_status=0) ...@@ -116,14 +116,14 @@ _=/usr/bin/env''', exit_status=0)
dry_run=True)) dry_run=True))
Results.should_receive('start_tests').once().ordered() Results.should_receive('start_tests').once().ordered()
args = {'status': u'PASS', 'whiteboard': '', 'time_start': 0, args = {'status': u'PASS', 'whiteboard': '', 'time_start': 0,
'name': u'sleeptest.1', 'class_name': 'RemoteTest', 'name': '1-sleeptest;0', 'class_name': 'RemoteTest',
'traceback': 'Not supported yet', 'traceback': 'Not supported yet',
'text_output': 'Not supported yet', 'time_end': 1.23, 'text_output': 'Not supported yet', 'time_end': 1.23,
'tagged_name': u'sleeptest.1', 'time_elapsed': 1.23, 'time_elapsed': 1.23,
'fail_class': 'Not supported yet', 'job_unique_id': '', 'fail_class': 'Not supported yet', 'job_unique_id': '',
'fail_reason': 'None', 'fail_reason': u'None',
'logdir': '/local/path/test-results/sleeptest.1', 'logdir': u'/local/path/test-results/1-sleeptest;0',
'logfile': '/local/path/test-results/sleeptest.1/debug.log'} 'logfile': u'/local/path/test-results/1-sleeptest;0/debug.log'}
Results.should_receive('start_test').once().with_args(args).ordered() Results.should_receive('start_test').once().with_args(args).ordered()
Results.should_receive('check_test').once().with_args(args).ordered() Results.should_receive('check_test').once().with_args(args).ordered()
(Remote.should_receive('receive_files') (Remote.should_receive('receive_files')
......
...@@ -39,19 +39,20 @@ class TestClassTestUnit(unittest.TestCase): ...@@ -39,19 +39,20 @@ class TestClassTestUnit(unittest.TestCase):
def testUglyName(self): def testUglyName(self):
def run(name, path_name): def run(name, path_name):
""" Initialize test and check the dirs were created """ """ Initialize test and check the dirs were created """
test = DummyTest("test", name, base_logdir=self.tmpdir) tst = DummyTest("test", test.TestName(1, name),
self.assertEqual(os.path.basename(test.logdir), path_name) base_logdir=self.tmpdir)
self.assertTrue(os.path.exists(test.logdir)) self.assertEqual(os.path.basename(tst.logdir), path_name)
self.assertEqual(os.path.dirname(os.path.dirname(test.logdir)), self.assertTrue(os.path.exists(tst.logdir))
self.assertEqual(os.path.dirname(os.path.dirname(tst.logdir)),
self.tmpdir) self.tmpdir)
run("/absolute/path", "_absolute_path") run("/absolute/path", "1-_absolute_path")
run("./relative/path", "__relative_path") run("./relative/path", "1-._relative_path")
run("../../multi_level/relative/path", run("../../multi_level/relative/path",
"_._.._multi_level_relative_path") "1-.._.._multi_level_relative_path")
# Greek word 'kosme' # Greek word 'kosme'
run("\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce\xb5", run("\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce\xb5",
"\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce\xb5") "1-\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce\xb5")
# Particularly problematic noncharacters in 16-bit applications # Particularly problematic noncharacters in 16-bit applications
name = ("\xb7\x95\xef\xb7\x96\xef\xb7\x97\xef\xb7\x98\xef\xb7\x99" name = ("\xb7\x95\xef\xb7\x96\xef\xb7\x97\xef\xb7\x98\xef\xb7\x99"
"\xef\xb7\x9a\xef\xb7\x9b\xef\xb7\x9c\xef\xb7\x9d\xef\xb7" "\xef\xb7\x9a\xef\xb7\x9b\xef\xb7\x9c\xef\xb7\x9d\xef\xb7"
...@@ -59,31 +60,48 @@ class TestClassTestUnit(unittest.TestCase): ...@@ -59,31 +60,48 @@ class TestClassTestUnit(unittest.TestCase):
"\xb7\xa3\xef\xb7\xa4\xef\xb7\xa5\xef\xb7\xa6\xef\xb7\xa7" "\xb7\xa3\xef\xb7\xa4\xef\xb7\xa5\xef\xb7\xa6\xef\xb7\xa7"
"\xef\xb7\xa8\xef\xb7\xa9\xef\xb7\xaa\xef\xb7\xab\xef\xb7" "\xef\xb7\xa8\xef\xb7\xa9\xef\xb7\xaa\xef\xb7\xab\xef\xb7"
"\xac\xef\xb7\xad\xef\xb7\xae\xef\xb7\xaf") "\xac\xef\xb7\xad\xef\xb7\xae\xef\xb7\xaf")
run(name, name) run(name, "1-" + name)
def testLongName(self): def testLongName(self):
test = DummyTest("test", "a" * 256, base_logdir=self.tmpdir) def check(uid, name, variant, exp_logdir):
self.assertEqual(os.path.basename(test.logdir), "a" * 250) tst = DummyTest("test", test.TestName(uid, name, variant))
test = DummyTest("test", "a" * 256, base_logdir=self.tmpdir) self.assertEqual(os.path.basename(tst.logdir), exp_logdir)
self.assertEqual(os.path.basename(test.logdir), "a" * 250 + ".1") return tst
self.assertEqual(os.path.basename(test.workdir),
os.path.basename(test.logdir)) # Everything fits
flexmock(test) check(1, "a" * 253, None, "1-" + ("a" * 253))
test.should_receive('filename').and_return(os.path.join(self.tmpdir, check(2, "a" * 251, 1, "2-" + ("a" * 251) + ";1")
"a"*250)) check(99, "a" * 249, 88, "99-" + ("a" * 249) + ";88")
# Shrink name
check(3, "a" * 252, 1, "3-" + ('a' * 251) + ";1")
# Shrink variant
check("a" * 253, "whatever", 99, "a" * 253 + ";9")
check("a" * 254, "whatever", 99, "a" * 254 + ";")
# No variant
tst = check("a" * 255, "whatever", "whatever-else", "a" * 255)
# Impossible to store (uid does not fit
self.assertRaises(AssertionError, check, "a" * 256, "whatever", "else",
None)
self.assertEqual(os.path.basename(tst.workdir),
os.path.basename(tst.logdir))
flexmock(tst)
tst.should_receive('filename').and_return(os.path.join(self.tmpdir,
"a"*250))
self.assertEqual(os.path.join(self.tmpdir, "a"*250 + ".data"), self.assertEqual(os.path.join(self.tmpdir, "a"*250 + ".data"),
test.datadir) tst.datadir)
test.should_receive('filename').and_return("a"*251) tst.should_receive('filename').and_return("a"*251)
self.assertFalse(test.datadir) self.assertFalse(tst.datadir)
test._record_reference_stdout # Should does nothing tst._record_reference_stdout # Should does nothing
test._record_reference_stderr # Should does nothing tst._record_reference_stderr # Should does nothing
test._record_reference_stdout() tst._record_reference_stdout()
test._record_reference_stderr() tst._record_reference_stderr()
def testAllDirsExistsNoHang(self): def testAllDirsExistsNoHang(self):
flexmock(os.path) flexmock(os.path)
os.path.should_receive('isdir').and_return(True) os.path.should_receive('exists').and_return(True)
self.assertRaises(exceptions.TestSetupFail, DummyTest, "test", "name") self.assertRaises(exceptions.TestSetupFail, DummyTest, "test",
test.TestName(1, "name"))
class TestClassTest(unittest.TestCase): class TestClassTest(unittest.TestCase):
...@@ -99,11 +117,9 @@ class TestClassTest(unittest.TestCase): ...@@ -99,11 +117,9 @@ class TestClassTest(unittest.TestCase):
self.base_logdir = tempfile.mkdtemp(prefix='avocado_' + __name__) self.base_logdir = tempfile.mkdtemp(prefix='avocado_' + __name__)
self.tst_instance_pass = AvocadoPass(base_logdir=self.base_logdir) self.tst_instance_pass = AvocadoPass(base_logdir=self.base_logdir)
self.tst_instance_pass.run_avocado() self.tst_instance_pass.run_avocado()
self.tst_instance_pass_new = AvocadoPass(base_logdir=self.base_logdir)
self.tst_instance_pass_new.run_avocado()
def testClassAttributesName(self): def testClassAttributesName(self):
self.assertEqual(self.tst_instance_pass.name, 'AvocadoPass') self.assertEqual(self.tst_instance_pass.name, '0-AvocadoPass')
def testClassAttributesStatus(self): def testClassAttributesStatus(self):
self.assertEqual(self.tst_instance_pass.status, 'PASS') self.assertEqual(self.tst_instance_pass.status, 'PASS')
...@@ -112,10 +128,7 @@ class TestClassTest(unittest.TestCase): ...@@ -112,10 +128,7 @@ class TestClassTest(unittest.TestCase):
self.assertIsInstance(self.tst_instance_pass.time_elapsed, float) self.assertIsInstance(self.tst_instance_pass.time_elapsed, float)
def testClassAttributesTag(self): def testClassAttributesTag(self):
self.assertEqual(self.tst_instance_pass.tag, "0") self.assertEqual(self.tst_instance_pass.tag, None)
def testClassAttributesTaggedName(self):
self.assertEqual(self.tst_instance_pass.tagged_name, "AvocadoPass")
def testWhiteboardSave(self): def testWhiteboardSave(self):
whiteboard_file = os.path.join( whiteboard_file = os.path.join(
...@@ -125,13 +138,14 @@ class TestClassTest(unittest.TestCase): ...@@ -125,13 +138,14 @@ class TestClassTest(unittest.TestCase):
whiteboard_contents = whiteboard_file_obj.read().strip() whiteboard_contents = whiteboard_file_obj.read().strip()
self.assertTrue(whiteboard_contents, 'foo') self.assertTrue(whiteboard_contents, 'foo')
def testTaggedNameNewTests(self): def testRunningTestTwiceWithTheSameUidFailure(self):
""" class AvocadoPass(test.Test):
New test instances should have crescent tag instances.
""" def test(self):
self.assertEqual( pass
self.tst_instance_pass_new.tagged_name, "AvocadoPass.1")
self.assertEqual(self.tst_instance_pass_new.tag, "1") self.assertRaises(exceptions.TestSetupFail, AvocadoPass,
base_logdir=self.base_logdir)
def tearDown(self): def tearDown(self):
shutil.rmtree(self.base_logdir) shutil.rmtree(self.base_logdir)
...@@ -154,12 +168,12 @@ class SimpleTestClassTest(unittest.TestCase): ...@@ -154,12 +168,12 @@ class SimpleTestClassTest(unittest.TestCase):
self.fail_script.save() self.fail_script.save()
self.tst_instance_pass = test.SimpleTest( self.tst_instance_pass = test.SimpleTest(
name=self.pass_script.path, name=test.TestName(1, self.pass_script.path),
base_logdir=self.tmpdir) base_logdir=self.tmpdir)
self.tst_instance_pass.run_avocado() self.tst_instance_pass.run_avocado()
self.tst_instance_fail = test.SimpleTest( self.tst_instance_fail = test.SimpleTest(
name=self.fail_script.path, name=test.TestName(1, self.fail_script.path),
base_logdir=self.tmpdir) base_logdir=self.tmpdir)
self.tst_instance_fail.run_avocado() self.tst_instance_fail.run_avocado()
...@@ -186,47 +200,46 @@ class SkipTest(unittest.TestCase): ...@@ -186,47 +200,46 @@ class SkipTest(unittest.TestCase):
self.assertRaises(exceptions.TestSkipError, self.tests[-1].setUp) self.assertRaises(exceptions.TestSkipError, self.tests[-1].setUp)
self.assertRaises(RuntimeError, self.tests[-1].test) self.assertRaises(RuntimeError, self.tests[-1].test)
# Positional # Positional
self.tests.append(test.SkipTest("test", "my_name", {}, None, "1", self.tests.append(test.SkipTest("test", test.TestName(1, "my_name"),
{}, None, "1",
None, None, "extra_param1", None, None, "extra_param1",
"extra_param2")) "extra_param2"))
self.assertEqual(self.tests[-1].name, "my_name") self.assertEqual(self.tests[-1].name, "1-my_name")
self.assertEqual(self.tests[-1].tagged_name, "my_name.1")
# Kwargs # Kwargs
self.tests.append(test.SkipTest(methodName="test", name="my_name2", self.tests.append(test.SkipTest(methodName="test",
name=test.TestName(1, "my_name2"),
params={}, base_logdir=None, params={}, base_logdir=None,
tag="a", job=None, runner_queue=None, tag="a", job=None, runner_queue=None,
extra1="extra_param1", extra1="extra_param1",
extra2="extra_param2")) extra2="extra_param2"))
self.assertEqual(self.tests[-1].name, "my_name2") self.assertEqual(self.tests[-1].name, "1-my_name2")
self.assertEqual(self.tests[-1].tagged_name, "my_name2.a")
# both (theoretically impossible in python, but valid for nasty tests) # both (theoretically impossible in python, but valid for nasty tests)
# keyword args are used as they explicitly represent what they mean # keyword args are used as they explicitly represent what they mean
self.tests.append(test.SkipTest("not used", "who cares", {}, None, "0", self.tests.append(test.SkipTest("not used", "who cares", {}, None, "0",
None, None, "extra_param1", None, None, "extra_param1",
"extra_param2", "extra_param2",
methodName="test", name="my_name3", methodName="test",
name=test.TestName(1, "my_name3"),
params={}, base_logdir=None, params={}, base_logdir=None,
tag="3", job=None, runner_queue=None, tag="3", job=None, runner_queue=None,
extra1="extra_param3", extra1="extra_param3",
extra2="extra_param4")) extra2="extra_param4"))
self.assertEqual(self.tests[-1].name, "my_name3") self.assertEqual(self.tests[-1].name, "1-my_name3")
self.assertEqual(self.tests[-1].tagged_name, "my_name3.3")
# combination # combination
self.tests.append(test.SkipTest("test", "my_name4", tag="321", self.tests.append(test.SkipTest("test", test.TestName(1, "my_name4"),
tag="321",
other_param="Whatever")) other_param="Whatever"))
self.assertEqual(self.tests[-1].name, "my_name4") self.assertEqual(self.tests[-1].name, "1-my_name4")
self.assertEqual(self.tests[-1].tagged_name, "my_name4.321")
# ugly combination (positional argument overrides kwargs, this only # ugly combination (positional argument overrides kwargs, this only
# happens when the substituted class reorders the positional arguments. # happens when the substituted class reorders the positional arguments.
# We try to first match keyword args and then fall-back to positional # We try to first match keyword args and then fall-back to positional
# ones. # ones.
name = "positional_method_name_becomes_test_name" name = "positional_method_name_becomes_test_name"
tag = "positional_base_logdir_becomes_tag" tag = "positional_base_logdir_becomes_tag"
self.tests.append(test.SkipTest(name, None, None, tag, self.tests.append(test.SkipTest(test.TestName(1, name), None, None, tag,
methodName="test", methodName="test",
other_param="Whatever")) other_param="Whatever"))
self.assertEqual(self.tests[-1].name, name) self.assertEqual(self.tests[-1].name, "1-" + name)
self.assertEqual(self.tests[-1].tagged_name, "%s.%s" % (name, tag))
def tearDown(self): def tearDown(self):
for tst in self.tests: for tst in self.tests:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册