Remove unnecessary python path updates for bundled subunit/testtools.
[samba.git] / lib / subunit / filters / subunit-filter
1 #!/usr/bin/env python
2 #  subunit: extensions to python unittest to get test results from subprocesses.
3 #  Copyright (C) 2008  Robert Collins <robertc@robertcollins.net>
4 #            (C) 2009  Martin Pool
5 #
6 #  Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
7 #  license at the users choice. A copy of both licenses are available in the
8 #  project source as Apache-2.0 and BSD. You may not use this file except in
9 #  compliance with one of these two licences.
10 #  
11 #  Unless required by applicable law or agreed to in writing, software
12 #  distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
13 #  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
14 #  license you chose for the specific language governing permissions and
15 #  limitations under that license.
16 #
17
18 """Filter a subunit stream to include/exclude tests.
19
20 The default is to strip successful tests.
21
22 Tests can be filtered by Python regular expressions with --with and --without,
23 which match both the test name and the error text (if any).  The result
24 contains tests which match any of the --with expressions and none of the
25 --without expressions.  For case-insensitive matching prepend '(?i)'.
26 Remember to quote shell metacharacters.
27 """
28
29 from optparse import OptionParser
30 import sys
31 import re
32
33 from subunit import (
34     DiscardStream,
35     ProtocolTestCase,
36     TestProtocolClient,
37     read_test_list,
38     )
39 from subunit.filters import filter_by_result
40 from subunit.test_results import (
41     and_predicates,
42     make_tag_filter,
43     TestResultFilter,
44     )
45
46
47 def make_options(description):
48     parser = OptionParser(description=__doc__)
49     parser.add_option("--error", action="store_false",
50         help="include errors", default=False, dest="error")
51     parser.add_option("-e", "--no-error", action="store_true",
52         help="exclude errors", dest="error")
53     parser.add_option("--failure", action="store_false",
54         help="include failures", default=False, dest="failure")
55     parser.add_option("-f", "--no-failure", action="store_true",
56         help="exclude failures", dest="failure")
57     parser.add_option("--passthrough", action="store_false",
58         help="Show all non subunit input.", default=False, dest="no_passthrough")
59     parser.add_option("--no-passthrough", action="store_true",
60         help="Hide all non subunit input.", default=False, dest="no_passthrough")
61     parser.add_option("-s", "--success", action="store_false",
62         help="include successes", dest="success")
63     parser.add_option("--no-success", action="store_true",
64         help="exclude successes", default=True, dest="success")
65     parser.add_option("--no-skip", action="store_true",
66         help="exclude skips", dest="skip")
67     parser.add_option("--xfail", action="store_false",
68         help="include expected falures", default=True, dest="xfail")
69     parser.add_option("--no-xfail", action="store_true",
70         help="exclude expected falures", default=True, dest="xfail")
71     parser.add_option(
72         "--with-tag", type=str,
73         help="include tests with these tags", action="append", dest="with_tags")
74     parser.add_option(
75         "--without-tag", type=str,
76         help="exclude tests with these tags", action="append", dest="without_tags")
77     parser.add_option("-m", "--with", type=str,
78         help="regexp to include (case-sensitive by default)",
79         action="append", dest="with_regexps")
80     parser.add_option("--fixup-expected-failures", type=str,
81         help="File with list of test ids that are expected to fail; on failure "
82              "their result will be changed to xfail; on success they will be "
83              "changed to error.", dest="fixup_expected_failures", action="append")
84     parser.add_option("--without", type=str,
85         help="regexp to exclude (case-sensitive by default)",
86         action="append", dest="without_regexps")
87     parser.add_option("-F", "--only-genuine-failures", action="callback",
88         callback=only_genuine_failures_callback,
89         help="Only pass through failures and exceptions.")
90     return parser
91
92
93 def only_genuine_failures_callback(option, opt, value, parser):
94     parser.rargs.insert(0, '--no-passthrough')
95     parser.rargs.insert(0, '--no-xfail')
96     parser.rargs.insert(0, '--no-skip')
97     parser.rargs.insert(0, '--no-success')
98
99
100 def _compile_re_from_list(l):
101     return re.compile("|".join(l), re.MULTILINE)
102
103
104 def _make_regexp_filter(with_regexps, without_regexps):
105     """Make a callback that checks tests against regexps.
106
107     with_regexps and without_regexps are each either a list of regexp strings,
108     or None.
109     """
110     with_re = with_regexps and _compile_re_from_list(with_regexps)
111     without_re = without_regexps and _compile_re_from_list(without_regexps)
112
113     def check_regexps(test, outcome, err, details, tags):
114         """Check if this test and error match the regexp filters."""
115         test_str = str(test) + outcome + str(err) + str(details)
116         if with_re and not with_re.search(test_str):
117             return False
118         if without_re and without_re.search(test_str):
119             return False
120         return True
121     return check_regexps
122
123
124 def _make_result(output, options, predicate):
125     """Make the result that we'll send the test outcomes to."""
126     fixup_expected_failures = set()
127     for path in options.fixup_expected_failures or ():
128         fixup_expected_failures.update(read_test_list(path))
129     return TestResultFilter(
130         TestProtocolClient(output),
131         filter_error=options.error,
132         filter_failure=options.failure,
133         filter_success=options.success,
134         filter_skip=options.skip,
135         filter_xfail=options.xfail,
136         filter_predicate=predicate,
137         fixup_expected_failures=fixup_expected_failures)
138
139
140 def main():
141     parser = make_options(__doc__)
142     (options, args) = parser.parse_args()
143
144     regexp_filter = _make_regexp_filter(
145         options.with_regexps, options.without_regexps)
146     tag_filter = make_tag_filter(options.with_tags, options.without_tags)
147     filter_predicate = and_predicates([regexp_filter, tag_filter])
148
149     filter_by_result(
150         lambda output_to: _make_result(sys.stdout, options, filter_predicate),
151         output_path=None,
152         passthrough=(not options.no_passthrough),
153         forward=False)
154     sys.exit(0)
155
156
157 if __name__ == '__main__':
158     main()