Merge lp:~thomir-deactivatedaccount/autopilot/trunk-add-new-formats into lp:autopilot
- trunk-add-new-formats
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Christopher Lee | ||||
Approved revision: | 383 | ||||
Merged at revision: | 378 | ||||
Proposed branch: | lp:~thomir-deactivatedaccount/autopilot/trunk-add-new-formats | ||||
Merge into: | lp:autopilot | ||||
Diff against target: |
406 lines (+173/-97) 5 files modified
autopilot/__init__.py (+6/-13) autopilot/run.py (+14/-52) autopilot/testresult.py (+57/-30) autopilot/tests/unit/test_testresults.py (+92/-0) debian/control (+4/-2) |
||||
To merge this branch: | bzr merge lp:~thomir-deactivatedaccount/autopilot/trunk-add-new-formats | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Christopher Lee (community) | Approve | ||
PS Jenkins bot | continuous-integration | Approve | |
Review via email: mp+197306@code.launchpad.net |
Commit message
Make autopilot support subunit bytestream output.
Description of the change
Make autopilot support subunit bytestream output.
- 380. By Thomi Richards
-
Merged trunk.
- 381. By Thomi Richards
-
Fix commented out code and incorrect copyright header date.
PS Jenkins bot (ps-jenkins) wrote : | # |
- 382. By Thomi Richards
-
Add junitxml as a build-dep.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:381
http://
Executed test runs:
None: http://
None: http://
None: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:380
http://
Executed test runs:
None: http://
None: http://
None: http://
Click here to trigger a rebuild:
http://
- 383. By Thomi Richards
-
Fix PEP8.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:382
http://
Executed test runs:
FAILURE: http://
None: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:383
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === modified file 'autopilot/__init__.py' | |||
2 | --- autopilot/__init__.py 2013-11-26 22:54:36 +0000 | |||
3 | +++ autopilot/__init__.py 2013-12-02 02:12:42 +0000 | |||
4 | @@ -20,20 +20,12 @@ | |||
5 | 20 | from argparse import ArgumentParser, REMAINDER, Action | 20 | from argparse import ArgumentParser, REMAINDER, Action |
6 | 21 | import subprocess | 21 | import subprocess |
7 | 22 | 22 | ||
8 | 23 | from autopilot.testresult import get_output_formats, get_default_format | ||
9 | 24 | from autopilot.exceptions import BackendException | ||
10 | 25 | |||
11 | 23 | version = '1.4.0' | 26 | version = '1.4.0' |
12 | 24 | 27 | ||
13 | 25 | 28 | ||
14 | 26 | class BackendException(RuntimeError): | ||
15 | 27 | |||
16 | 28 | """An error occured while trying to initialise an autopilot backend.""" | ||
17 | 29 | |||
18 | 30 | def __init__(self, original_exception): | ||
19 | 31 | super(BackendException, self).__init__( | ||
20 | 32 | "Error while initialising backend. Original exception was: " + | ||
21 | 33 | str(original_exception)) | ||
22 | 34 | self.original_exception = original_exception | ||
23 | 35 | |||
24 | 36 | |||
25 | 37 | def parse_arguments(argv=None): | 29 | def parse_arguments(argv=None): |
26 | 38 | """Parse command-line arguments, and return an argparse arguments | 30 | """Parse command-line arguments, and return an argparse arguments |
27 | 39 | object. | 31 | object. |
28 | @@ -57,8 +49,9 @@ | |||
29 | 57 | If given a directory instead of a file will \ | 49 | If given a directory instead of a file will \ |
30 | 58 | write to a file in that directory named: \ | 50 | write to a file in that directory named: \ |
31 | 59 | <hostname>_<dd.mm.yyy_HHMMSS>.log') | 51 | <hostname>_<dd.mm.yyy_HHMMSS>.log') |
34 | 60 | parser_run.add_argument('-f', "--format", choices=['text', 'xml'], | 52 | available_formats = get_output_formats().keys() |
35 | 61 | default='text', | 53 | parser_run.add_argument('-f', "--format", choices=available_formats, |
36 | 54 | default=get_default_format(), | ||
37 | 62 | required=False, | 55 | required=False, |
38 | 63 | help='Specify desired output format. \ | 56 | help='Specify desired output format. \ |
39 | 64 | Default is "text".') | 57 | Default is "text".') |
40 | 65 | 58 | ||
41 | === modified file 'autopilot/run.py' | |||
42 | --- autopilot/run.py 2013-11-28 04:03:49 +0000 | |||
43 | +++ autopilot/run.py 2013-12-02 02:12:42 +0000 | |||
44 | @@ -41,11 +41,10 @@ | |||
45 | 41 | from unittest import TestLoader, TestSuite | 41 | from unittest import TestLoader, TestSuite |
46 | 42 | 42 | ||
47 | 43 | from testtools import iterate_tests | 43 | from testtools import iterate_tests |
48 | 44 | from testtools import TextTestResult | ||
49 | 45 | 44 | ||
50 | 46 | from autopilot import get_version_string, parse_arguments | 45 | from autopilot import get_version_string, parse_arguments |
51 | 47 | import autopilot.globals | 46 | import autopilot.globals |
53 | 48 | from autopilot.testresult import AutopilotVerboseResult | 47 | from autopilot.testresult import get_output_formats |
54 | 49 | from autopilot.utilities import DebugLogFilter, LogFormatter | 48 | from autopilot.utilities import DebugLogFilter, LogFormatter |
55 | 50 | 49 | ||
56 | 51 | 50 | ||
57 | @@ -69,29 +68,13 @@ | |||
58 | 69 | root_logger.info(get_version_string()) | 68 | root_logger.info(get_version_string()) |
59 | 70 | 69 | ||
60 | 71 | 70 | ||
65 | 72 | def construct_test_runner(args): | 71 | def construct_test_result(args): |
66 | 73 | kwargs = dict( | 72 | formats = get_output_formats() |
67 | 74 | stdout=get_output_stream(args), | 73 | return formats[args.format]( |
68 | 75 | output_format=get_output_format(args.format), | 74 | stream=get_output_stream(args), |
69 | 75 | failfast=args.failfast, | ||
70 | 76 | ) | 76 | ) |
71 | 77 | 77 | ||
72 | 78 | return ConfigurableTestRunner(**kwargs) | ||
73 | 79 | |||
74 | 80 | |||
75 | 81 | def get_output_format(format): | ||
76 | 82 | """Return a Result object for each format we support.""" | ||
77 | 83 | |||
78 | 84 | if format == "text": | ||
79 | 85 | return type('VerboseTextTestResult', (TextTestResult,), | ||
80 | 86 | dict(AutopilotVerboseResult.__dict__)) | ||
81 | 87 | |||
82 | 88 | elif format == "xml": | ||
83 | 89 | from junitxml import JUnitXmlResult | ||
84 | 90 | return type('VerboseXmlResult', (JUnitXmlResult,), | ||
85 | 91 | dict(AutopilotVerboseResult.__dict__)) | ||
86 | 92 | |||
87 | 93 | raise KeyError("Unknown format name '%s'" % format) | ||
88 | 94 | |||
89 | 95 | 78 | ||
90 | 96 | def get_output_stream(args): | 79 | def get_output_stream(args): |
91 | 97 | global _output_stream | 80 | global _output_stream |
92 | @@ -118,30 +101,6 @@ | |||
93 | 118 | return _output_stream | 101 | return _output_stream |
94 | 119 | 102 | ||
95 | 120 | 103 | ||
96 | 121 | class ConfigurableTestRunner(object): | ||
97 | 122 | """A configurable test runner class. | ||
98 | 123 | |||
99 | 124 | This class alows us to configure the output format and whether of not we | ||
100 | 125 | collect coverage information for the test run. | ||
101 | 126 | |||
102 | 127 | """ | ||
103 | 128 | |||
104 | 129 | def __init__(self, stdout, output_format): | ||
105 | 130 | self.stdout = stdout | ||
106 | 131 | self.result_class = output_format | ||
107 | 132 | |||
108 | 133 | def run(self, test, failfast=False): | ||
109 | 134 | "Run the given test case or test suite." | ||
110 | 135 | result = self.result_class(self.stdout) | ||
111 | 136 | result.startTestRun() | ||
112 | 137 | result.failfast = failfast | ||
113 | 138 | try: | ||
114 | 139 | test_result = test.run(result) | ||
115 | 140 | finally: | ||
116 | 141 | result.stopTestRun() | ||
117 | 142 | return test_result | ||
118 | 143 | |||
119 | 144 | |||
120 | 145 | def get_package_location(import_name): | 104 | def get_package_location(import_name): |
121 | 146 | """Get the on-disk location of a package from a test id name. | 105 | """Get the on-disk location of a package from a test id name. |
122 | 147 | 106 | ||
123 | @@ -218,6 +177,7 @@ | |||
124 | 218 | self.args = parse_arguments() | 177 | self.args = parse_arguments() |
125 | 219 | 178 | ||
126 | 220 | def run(self): | 179 | def run(self): |
127 | 180 | setup_logging(self.args.verbose) | ||
128 | 221 | if self.args.mode == 'list': | 181 | if self.args.mode == 'list': |
129 | 222 | self.list_tests() | 182 | self.list_tests() |
130 | 223 | elif self.args.mode == 'run': | 183 | elif self.args.mode == 'run': |
131 | @@ -228,7 +188,6 @@ | |||
132 | 228 | self.launch_app() | 188 | self.launch_app() |
133 | 229 | 189 | ||
134 | 230 | def run_vis(self): | 190 | def run_vis(self): |
135 | 231 | setup_logging(self.args.verbose) | ||
136 | 232 | # importing this requires that DISPLAY is set. Since we don't always | 191 | # importing this requires that DISPLAY is set. Since we don't always |
137 | 233 | # want that requirement, do the import here: | 192 | # want that requirement, do the import here: |
138 | 234 | from autopilot.vis import vis_main | 193 | from autopilot.vis import vis_main |
139 | @@ -251,7 +210,6 @@ | |||
140 | 251 | get_application_launcher_from_string_hint, | 210 | get_application_launcher_from_string_hint, |
141 | 252 | ) | 211 | ) |
142 | 253 | 212 | ||
143 | 254 | setup_logging(self.args.verbose) | ||
144 | 255 | app_name = self.args.application[0] | 213 | app_name = self.args.application[0] |
145 | 256 | if not os.path.isabs(app_name) or not os.path.exists(app_name): | 214 | if not os.path.isabs(app_name) or not os.path.exists(app_name): |
146 | 257 | try: | 215 | try: |
147 | @@ -325,9 +283,13 @@ | |||
148 | 325 | if self.args.verbose: | 283 | if self.args.verbose: |
149 | 326 | autopilot.globals.set_log_verbose(True) | 284 | autopilot.globals.set_log_verbose(True) |
150 | 327 | 285 | ||
154 | 328 | setup_logging(self.args.verbose) | 286 | result = construct_test_result(self.args) |
155 | 329 | runner = construct_test_runner(self.args) | 287 | result.startTestRun() |
156 | 330 | test_result = runner.run(test_suite, self.args.failfast) | 288 | try: |
157 | 289 | test_result = test_suite.run(result) | ||
158 | 290 | finally: | ||
159 | 291 | result.stopTestRun() | ||
160 | 292 | |||
161 | 331 | if not test_result.wasSuccessful(): | 293 | if not test_result.wasSuccessful(): |
162 | 332 | exit(1) | 294 | exit(1) |
163 | 333 | 295 | ||
164 | 334 | 296 | ||
165 | === modified file 'autopilot/testresult.py' | |||
166 | --- autopilot/testresult.py 2013-09-16 17:26:27 +0000 | |||
167 | +++ autopilot/testresult.py 2013-12-02 02:12:42 +0000 | |||
168 | @@ -25,11 +25,17 @@ | |||
169 | 25 | import logging | 25 | import logging |
170 | 26 | 26 | ||
171 | 27 | from autopilot.globals import get_log_verbose | 27 | from autopilot.globals import get_log_verbose |
177 | 28 | 28 | from testtools import ( | |
178 | 29 | 29 | ExtendedToOriginalDecorator, | |
179 | 30 | class AutopilotVerboseResult(object): | 30 | ExtendedToStreamDecorator, |
180 | 31 | """A result class that logs failures, errors and success via the python | 31 | TestResultDecorator, |
181 | 32 | logging framework.""" | 32 | TextTestResult, |
182 | 33 | try_import, | ||
183 | 34 | ) | ||
184 | 35 | |||
185 | 36 | |||
186 | 37 | class LoggedTestResultDecorator(TestResultDecorator): | ||
187 | 38 | """A decorator that logs messages to python's logging system.""" | ||
188 | 33 | 39 | ||
189 | 34 | def _log(self, level, message): | 40 | def _log(self, level, message): |
190 | 35 | """Performs the actual message logging""" | 41 | """Performs the actual message logging""" |
191 | @@ -46,40 +52,61 @@ | |||
192 | 46 | self._log(level, text) | 52 | self._log(level, text) |
193 | 47 | 53 | ||
194 | 48 | def addSuccess(self, test, details=None): | 54 | def addSuccess(self, test, details=None): |
195 | 49 | """Called for a successful test""" | ||
196 | 50 | # Allow for different calling syntax used by the base class. | ||
197 | 51 | if details is None: | ||
198 | 52 | super(type(self), self).addSuccess(test) | ||
199 | 53 | else: | ||
200 | 54 | super(type(self), self).addSuccess(test, details) | ||
201 | 55 | self._log(logging.INFO, "OK: %s" % (test.id())) | 55 | self._log(logging.INFO, "OK: %s" % (test.id())) |
202 | 56 | return super(LoggedTestResultDecorator, self).addSuccess(test, details) | ||
203 | 56 | 57 | ||
204 | 57 | def addError(self, test, err=None, details=None): | 58 | def addError(self, test, err=None, details=None): |
205 | 58 | """Called for a test which failed with an error""" | ||
206 | 59 | # Allow for different calling syntax used by the base class. | ||
207 | 60 | # The xml path only uses 'err'. Use of 'err' can be | ||
208 | 61 | # forced by raising TypeError when it is not specified. | ||
209 | 62 | if err is None: | ||
210 | 63 | raise TypeError | ||
211 | 64 | if details is None: | ||
212 | 65 | super(type(self), self).addError(test, err) | ||
213 | 66 | else: | ||
214 | 67 | super(type(self), self).addError(test, err, details) | ||
215 | 68 | self._log(logging.ERROR, "ERROR: %s" % (test.id())) | 59 | self._log(logging.ERROR, "ERROR: %s" % (test.id())) |
216 | 69 | if hasattr(test, "getDetails"): | 60 | if hasattr(test, "getDetails"): |
217 | 70 | self._log_details(logging.ERROR, test.getDetails()) | 61 | self._log_details(logging.ERROR, test.getDetails()) |
218 | 62 | return super(type(self), self).addError(test, err, details) | ||
219 | 71 | 63 | ||
220 | 72 | def addFailure(self, test, err=None, details=None): | 64 | def addFailure(self, test, err=None, details=None): |
221 | 73 | """Called for a test which failed an assert""" | 65 | """Called for a test which failed an assert""" |
222 | 74 | # Allow for different calling syntax used by the base class. | ||
223 | 75 | # The xml path only uses 'err' or 'details'. Use of 'err' can be | ||
224 | 76 | # forced by raising TypeError when it is not specified. | ||
225 | 77 | if err is None: | ||
226 | 78 | raise TypeError | ||
227 | 79 | if details is None: | ||
228 | 80 | super(type(self), self).addFailure(test, err) | ||
229 | 81 | else: | ||
230 | 82 | super(type(self), self).addFailure(test, err, details) | ||
231 | 83 | self._log(logging.ERROR, "FAIL: %s" % (test.id())) | 66 | self._log(logging.ERROR, "FAIL: %s" % (test.id())) |
232 | 84 | if hasattr(test, "getDetails"): | 67 | if hasattr(test, "getDetails"): |
233 | 85 | self._log_details(logging.ERROR, test.getDetails()) | 68 | self._log_details(logging.ERROR, test.getDetails()) |
234 | 69 | return super(type(self), self).addFailure(test, err, details) | ||
235 | 70 | |||
236 | 71 | |||
237 | 72 | def get_output_formats(): | ||
238 | 73 | """Get information regarding the different output formats supported.""" | ||
239 | 74 | supported_formats = {} | ||
240 | 75 | |||
241 | 76 | supported_formats['text'] = _construct_text | ||
242 | 77 | |||
243 | 78 | if try_import('junitxml'): | ||
244 | 79 | supported_formats['xml'] = _construct_xml | ||
245 | 80 | if try_import('subunit'): | ||
246 | 81 | supported_formats['subunit'] = _construct_subunit | ||
247 | 82 | return supported_formats | ||
248 | 83 | |||
249 | 84 | |||
250 | 85 | def get_default_format(): | ||
251 | 86 | return 'text' | ||
252 | 87 | |||
253 | 88 | |||
254 | 89 | def _construct_xml(**kwargs): | ||
255 | 90 | from junitxml import JUnitXmlResult | ||
256 | 91 | stream = kwargs.pop('stream') | ||
257 | 92 | return LoggedTestResultDecorator( | ||
258 | 93 | ExtendedToOriginalDecorator( | ||
259 | 94 | JUnitXmlResult(stream) | ||
260 | 95 | ) | ||
261 | 96 | ) | ||
262 | 97 | |||
263 | 98 | |||
264 | 99 | def _construct_text(**kwargs): | ||
265 | 100 | stream = kwargs.pop('stream') | ||
266 | 101 | failfast = kwargs.pop('failfast') | ||
267 | 102 | return LoggedTestResultDecorator(TextTestResult(stream, failfast)) | ||
268 | 103 | |||
269 | 104 | |||
270 | 105 | def _construct_subunit(**kwargs): | ||
271 | 106 | from subunit import StreamResultToBytes | ||
272 | 107 | stream = kwargs.pop('stream') | ||
273 | 108 | return LoggedTestResultDecorator( | ||
274 | 109 | ExtendedToStreamDecorator( | ||
275 | 110 | StreamResultToBytes(stream) | ||
276 | 111 | ) | ||
277 | 112 | ) | ||
278 | 86 | 113 | ||
279 | === added file 'autopilot/tests/unit/test_testresults.py' | |||
280 | --- autopilot/tests/unit/test_testresults.py 1970-01-01 00:00:00 +0000 | |||
281 | +++ autopilot/tests/unit/test_testresults.py 2013-12-02 02:12:42 +0000 | |||
282 | @@ -0,0 +1,92 @@ | |||
283 | 1 | # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- | ||
284 | 2 | # | ||
285 | 3 | # Autopilot Functional Test Tool | ||
286 | 4 | # Copyright (C) 2013 Canonical | ||
287 | 5 | # | ||
288 | 6 | # This program is free software: you can redistribute it and/or modify | ||
289 | 7 | # it under the terms of the GNU General Public License as published by | ||
290 | 8 | # the Free Software Foundation, either version 3 of the License, or | ||
291 | 9 | # (at your option) any later version. | ||
292 | 10 | # | ||
293 | 11 | # This program is distributed in the hope that it will be useful, | ||
294 | 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
295 | 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
296 | 14 | # GNU General Public License for more details. | ||
297 | 15 | # | ||
298 | 16 | # You should have received a copy of the GNU General Public License | ||
299 | 17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
300 | 18 | # | ||
301 | 19 | |||
302 | 20 | from mock import Mock, patch | ||
303 | 21 | from testtools import TestCase, PlaceHolder | ||
304 | 22 | from testtools.content import text_content | ||
305 | 23 | |||
306 | 24 | from autopilot.testresult import ( | ||
307 | 25 | get_default_format, | ||
308 | 26 | get_output_formats, | ||
309 | 27 | LoggedTestResultDecorator, | ||
310 | 28 | ) | ||
311 | 29 | |||
312 | 30 | |||
313 | 31 | class LoggedTestResultDecoratorTests(TestCase): | ||
314 | 32 | |||
315 | 33 | def construct_simple_content_object(self): | ||
316 | 34 | return text_content(self.getUniqueString) | ||
317 | 35 | |||
318 | 36 | def test_can_construct(self): | ||
319 | 37 | LoggedTestResultDecorator(Mock()) | ||
320 | 38 | |||
321 | 39 | def test_addSuccess_calls_decorated_test(self): | ||
322 | 40 | wrapped = Mock() | ||
323 | 41 | result = LoggedTestResultDecorator(wrapped) | ||
324 | 42 | fake_test = PlaceHolder('fake_test') | ||
325 | 43 | fake_details = self.construct_simple_content_object() | ||
326 | 44 | |||
327 | 45 | result.addSuccess(fake_test, fake_details) | ||
328 | 46 | |||
329 | 47 | wrapped.addSuccess.assert_called_once_with( | ||
330 | 48 | fake_test, | ||
331 | 49 | details=fake_details | ||
332 | 50 | ) | ||
333 | 51 | |||
334 | 52 | def test_addError_calls_decorated_test(self): | ||
335 | 53 | wrapped = Mock() | ||
336 | 54 | result = LoggedTestResultDecorator(wrapped) | ||
337 | 55 | fake_test = PlaceHolder('fake_test') | ||
338 | 56 | fake_error = object() | ||
339 | 57 | fake_details = self.construct_simple_content_object() | ||
340 | 58 | |||
341 | 59 | result.addError(fake_test, fake_error, fake_details) | ||
342 | 60 | |||
343 | 61 | wrapped.addError.assert_called_once_with( | ||
344 | 62 | fake_test, | ||
345 | 63 | fake_error, | ||
346 | 64 | details=fake_details | ||
347 | 65 | ) | ||
348 | 66 | |||
349 | 67 | def test_addFailure_calls_decorated_test(self): | ||
350 | 68 | wrapped = Mock() | ||
351 | 69 | result = LoggedTestResultDecorator(wrapped) | ||
352 | 70 | fake_test = PlaceHolder('fake_test') | ||
353 | 71 | fake_error = object() | ||
354 | 72 | fake_details = self.construct_simple_content_object() | ||
355 | 73 | |||
356 | 74 | result.addFailure(fake_test, fake_error, fake_details) | ||
357 | 75 | |||
358 | 76 | wrapped.addFailure.assert_called_once_with( | ||
359 | 77 | fake_test, | ||
360 | 78 | fake_error, | ||
361 | 79 | details=fake_details | ||
362 | 80 | ) | ||
363 | 81 | |||
364 | 82 | |||
365 | 83 | class OutputFormatFactoryTests(TestCase): | ||
366 | 84 | |||
367 | 85 | def test_has_text_format(self): | ||
368 | 86 | self.assertTrue('text' in get_output_formats()) | ||
369 | 87 | |||
370 | 88 | def test_has_xml_format(self): | ||
371 | 89 | self.assertTrue('xml' in get_output_formats()) | ||
372 | 90 | |||
373 | 91 | def test_default_format_is_available(self): | ||
374 | 92 | self.assertTrue(get_default_format() in get_output_formats()) | ||
375 | 0 | 93 | ||
376 | === modified file 'debian/control' | |||
377 | --- debian/control 2013-11-27 20:15:34 +0000 | |||
378 | +++ debian/control 2013-12-02 02:12:42 +0000 | |||
379 | @@ -16,23 +16,25 @@ | |||
380 | 16 | python-debian, | 16 | python-debian, |
381 | 17 | python-dev, | 17 | python-dev, |
382 | 18 | python-gi, | 18 | python-gi, |
383 | 19 | python-junitxml, | ||
384 | 19 | python-mock, | 20 | python-mock, |
385 | 20 | python-psutil, | 21 | python-psutil, |
386 | 21 | python-setuptools, | 22 | python-setuptools, |
387 | 22 | python-subunit, | ||
388 | 23 | python-six, | 23 | python-six, |
389 | 24 | python-sphinx, | 24 | python-sphinx, |
390 | 25 | python-subunit, | ||
391 | 25 | python-testscenarios, | 26 | python-testscenarios, |
392 | 26 | python-testtools, | 27 | python-testtools, |
393 | 27 | python-xlib, | 28 | python-xlib, |
394 | 28 | python3-all-dev (>= 3.3), | 29 | python3-all-dev (>= 3.3), |
395 | 29 | python3-dbus, | 30 | python3-dbus, |
396 | 30 | python3-gi, | 31 | python3-gi, |
397 | 32 | python3-junitxml, | ||
398 | 31 | python3-mock, | 33 | python3-mock, |
399 | 32 | python3-psutil, | 34 | python3-psutil, |
400 | 33 | python3-setuptools, | 35 | python3-setuptools, |
401 | 36 | python3-six, | ||
402 | 34 | python3-subunit, | 37 | python3-subunit, |
403 | 35 | python3-six, | ||
404 | 36 | python3-testscenarios, | 38 | python3-testscenarios, |
405 | 37 | python3-testtools, | 39 | python3-testtools, |
406 | 38 | python3-xlib, | 40 | python3-xlib, |
FAILED: Continuous integration, rev:380 jenkins. qa.ubuntu. com/job/ autopilot- ci/320/ jenkins. qa.ubuntu. com/job/ autopilot- trusty- amd64-ci/ 46/console jenkins. qa.ubuntu. com/job/ autopilot- trusty- armhf-ci/ 46/console jenkins. qa.ubuntu. com/job/ autopilot- trusty- i386-ci/ 46/console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/autopilot- ci/320/ rebuild
http://