summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Collins <robertc@robertcollins.net>2013-04-10 00:06:35 +1200
committerRobert Collins <robertc@robertcollins.net>2013-04-10 00:06:35 +1200
commit10892056dad5f2eb482fa6a3582589d875d26023 (patch)
tree1e7f86660c82b811e15c4b3302fd311bcac1d609
parentdcee5da3a240a2d241589f7b2cd431e31e45580f (diff)
downloadtestrepository-10892056dad5f2eb482fa6a3582589d875d26023.tar.gz
Move tag based test filtering into the UI: many test things won't be filtered, such as slow tests and pdb debugging, so the UI has to see the tests. Moving the responsibility into the UI may lead to repetition in other UI's if not made easy to reuse, but that seems like the lesser of evils for now.
-rw-r--r--.testr.conf1
-rw-r--r--testrepository/commands/failing.py2
-rw-r--r--testrepository/testcommand.py33
-rw-r--r--testrepository/tests/__init__.py11
-rw-r--r--testrepository/tests/commands/test_failing.py11
-rw-r--r--testrepository/tests/commands/test_last.py13
-rw-r--r--testrepository/tests/commands/test_load.py12
-rw-r--r--testrepository/tests/test_testcommand.py46
-rw-r--r--testrepository/tests/ui/test_cli.py37
-rw-r--r--testrepository/ui/cli.py42
-rw-r--r--testrepository/ui/model.py3
11 files changed, 79 insertions, 132 deletions
diff --git a/.testr.conf b/.testr.conf
index c54ceb6..8cd68fb 100644
--- a/.testr.conf
+++ b/.testr.conf
@@ -2,3 +2,4 @@
test_command=${PYTHON:-python} -m subunit.run $LISTOPT $IDOPTION testrepository.tests.test_suite
test_id_option=--load-list $IDFILE
test_list_option=--list
+;filter_tags=worker-0
diff --git a/testrepository/commands/failing.py b/testrepository/commands/failing.py
index 9cc1ed7..efea923 100644
--- a/testrepository/commands/failing.py
+++ b/testrepository/commands/failing.py
@@ -58,7 +58,7 @@ class failing(Command):
testcommand = self.command_factory(self.ui, repo)
if self.ui.options.list:
list_result = testtools.StreamSummary()
- return testcommand.make_result(list_result), list_result
+ return list_result, list_result
else:
return self.ui.make_result(repo.latest_id, testcommand)
diff --git a/testrepository/testcommand.py b/testrepository/testcommand.py
index 590066e..229017b 100644
--- a/testrepository/testcommand.py
+++ b/testrepository/testcommand.py
@@ -567,39 +567,6 @@ class TestCommand(Fixture):
self._allocated_instances.add(result)
return result
- def make_result(self, receiver):
- """Create a TestResult that will perform any global filtering etc.
-
- :param receiver: The result to forward the result of global filtering.
- :return: A TestResult.
- """
- filter_tags = self.get_filter_tags()
- if filter_tags:
- try:
- from subunit.test_results import make_tag_filter
- except ImportError:
- raise ValueError(
- "Subunit not installed or does not have tag filtering support")
- # predicates return False to filter something out. We want to
- # filter out tagged tests *unless* they fail/error. So we want
- # tag_p:False + outcome_p:False -> False
- # tag_p:False + outcome_p:True -> True
- # tag_p:True + * -> True
- def error_or_fail(t, outcome, e, d, tags):
- return outcome in ('error', 'failure')
- def or_predicates(predicates):
- def fn(*args, **kwargs):
- for predicate in predicates:
- if predicate(*args, **kwargs):
- return True
- return False
- return fn
- predicates = [make_tag_filter(None, filter_tags), error_or_fail]
- predicate = or_predicates(predicates)
- return TestResultFilter(
- receiver, filter_success=False, filter_predicate=predicate)
- return receiver
-
def release_instance(self, instance_id):
"""Return instance_ids to the pool for reuse."""
self._allocated_instances.remove(instance_id)
diff --git a/testrepository/tests/__init__.py b/testrepository/tests/__init__.py
index 847aec8..d6f38dc 100644
--- a/testrepository/tests/__init__.py
+++ b/testrepository/tests/__init__.py
@@ -19,8 +19,6 @@ import unittest
import testresources
from testscenarios import generate_scenarios
from testtools import TestCase
-from testtools.testresult.doubles import ExtendedTestResult
-from testtools.testresult.real import MultiTestResult
class ResourcedTestCase(TestCase, testresources.ResourcedTestCase):
@@ -45,16 +43,15 @@ Wildcard = _Wildcard()
class StubTestCommand:
- def __init__(self):
+ def __init__(self, filter_tags=None):
self.results = []
+ self.filter_tags = filter_tags or set()
def __call__(self, ui, repo):
return self
- def make_result(self, receiver):
- result = ExtendedTestResult()
- self.results.append(result)
- return MultiTestResult(result, receiver)
+ def get_filter_tags(self):
+ return self.filter_tags
def test_suite():
diff --git a/testrepository/tests/commands/test_failing.py b/testrepository/tests/commands/test_failing.py
index 5cda8cd..1e25e87 100644
--- a/testrepository/tests/commands/test_failing.py
+++ b/testrepository/tests/commands/test_failing.py
@@ -134,14 +134,3 @@ class TestCommand(ResourcedTestCase):
cmd.repository_factory.initialise(ui.here)
self.assertEqual(1, cmd.execute())
self.assertEqual([True], calls)
-
- def test_grabs_TestCommand_result(self):
- ui, cmd = self.get_test_ui_and_cmd()
- cmd.repository_factory = memory.RepositoryFactory()
- calls = []
- cmd.repository_factory.initialise(ui.here)
- cmd.command_factory = StubTestCommand()
- cmd.execute()
- self.assertEqual(
- [('startTestRun',), ('stopTestRun',)],
- cmd.command_factory.results[0]._events)
diff --git a/testrepository/tests/commands/test_last.py b/testrepository/tests/commands/test_last.py
index fb62cbe..39c20dc 100644
--- a/testrepository/tests/commands/test_last.py
+++ b/testrepository/tests/commands/test_last.py
@@ -94,19 +94,6 @@ class TestCommand(ResourcedTestCase):
self.assertEqual(1, len(result.failures))
self.assertEqual(2, result.testsRun)
- def test_grabs_TestCommand_result(self):
- ui, cmd = self.get_test_ui_and_cmd()
- cmd.repository_factory = memory.RepositoryFactory()
- repo = cmd.repository_factory.initialise(ui.here)
- inserter = repo.get_inserter()
- inserter.startTestRun()
- inserter.stopTestRun()
- cmd.command_factory = StubTestCommand()
- cmd.execute()
- self.assertEqual(
- [('startTestRun',), ('stopTestRun',)],
- cmd.command_factory.results[0]._events)
-
def test_shows_subunit_stream(self):
ui, cmd = self.get_test_ui_and_cmd(options=[('subunit', True)])
cmd.repository_factory = memory.RepositoryFactory()
diff --git a/testrepository/tests/commands/test_load.py b/testrepository/tests/commands/test_load.py
index 2b1f90e..1360e41 100644
--- a/testrepository/tests/commands/test_load.py
+++ b/testrepository/tests/commands/test_load.py
@@ -313,15 +313,3 @@ class TestCommandLoad(ResourcedTestCase):
[('summary', False, 2, 1, 6.0, -3.0,
[('id', 1, None), ('failures', 2, 1)])],
ui.outputs[1:])
-
- def test_grabs_TestCommand_result(self):
- ui = UI([('subunit', _b(''))])
- cmd = load.load(ui)
- ui.set_command(cmd)
- cmd.repository_factory = memory.RepositoryFactory()
- cmd.repository_factory.initialise(ui.here)
- cmd.command_factory = StubTestCommand()
- cmd.execute()
- self.assertEqual(
- [('startTestRun',), ('stopTestRun',)],
- cmd.command_factory.results[0]._events)
diff --git a/testrepository/tests/test_testcommand.py b/testrepository/tests/test_testcommand.py
index 21d5c7a..43be8e6 100644
--- a/testrepository/tests/test_testcommand.py
+++ b/testrepository/tests/test_testcommand.py
@@ -488,52 +488,6 @@ class TestTestCommand(ResourcedTestCase):
self.assertEqual(None, fixture.callout_concurrency())
self.assertEqual([], ui.outputs)
- def test_make_result(self):
- # Just a simple 'the dots are joined' test. More later.
- ui, command = self.get_test_ui_and_cmd()
- log = ExtendedTestResult()
- self.set_config('[DEFAULT]\n')
- result = command.make_result(log)
- result.startTestRun()
- result.stopTestRun()
- self.assertEqual([('startTestRun',), ('stopTestRun',)], log._events)
-
- def test_make_result_tag_filter(self):
- ui, command = self.get_test_ui_and_cmd()
- self.set_config('[DEFAULT]\nfilter_tags=foo bar\n')
- log = ExtendedTestResult()
- class Tests(ResourcedTestCase):
- def ignored(self): pass
- def fails(self): self.fail('foo')
- def filtered(self): pass
- try:
- result = command.make_result(log)
- except ValueError:
- self.skip("Subunit too old for tag filtering support.")
- result.startTestRun()
- result.tags(set(['ignored']), set())
- ignored = Tests("ignored")
- ignored.run(result)
- result.tags(set(['foo']), set())
- fails = Tests("fails")
- fails.run(result)
- filtered = Tests("filtered")
- filtered.run(result)
- result.stopTestRun()
- self.assertEqual([
- ('startTestRun',),
- ('tags', set(['ignored']), set()),
- ('startTest', ignored),
- ('addSuccess', ignored),
- ('stopTest', ignored),
- ('tags', set(['foo']), set()),
- ('startTest', fails),
- ('addFailure', fails, Wildcard),
- ('stopTest', fails),
- ('stopTestRun',),
- ],
- log._events)
-
def test_filter_tests_by_regex_only(self):
if v2_avail:
buffer = BytesIO()
diff --git a/testrepository/tests/ui/test_cli.py b/testrepository/tests/ui/test_cli.py
index 01ac8cc..145daa3 100644
--- a/testrepository/tests/ui/test_cli.py
+++ b/testrepository/tests/ui/test_cli.py
@@ -320,7 +320,7 @@ class TestCLITestResult(TestCase):
except ZeroDivisionError:
return sys.exc_info()
- def make_result(self, stream=None, argv=None):
+ def make_result(self, stream=None, argv=None, filter_tags=None):
if stream is None:
stream = BytesIO()
argv = argv or []
@@ -331,7 +331,8 @@ class TestCLITestResult(TestCase):
default=False, help="Display results in subunit format."),
]
ui.set_command(cmd)
- return ui.make_result(lambda: None, StubTestCommand())
+ return ui.make_result(
+ lambda: None, StubTestCommand(filter_tags=filter_tags))
def test_initial_stream(self):
# CLITestResult.__init__ does not do anything to the stream it is
@@ -342,7 +343,7 @@ class TestCLITestResult(TestCase):
def _unwrap(self, result):
"""Unwrap result to get to the CLI result object."""
- return result.targets[0].decorated.decorated._results[1].decorated
+ return result.targets[0].decorated.decorated
def test_format_error(self):
# CLITestResult formats errors by giving them a big fat line, a title
@@ -405,3 +406,33 @@ class TestCLITestResult(TestCase):
result.startTestRun()
result.stopTestRun()
self.assertEqual(b'', bytestream.getvalue())
+
+ def test_make_result_tag_filter(self):
+ stream = StringIO()
+ result, summary = self.make_result(
+ stream, filter_tags=set(['worker-0']))
+ # Generate a bunch of results with tags in the same events that
+ # testtools generates them.
+ tags = set(['worker-0'])
+ result.startTestRun()
+ result.status(test_id='pass', test_status='inprogress')
+ result.status(test_id='pass', test_status='success', test_tags=tags)
+ result.status(test_id='fail', test_status='inprogress')
+ result.status(test_id='fail', test_status='fail', test_tags=tags)
+ result.status(test_id='xfail', test_status='inprogress')
+ result.status(test_id='xfail', test_status='xfail', test_tags=tags)
+ result.status(test_id='uxsuccess', test_status='inprogress')
+ result.status(
+ test_id='uxsuccess', test_status='uxsuccess', test_tags=tags)
+ result.status(test_id='skip', test_status='inprogress')
+ result.status(test_id='skip', test_status='skip', test_tags=tags)
+ result.stopTestRun()
+ self.assertEqual("""\
+======================================================================
+FAIL: fail
+tags: worker-0
+----------------------------------------------------------------------
+Ran 1 tests
+FAILED (id=None, failures=1)
+""", stream.getvalue())
+
diff --git a/testrepository/ui/cli.py b/testrepository/ui/cli.py
index 1c8840d..8744281 100644
--- a/testrepository/ui/cli.py
+++ b/testrepository/ui/cli.py
@@ -34,12 +34,19 @@ from testrepository.results import TestResultFilter
class CLITestResult(ui.BaseUITestResult):
"""A TestResult for the CLI."""
- def __init__(self, ui, get_id, stream, previous_run=None):
- """Construct a CLITestResult writing to stream."""
+ def __init__(self, ui, get_id, stream, previous_run=None, filter_tags=None):
+ """Construct a CLITestResult writing to stream.
+
+ :param filter_tags: Tags that should be used to filter tests out. When
+ a tag in this set is present on a test outcome, the test is not
+ counted towards the test run count. If the test errors, then it is
+ still counted and the error is still shown.
+ """
super(CLITestResult, self).__init__(ui, get_id, previous_run)
self.stream = unicode_output_stream(stream)
self.sep1 = _u('=' * 70 + '\n')
self.sep2 = _u('-' * 70 + '\n')
+ self.filter_tags = filter_tags or frozenset()
def _format_error(self, label, test, error_text):
tags = _u(' ').join(self.current_tags)
@@ -61,6 +68,31 @@ class CLITestResult(ui.BaseUITestResult):
super(CLITestResult, self).addFailure(test, err=err, details=details)
self.stream.write(self._format_error(_u('FAIL'), *(self.failures[-1])))
+ def addSuccess(self, test, details=None):
+ if self.current_tags.intersection(self.filter_tags):
+ self.testsRun -= 1
+ return
+ super(CLITestResult, self).addSuccess(test, details=details)
+
+ def addUnexpectedSuccess(self, test, details=None):
+ if self.current_tags.intersection(self.filter_tags):
+ self.testsRun -= 1
+ return
+ super(CLITestResult, self).addUnexpectedSuccess(test, details=details)
+
+ def addExpectedFailure(self, test, err=None, details=None):
+ if self.current_tags.intersection(self.filter_tags):
+ self.testsRun -= 1
+ return
+ super(CLITestResult, self).addExpectedFailure(
+ test, err=err, details=details)
+
+ def addSkip(self, test, reason=None, details=None):
+ if self.current_tags.intersection(self.filter_tags):
+ self.testsRun -= 1
+ return
+ super(CLITestResult, self).addSkip(test, reason=reason, details=details)
+
class UI(ui.AbstractUI):
"""A command line user interface."""
@@ -96,9 +128,11 @@ class UI(ui.AbstractUI):
summary.stopTestRun()
return result, summary
else:
- output = CLITestResult(self, get_id, self._stdout, previous_run)
+ filter_tags = test_command.get_filter_tags()
+ output = CLITestResult(self, get_id, self._stdout, previous_run,
+ filter_tags=filter_tags)
# Apply user defined transforms.
- result = ExtendedToStreamDecorator(StreamToExtendedDecorator(test_command.make_result(output)))
+ result = ExtendedToStreamDecorator(StreamToExtendedDecorator(output))
return result, result
def output_error(self, error_tuple):
diff --git a/testrepository/ui/model.py b/testrepository/ui/model.py
index 59717f8..8d86801 100644
--- a/testrepository/ui/model.py
+++ b/testrepository/ui/model.py
@@ -158,8 +158,7 @@ class UI(ui.AbstractUI):
def make_result(self, get_id, test_command, previous_run=None):
result = testtools.ExtendedToStreamDecorator(
testtools.StreamToExtendedDecorator(
- test_command.make_result(
- TestResultModel(self, get_id, previous_run))))
+ TestResultModel(self, get_id, previous_run)))
return result, result
def output_error(self, error_tuple):