Merge lp:~barry/autopilot/lp1488175 into lp:autopilot
- lp1488175
- Merge into trunk
Status: | Needs review | ||||
---|---|---|---|---|---|
Proposed branch: | lp:~barry/autopilot/lp1488175 | ||||
Merge into: | lp:autopilot | ||||
Diff against target: |
912 lines (+242/-119) (has conflicts) 23 files modified
autopilot/introspection/_xpathselect.py (+4/-7) autopilot/process/_bamf.py (+4/-2) autopilot/run.py (+8/-8) autopilot/testcase.py (+2/-2) autopilot/tests/functional/test_ap_apps.py (+2/-1) autopilot/tests/functional/test_autopilot_functional.py (+4/-2) autopilot/tests/functional/test_dbus_query.py (+8/-10) autopilot/tests/functional/test_input_stack.py (+9/-1) autopilot/tests/unit/test_application_launcher.py (+11/-10) autopilot/tests/unit/test_input.py (+4/-1) autopilot/tests/unit/test_introspection_dbus.py (+4/-1) autopilot/tests/unit/test_introspection_xpathselect.py (+33/-19) autopilot/tests/unit/test_pick_backend.py (+20/-17) autopilot/tests/unit/test_platform.py (+2/-1) autopilot/tests/unit/test_process.py (+2/-2) autopilot/tests/unit/test_stagnate_state.py (+6/-3) autopilot/tests/unit/test_test_loader.py (+4/-3) autopilot/tests/unit/test_testcase.py (+5/-4) autopilot/tests/unit/test_types.py (+17/-8) autopilot/vis/dbus_search.py (+10/-6) debian/changelog (+73/-5) docs/otto.py (+1/-4) setup.py (+9/-2) Text conflict in autopilot/tests/functional/test_input_stack.py Text conflict in setup.py |
||||
To merge this branch: | bzr merge lp:~barry/autopilot/lp1488175 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot | continuous-integration | Needs Fixing | |
Autopilot Hackers | Pending | ||
Review via email: mp+268967@code.launchpad.net |
Commit message
Description of the change
My take on fixing the flake8 failures.
PS Jenkins bot (ps-jenkins) wrote : | # |
Max Brustkern (nuclearbob) wrote : | # |
I have a couple of questions, but other than the merge conflicts, most of it looks great.
Barry Warsaw (barry) wrote : | # |
On Aug 27, 2015, at 08:22 PM, Max Brustkern wrote:
>> @@ -39,8 +40,7 @@
>> """Must return a backend when called with a single backend."""
>> class Backend(object):
>> pass
>> - _create_backend = lambda: Backend()
>> - backend = _pick_backend(
>> + backend = _pick_backend(
>
>I'm not quite a clever in python as everyone else here, so I just want to
>make sure that Backend is wanted here and not Backend() This seems to apply
>to most of the tests around this, so I imagine they'd be breaking if it were
>incorrect, I just like to be sure it's intentional.
It is!
lambdas are defined here:
https:/
The example given provides a great way to think about them.
lambda args: expression
is equivalent to
def <lambda>(args):
return expression
where the function is anonymous, i.e. it performs no name binding in the
current namespace. When you assign the `lambda: Backend()` to
_create_backend, it's the assignment that does the name binding to the
function object, so its exactly equivalent to:
def _create_backend():
return Backend()
Note too that when _pick_backend() is called, the code is passing in a
dictionary that maps the `foo` key to the *function object* created by the
lambda; specifically, the calling of the lambda-created function object is
deferred. It's exactly equivalent to just setting `foo` to the Backend
function object without calling it.
So in this case, the lambda is really just creating an unnecessary extra level
of call.
That's not quite the case where some of the lambdas take an argument, but you
can see how the translation to explicit function definitions should generally
go.
One thing to keep in mind about my translations. To be accurate, the
lambda-to-def conversion should return the value of the last expression, but I
don't do this in all cases because the tests appear to only care that an
exception is raised. Thus, the return values are mostly ignored. But for
correctness, you could return the values out of the locally defined
functions.
BTW, this equivalence is the reason why lambda are generally frowned upon.
They're almost never needed, and the savings in lines of code are usually not
worth the cost in readability or comprehension. It was probably the fans of
functional programming that tipped the balance against just removing them from
Python 3. ;)
>> === modified file 'setup.py'
>> --- setup.py 2015-08-19 00:25:00 +0000
>> +++ setup.py 2015-08-24 19:50:50 +0000
>> @@ -20,9 +20,16 @@
>> from setuptools import find_packages, setup, Extension
>>
>> import sys
>> +
>> +from setuptools import find_packages, setup, Extension
>
>It surprises me that these are needed and haven't been included
>previously. Was there something causing static analysis to not pick this up
>before?
Oh, that's weird. I don't remember adding that. Maybe it's a merge snafu?
P.S. you can play with this to see the equivalence in action:
def runme(func):
print(func())
def hey():
return 'hey'
yo = lambda: hey()
def sup():
return hey()
run...
Max Brustkern (nuclearbob) wrote : | # |
> On Aug 27, 2015, at 08:22 PM, Max Brustkern wrote:
>
> >> @@ -39,8 +40,7 @@
> >> """Must return a backend when called with a single backend."""
> >> class Backend(object):
> >> pass
> >> - _create_backend = lambda: Backend()
> >> - backend = _pick_backend(
> >> + backend = _pick_backend(
> >
> >I'm not quite a clever in python as everyone else here, so I just want to
> >make sure that Backend is wanted here and not Backend() This seems to apply
> >to most of the tests around this, so I imagine they'd be breaking if it were
> >incorrect, I just like to be sure it's intentional.
>
> It is!
>
> lambdas are defined here:
>
> https:/
>
> The example given provides a great way to think about them.
>
> lambda args: expression
>
> is equivalent to
>
> def <lambda>(args):
> return expression
>
> where the function is anonymous, i.e. it performs no name binding in the
> current namespace. When you assign the `lambda: Backend()` to
> _create_backend, it's the assignment that does the name binding to the
> function object, so its exactly equivalent to:
>
> def _create_backend():
> return Backend()
>
> Note too that when _pick_backend() is called, the code is passing in a
> dictionary that maps the `foo` key to the *function object* created by the
> lambda; specifically, the calling of the lambda-created function object is
> deferred. It's exactly equivalent to just setting `foo` to the Backend
> function object without calling it.
Yeah, that's what was confusing me. I saw:
_create_backend = lambda: Backend()
and assumed that would return Backend() (the return value of that, i.e. an instance of the class) and not Backend (the class itself) The rest of the lambda syntax made sense to me, I just tripped over some parentheses here, since we're setting foo=Backend and not foo=Backend() like I would have expected from the lambda. Exciting stuff!
Unmerged revisions
- 519. By Barry Warsaw
-
A few more flake8 fixes.
- 518. By Barry Warsaw
-
Mass fix for LP: #1488175
Preview Diff
1 | === modified file 'autopilot/introspection/_xpathselect.py' |
2 | --- autopilot/introspection/_xpathselect.py 2014-05-23 13:26:00 +0000 |
3 | +++ autopilot/introspection/_xpathselect.py 2015-08-24 19:50:50 +0000 |
4 | @@ -102,10 +102,7 @@ |
5 | raise TypeError( |
6 | "'operation' parameter must be bytes, not '%s'" |
7 | % type(operation).__name__) |
8 | - if ( |
9 | - parent |
10 | - and parent.needs_client_side_filtering() |
11 | - ): |
12 | + if parent and parent.needs_client_side_filtering(): |
13 | raise InvalidXPathQuery( |
14 | "Cannot create a new query from a parent that requires " |
15 | "client-side filter processing." |
16 | @@ -137,9 +134,9 @@ |
17 | k: v for k, v in filters.items() if k not in self._server_filters |
18 | } |
19 | if ( |
20 | - operation == Query.Operation.DESCENDANT |
21 | - and query == Query.WILDCARD |
22 | - and not self._server_filters |
23 | + operation == Query.Operation.DESCENDANT and |
24 | + query == Query.WILDCARD and |
25 | + not self._server_filters |
26 | ): |
27 | raise InvalidXPathQuery( |
28 | "Must provide at least one server-side filter when searching " |
29 | |
30 | === modified file 'autopilot/process/_bamf.py' |
31 | --- autopilot/process/_bamf.py 2014-07-23 03:37:24 +0000 |
32 | +++ autopilot/process/_bamf.py 2015-08-24 19:50:50 +0000 |
33 | @@ -167,8 +167,10 @@ |
34 | try: |
35 | new_windows = [] |
36 | [new_windows.extend(a.get_windows()) for a in apps] |
37 | - filter_fn = lambda w: w.x_id not in [ |
38 | - c.x_id for c in existing_windows] |
39 | + |
40 | + def filter_fn(w): |
41 | + return w.x_id not in [c.x_id for c in existing_windows] |
42 | + |
43 | new_wins = list(filter(filter_fn, new_windows)) |
44 | if new_wins: |
45 | assert len(new_wins) == 1 |
46 | |
47 | === modified file 'autopilot/run.py' |
48 | --- autopilot/run.py 2015-08-19 00:25:00 +0000 |
49 | +++ autopilot/run.py 2015-08-24 19:50:50 +0000 |
50 | @@ -355,8 +355,8 @@ |
51 | |
52 | def _is_testing_autopilot_module(test_names): |
53 | return ( |
54 | - os.path.basename(sys.argv[0]) == 'autopilot' |
55 | - and any(t.startswith('autopilot') for t in test_names) |
56 | + os.path.basename(sys.argv[0]) == 'autopilot' and |
57 | + any(t.startswith('autopilot') for t in test_names) |
58 | ) |
59 | |
60 | |
61 | @@ -712,23 +712,23 @@ |
62 | ) |
63 | |
64 | if self.args.run_order: |
65 | - test_list_fn = lambda: iterate_tests(test_suite) |
66 | + test_list = list(iterate_tests(test_suite)) |
67 | else: |
68 | - test_list_fn = lambda: sorted(iterate_tests(test_suite), key=id) |
69 | + test_list = list(sorted(iterate_tests(test_suite), key=id)) |
70 | |
71 | # only show test suites, not test cases. TODO: Check if this is still |
72 | # a requirement. |
73 | if self.args.suites: |
74 | suite_names = ["%s.%s" % (t.__module__, t.__class__.__name__) |
75 | - for t in test_list_fn()] |
76 | + for t in test_list] |
77 | unique_suite_names = list(OrderedDict.fromkeys(suite_names).keys()) |
78 | num_tests = len(unique_suite_names) |
79 | total_title = "suites" |
80 | print(" %s" % ("\n ".join(unique_suite_names))) |
81 | else: |
82 | - for test in test_list_fn(): |
83 | - has_scenarios = (hasattr(test, "scenarios") |
84 | - and type(test.scenarios) is list) |
85 | + for test in test_list: |
86 | + has_scenarios = (hasattr(test, "scenarios") and |
87 | + type(test.scenarios) is list) |
88 | if has_scenarios: |
89 | num_tests += len(test.scenarios) |
90 | print(" *%d %s" % (len(test.scenarios), test.id())) |
91 | |
92 | === modified file 'autopilot/testcase.py' |
93 | --- autopilot/testcase.py 2015-08-19 03:34:47 +0000 |
94 | +++ autopilot/testcase.py 2015-08-24 19:50:50 +0000 |
95 | @@ -547,6 +547,6 @@ |
96 | |
97 | def _considered_failing_test(failure_class_type): |
98 | return ( |
99 | - not issubclass(failure_class_type, SkipTest) |
100 | - and not issubclass(failure_class_type, _ExpectedFailure) |
101 | + not issubclass(failure_class_type, SkipTest) and |
102 | + not issubclass(failure_class_type, _ExpectedFailure) |
103 | ) |
104 | |
105 | === modified file 'autopilot/tests/functional/test_ap_apps.py' |
106 | --- autopilot/tests/functional/test_ap_apps.py 2015-01-26 01:59:18 +0000 |
107 | +++ autopilot/tests/functional/test_ap_apps.py 2015-08-24 19:50:50 +0000 |
108 | @@ -31,6 +31,7 @@ |
109 | Raises, |
110 | raises, |
111 | ) |
112 | +from functools import partial |
113 | from textwrap import dedent |
114 | |
115 | from fixtures import EnvironmentVariable |
116 | @@ -348,7 +349,7 @@ |
117 | time.sleep(1) |
118 | os.abort() |
119 | """)) |
120 | - launch_fn = lambda: self.launch_test_application(path, app_type='qt') |
121 | + launch_fn = partial(self.launch_test_application, path, app_type='qt') |
122 | self.assertThat(launch_fn, raises(ProcessSearchError)) |
123 | |
124 | @skipIf(model() != "Desktop", "Only suitable on Desktop (Qt4)") |
125 | |
126 | === modified file 'autopilot/tests/functional/test_autopilot_functional.py' |
127 | --- autopilot/tests/functional/test_autopilot_functional.py 2015-05-01 02:29:02 +0000 |
128 | +++ autopilot/tests/functional/test_autopilot_functional.py 2015-08-24 19:50:50 +0000 |
129 | @@ -528,8 +528,10 @@ |
130 | with TempDir() as tmp_dir_fixture: |
131 | dir_pattern = os.path.join(tmp_dir_fixture.path, 'rMD-session*') |
132 | original_session_dirs = set(glob.glob(dir_pattern)) |
133 | - get_new_sessions = lambda: \ |
134 | - set(glob.glob(dir_pattern)) - original_session_dirs |
135 | + |
136 | + def get_new_sessions(): |
137 | + return set(glob.glob(dir_pattern)) - original_session_dirs |
138 | + |
139 | mock_test_case = Mock() |
140 | mock_test_case.shortDescription.return_value = "Dummy_Description" |
141 | logger = RMDVideoLogFixture(tmp_dir_fixture.path, mock_test_case) |
142 | |
143 | === modified file 'autopilot/tests/functional/test_dbus_query.py' |
144 | --- autopilot/tests/functional/test_dbus_query.py 2015-06-09 15:35:08 +0000 |
145 | +++ autopilot/tests/functional/test_dbus_query.py 2015-08-24 19:50:50 +0000 |
146 | @@ -22,6 +22,7 @@ |
147 | import os |
148 | import subprocess |
149 | import signal |
150 | +from functools import partial |
151 | from timeit import default_timer |
152 | from tempfile import mktemp |
153 | from testtools import skipIf |
154 | @@ -140,12 +141,11 @@ |
155 | |
156 | def test_select_single_no_name_no_parameter_raises_exception(self): |
157 | app = self.start_fully_featured_app() |
158 | - fn = lambda: app.select_single() |
159 | - self.assertThat(fn, raises(ValueError)) |
160 | + self.assertThat(app.select_single, raises(ValueError)) |
161 | |
162 | def test_select_single_no_match_raises_exception(self): |
163 | app = self.start_fully_featured_app() |
164 | - match_fn = lambda: app.select_single("QMadeupType") |
165 | + match_fn = partial(app.select_single, "QMadeupType") |
166 | self.assertThat(match_fn, raises(StateNotFoundError('QMadeupType'))) |
167 | |
168 | def test_exception_raised_when_operating_on_dead_app(self): |
169 | @@ -174,7 +174,7 @@ |
170 | |
171 | def test_select_single_parameters_no_match_raises_exception(self): |
172 | app = self.start_fully_featured_app() |
173 | - match_fn = lambda: app.select_single(title="Non-existant object") |
174 | + match_fn = partial(app.select_single, title="Non-existant object") |
175 | self.assertThat( |
176 | match_fn, |
177 | raises(StateNotFoundError('*', title="Non-existant object")) |
178 | @@ -182,13 +182,12 @@ |
179 | |
180 | def test_select_single_returning_multiple_raises(self): |
181 | app = self.start_fully_featured_app() |
182 | - fn = lambda: app.select_single('QMenu') |
183 | + fn = partial(app.select_single, 'QMenu') |
184 | self.assertThat(fn, raises(ValueError)) |
185 | |
186 | def test_select_many_no_name_no_parameter_raises_exception(self): |
187 | app = self.start_fully_featured_app() |
188 | - fn = lambda: app.select_single() |
189 | - self.assertThat(fn, raises(ValueError)) |
190 | + self.assertThat(app.select_single, raises(ValueError)) |
191 | |
192 | def test_select_many_only_using_parameters(self): |
193 | app = self.start_fully_featured_app() |
194 | @@ -211,7 +210,7 @@ |
195 | def test_wait_select_single_fails_slowly(self): |
196 | app = self.start_fully_featured_app() |
197 | start_time = default_timer() |
198 | - fn = lambda: app.wait_select_single('QMadeupType') |
199 | + fn = partial(app.wait_select_single, 'QMadeupType') |
200 | self.assertThat(fn, raises(StateNotFoundError('QMadeupType'))) |
201 | end_time = default_timer() |
202 | self.assertThat(abs(end_time - start_time), GreaterThan(9)) |
203 | @@ -234,8 +233,7 @@ |
204 | dbus_pid = int(results[1].split("=")[1]) |
205 | dbus_address = results[0].split("=", 1)[1] |
206 | |
207 | - kill_dbus = lambda pid: os.killpg(pid, signal.SIGTERM) |
208 | - self.addCleanup(kill_dbus, dbus_pid) |
209 | + self.addCleanup(os.killpg, dbus_pid, signal.SIGTERM) |
210 | |
211 | return dbus_address |
212 | |
213 | |
214 | === modified file 'autopilot/tests/functional/test_input_stack.py' |
215 | --- autopilot/tests/functional/test_input_stack.py 2015-08-19 00:25:00 +0000 |
216 | +++ autopilot/tests/functional/test_input_stack.py 2015-08-24 19:50:50 +0000 |
217 | @@ -190,11 +190,17 @@ |
218 | from autopilot.input import _uinput |
219 | return _uinput.Keyboard._device._pressed_keys_ecodes |
220 | else: |
221 | +<<<<<<< TREE |
222 | self.fail( |
223 | "Don't know how to get pressed keys list for {}".format( |
224 | self.backend |
225 | ) |
226 | ) |
227 | +======= |
228 | + self.fail("Don't know how to get pressed keys list for backend " + |
229 | + self.backend |
230 | + ) |
231 | +>>>>>>> MERGE-SOURCE |
232 | |
233 | |
234 | @skipIf(platform.model() != "Desktop", "Only suitable on Desktop (WinMocker)") |
235 | @@ -468,7 +474,9 @@ |
236 | |
237 | app = self.start_qml_script(test_qml) |
238 | pinch_widget = app.select_single("QQuickRectangle") |
239 | - widget_bg_colour = lambda: pinch_widget.color |
240 | + |
241 | + def widget_bg_colour(): |
242 | + return pinch_widget.color |
243 | |
244 | self.assertThat(widget_bg_colour, Eventually(Equals(start_green_bg))) |
245 | |
246 | |
247 | === modified file 'autopilot/tests/unit/test_application_launcher.py' |
248 | --- autopilot/tests/unit/test_application_launcher.py 2014-07-22 02:30:19 +0000 |
249 | +++ autopilot/tests/unit/test_application_launcher.py 2015-08-24 19:50:50 +0000 |
250 | @@ -586,9 +586,9 @@ |
251 | ) |
252 | |
253 | def test_check_error_raises_RuntimeError_on_timeout(self): |
254 | - fn = lambda: UpstartApplicationLauncher._check_status_error( |
255 | - UpstartApplicationLauncher.Timeout |
256 | - ) |
257 | + def fn(): |
258 | + UpstartApplicationLauncher._check_status_error( |
259 | + UpstartApplicationLauncher.Timeout) |
260 | self.assertThat( |
261 | fn, |
262 | raises( |
263 | @@ -599,9 +599,9 @@ |
264 | ) |
265 | |
266 | def test_check_error_raises_RuntimeError_on_failure(self): |
267 | - fn = lambda: UpstartApplicationLauncher._check_status_error( |
268 | - UpstartApplicationLauncher.Failed |
269 | - ) |
270 | + def fn(): |
271 | + UpstartApplicationLauncher._check_status_error( |
272 | + UpstartApplicationLauncher.Failed) |
273 | self.assertThat( |
274 | fn, |
275 | raises( |
276 | @@ -612,10 +612,11 @@ |
277 | ) |
278 | |
279 | def test_check_error_raises_RuntimeError_with_extra_message(self): |
280 | - fn = lambda: UpstartApplicationLauncher._check_status_error( |
281 | - UpstartApplicationLauncher.Failed, |
282 | - "extra message" |
283 | - ) |
284 | + def fn(): |
285 | + UpstartApplicationLauncher._check_status_error( |
286 | + UpstartApplicationLauncher.Failed, |
287 | + "extra message" |
288 | + ) |
289 | self.assertThat( |
290 | fn, |
291 | raises( |
292 | |
293 | === modified file 'autopilot/tests/unit/test_input.py' |
294 | --- autopilot/tests/unit/test_input.py 2015-07-07 22:50:24 +0000 |
295 | +++ autopilot/tests/unit/test_input.py 2015-08-24 19:50:50 +0000 |
296 | @@ -57,7 +57,10 @@ |
297 | |
298 | def test_get_center_point_raises_ValueError_on_empty_object(self): |
299 | obj = make_fake_object() |
300 | - fn = lambda: get_center_point(obj) |
301 | + |
302 | + def fn(): |
303 | + get_center_point(obj) |
304 | + |
305 | expected_exception = ValueError( |
306 | "Object '%r' does not have any recognised position attributes" % |
307 | obj) |
308 | |
309 | === modified file 'autopilot/tests/unit/test_introspection_dbus.py' |
310 | --- autopilot/tests/unit/test_introspection_dbus.py 2015-05-20 20:13:30 +0000 |
311 | +++ autopilot/tests/unit/test_introspection_dbus.py 2015-08-24 19:50:50 +0000 |
312 | @@ -185,7 +185,10 @@ |
313 | |
314 | with patch.object(fake_object, 'get_children', return_value=[child]): |
315 | out = StringIO() |
316 | - print_func = lambda: fake_object.print_tree(out) |
317 | + |
318 | + def print_func(): |
319 | + fake_object.print_tree(out) |
320 | + |
321 | self.assertThat(print_func, Not(Raises(StateNotFoundError))) |
322 | self.assertEqual(out.getvalue(), dedent("""\ |
323 | == /some/path == |
324 | |
325 | === modified file 'autopilot/tests/unit/test_introspection_xpathselect.py' |
326 | --- autopilot/tests/unit/test_introspection_xpathselect.py 2014-05-23 13:30:10 +0000 |
327 | +++ autopilot/tests/unit/test_introspection_xpathselect.py 2015-08-24 19:50:50 +0000 |
328 | @@ -28,11 +28,12 @@ |
329 | class XPathSelectQueryTests(TestCase): |
330 | |
331 | def test_query_raises_TypeError_on_non_bytes_query(self): |
332 | - fn = lambda: xpathselect.Query( |
333 | - None, |
334 | - xpathselect.Query.Operation.CHILD, |
335 | - 'asd' |
336 | - ) |
337 | + def fn(): |
338 | + xpathselect.Query( |
339 | + None, |
340 | + xpathselect.Query.Operation.CHILD, |
341 | + 'asd' |
342 | + ) |
343 | self.assertThat( |
344 | fn, |
345 | raises( |
346 | @@ -151,7 +152,10 @@ |
347 | def test_deriving_from_client_side_filtered_query_raises_ValueError(self): |
348 | q = xpathselect.Query.root("Foo") \ |
349 | .select_descendant("Baz", dict(name="\u2026")) |
350 | - fn = lambda: q.select_child("Foo") |
351 | + |
352 | + def fn(): |
353 | + q.select_child("Foo") |
354 | + |
355 | self.assertThat( |
356 | fn, |
357 | raises(InvalidXPathQuery( |
358 | @@ -161,7 +165,8 @@ |
359 | ) |
360 | |
361 | def test_init_raises_TypeError_on_invalid_operation_type(self): |
362 | - fn = lambda: xpathselect.Query(None, '/', b'sdf') |
363 | + def fn(): |
364 | + xpathselect.Query(None, '/', b'sdf') |
365 | self.assertThat( |
366 | fn, |
367 | raises(TypeError( |
368 | @@ -171,14 +176,16 @@ |
369 | ) |
370 | |
371 | def test_init_raises_ValueError_on_invalid_operation(self): |
372 | - fn = lambda: xpathselect.Query(None, b'foo', b'sdf') |
373 | + def fn(): |
374 | + xpathselect.Query(None, b'foo', b'sdf') |
375 | self.assertThat( |
376 | fn, |
377 | raises(InvalidXPathQuery("Invalid operation 'foo'.")) |
378 | ) |
379 | |
380 | def test_init_raises_ValueError_on_invalid_descendant_search(self): |
381 | - fn = lambda: xpathselect.Query(None, b'//', b'*') |
382 | + def fn(): |
383 | + xpathselect.Query(None, b'//', b'*') |
384 | self.assertThat( |
385 | fn, |
386 | raises(InvalidXPathQuery( |
387 | @@ -188,7 +195,8 @@ |
388 | ) |
389 | |
390 | def test_new_from_path_and_id_raises_TypeError_on_unicode_path(self): |
391 | - fn = lambda: xpathselect.Query.new_from_path_and_id('bad_path', 42) |
392 | + def fn(): |
393 | + xpathselect.Query.new_from_path_and_id('bad_path', 42) |
394 | self.assertThat( |
395 | fn, |
396 | raises(TypeError( |
397 | @@ -197,14 +205,16 @@ |
398 | ) |
399 | |
400 | def test_new_from_path_and_id_raises_ValueError_on_invalid_path(self): |
401 | - fn = lambda: xpathselect.Query.new_from_path_and_id(b'bad_path', 42) |
402 | + def fn(): |
403 | + xpathselect.Query.new_from_path_and_id(b'bad_path', 42) |
404 | self.assertThat( |
405 | fn, |
406 | raises(InvalidXPathQuery("Invalid path 'bad_path'.")) |
407 | ) |
408 | |
409 | def test_new_from_path_and_id_raises_ValueError_on_invalid_path2(self): |
410 | - fn = lambda: xpathselect.Query.new_from_path_and_id(b'/', 42) |
411 | + def fn(): |
412 | + xpathselect.Query.new_from_path_and_id(b'/', 42) |
413 | self.assertThat( |
414 | fn, |
415 | raises(InvalidXPathQuery("Invalid path '/'.")) |
416 | @@ -238,7 +248,8 @@ |
417 | self.assertEqual(b'/root/child[id=42]/..', q2.server_query_bytes()) |
418 | |
419 | def test_init_raises_ValueError_when_passing_filters_and_parent(self): |
420 | - fn = lambda: xpathselect.Query(None, b'/', b'..', dict(foo=123)) |
421 | + def fn(): |
422 | + xpathselect.Query(None, b'/', b'..', dict(foo=123)) |
423 | self.assertThat( |
424 | fn, |
425 | raises(InvalidXPathQuery( |
426 | @@ -247,7 +258,8 @@ |
427 | ) |
428 | |
429 | def test_init_raises_ValueError_when_passing_bad_op_and_parent(self): |
430 | - fn = lambda: xpathselect.Query(None, b'//', b'..') |
431 | + def fn(): |
432 | + xpathselect.Query(None, b'//', b'..') |
433 | self.assertThat( |
434 | fn, |
435 | raises(InvalidXPathQuery( |
436 | @@ -260,7 +272,8 @@ |
437 | self.assertEqual(b'/', q.server_query_bytes()) |
438 | |
439 | def test_cannot_select_child_on_pseudo_tree_root(self): |
440 | - fn = lambda: xpathselect.Query.pseudo_tree_root().select_child('foo') |
441 | + def fn(): |
442 | + xpathselect.Query.pseudo_tree_root().select_child('foo') |
443 | self.assertThat( |
444 | fn, |
445 | raises(InvalidXPathQuery( |
446 | @@ -311,10 +324,11 @@ |
447 | class ParameterFilterStringTests(TestWithScenarios, TestCase): |
448 | |
449 | def test_raises_ValueError_on_unknown_type(self): |
450 | - fn = lambda: xpathselect._get_filter_string_for_key_value_pair( |
451 | - 'k', |
452 | - object() |
453 | - ) |
454 | + def fn(): |
455 | + xpathselect._get_filter_string_for_key_value_pair( |
456 | + 'k', |
457 | + object() |
458 | + ) |
459 | self.assertThat( |
460 | fn, |
461 | raises( |
462 | |
463 | === modified file 'autopilot/tests/unit/test_pick_backend.py' |
464 | --- autopilot/tests/unit/test_pick_backend.py 2014-02-10 03:14:40 +0000 |
465 | +++ autopilot/tests/unit/test_pick_backend.py 2015-08-24 19:50:50 +0000 |
466 | @@ -31,7 +31,8 @@ |
467 | |
468 | def test_raises_runtime_error_on_empty_backends(self): |
469 | """Must raise a RuntimeError when we pass no backends.""" |
470 | - fn = lambda: _pick_backend({}, '') |
471 | + def fn(): |
472 | + _pick_backend({}, '') |
473 | self.assertThat( |
474 | fn, raises(RuntimeError("Unable to instantiate any backends\n"))) |
475 | |
476 | @@ -39,8 +40,7 @@ |
477 | """Must return a backend when called with a single backend.""" |
478 | class Backend(object): |
479 | pass |
480 | - _create_backend = lambda: Backend() |
481 | - backend = _pick_backend(dict(foo=_create_backend), '') |
482 | + backend = _pick_backend(dict(foo=Backend), '') |
483 | self.assertThat(backend, IsInstance(Backend)) |
484 | |
485 | def test_first_backend(self): |
486 | @@ -51,8 +51,8 @@ |
487 | class Backend2(object): |
488 | pass |
489 | backend_dict = OrderedDict() |
490 | - backend_dict['be1'] = lambda: Backend1() |
491 | - backend_dict['be2'] = lambda: Backend2() |
492 | + backend_dict['be1'] = Backend1 |
493 | + backend_dict['be2'] = Backend2 |
494 | |
495 | backend = _pick_backend(backend_dict, '') |
496 | self.assertThat(backend, IsInstance(Backend1)) |
497 | @@ -66,8 +66,8 @@ |
498 | class Backend2(object): |
499 | pass |
500 | backend_dict = OrderedDict() |
501 | - backend_dict['be1'] = lambda: Backend1() |
502 | - backend_dict['be2'] = lambda: Backend2() |
503 | + backend_dict['be1'] = Backend1 |
504 | + backend_dict['be2'] = Backend2 |
505 | |
506 | backend = _pick_backend(backend_dict, 'be2') |
507 | self.assertThat(backend, IsInstance(Backend2)) |
508 | @@ -82,18 +82,20 @@ |
509 | def __init__(self): |
510 | raise ValueError("Foo") |
511 | backend_dict = OrderedDict() |
512 | - backend_dict['be1'] = lambda: Backend1() |
513 | - backend_dict['be2'] = lambda: Backend2() |
514 | + backend_dict['be1'] = Backend1 |
515 | + backend_dict['be2'] = Backend2 |
516 | |
517 | - fn = lambda: _pick_backend(backend_dict, 'be2') |
518 | + def fn(): |
519 | + _pick_backend(backend_dict, 'be2') |
520 | self.assertThat(fn, raises(BackendException)) |
521 | |
522 | def test_raises_RuntimeError_on_invalid_preferred_backend(self): |
523 | """Must raise RuntimeError when we pass a backend that's not there""" |
524 | class Backend(object): |
525 | pass |
526 | - _create_backend = lambda: Backend() |
527 | - fn = lambda: _pick_backend(dict(foo=_create_backend), 'bar') |
528 | + |
529 | + def fn(): |
530 | + return _pick_backend(dict(foo=Backend), 'bar') |
531 | |
532 | self.assertThat( |
533 | fn, |
534 | @@ -109,8 +111,8 @@ |
535 | def __init__(self): |
536 | raise ValueError("Foo") |
537 | backend_dict = OrderedDict() |
538 | - backend_dict['be1'] = lambda: Backend1() |
539 | - backend_dict['be2'] = lambda: Backend2() |
540 | + backend_dict['be1'] = Backend1 |
541 | + backend_dict['be2'] = Backend2 |
542 | |
543 | raised = False |
544 | try: |
545 | @@ -128,10 +130,11 @@ |
546 | def __init__(self): |
547 | raise ValueError("Foo") |
548 | backend_dict = OrderedDict() |
549 | - backend_dict['be1'] = lambda: BadBackend() |
550 | - backend_dict['be2'] = lambda: BadBackend() |
551 | + backend_dict['be1'] = BadBackend |
552 | + backend_dict['be2'] = BadBackend |
553 | |
554 | - fn = lambda: _pick_backend(backend_dict, '') |
555 | + def fn(): |
556 | + return _pick_backend(backend_dict, '') |
557 | expected_exception = RuntimeError(dedent("""\ |
558 | Unable to instantiate any backends |
559 | be1: ValueError('Foo',) |
560 | |
561 | === modified file 'autopilot/tests/unit/test_platform.py' |
562 | --- autopilot/tests/unit/test_platform.py 2014-07-23 03:37:24 +0000 |
563 | +++ autopilot/tests/unit/test_platform.py 2015-08-24 19:50:50 +0000 |
564 | @@ -68,7 +68,8 @@ |
565 | class PlatformGetProcessNameTests(TestCase): |
566 | |
567 | def test_returns_callable_value(self): |
568 | - test_callable = lambda: "foo" |
569 | + def test_callable(): |
570 | + return "foo" |
571 | self.assertEqual("foo", platform._get_process_name(test_callable)) |
572 | |
573 | def test_returns_string(self): |
574 | |
575 | === modified file 'autopilot/tests/unit/test_process.py' |
576 | --- autopilot/tests/unit/test_process.py 2014-05-20 08:53:21 +0000 |
577 | +++ autopilot/tests/unit/test_process.py 2015-08-24 19:50:50 +0000 |
578 | @@ -44,8 +44,8 @@ |
579 | process.launch_uris_as_manager.called_once_with( |
580 | [], |
581 | None, |
582 | - GLib.SpawnFlags.SEARCH_PATH |
583 | - | GLib.SpawnFlags.STDOUT_TO_DEV_NULL, |
584 | + GLib.SpawnFlags.SEARCH_PATH | |
585 | + GLib.SpawnFlags.STDOUT_TO_DEV_NULL, |
586 | None, |
587 | None, |
588 | None, |
589 | |
590 | === modified file 'autopilot/tests/unit/test_stagnate_state.py' |
591 | --- autopilot/tests/unit/test_stagnate_state.py 2013-09-04 03:44:07 +0000 |
592 | +++ autopilot/tests/unit/test_stagnate_state.py 2015-08-24 19:50:50 +0000 |
593 | @@ -41,7 +41,8 @@ |
594 | x, y = (1, 1) |
595 | state_check.check_state(x, y) |
596 | |
597 | - fn = lambda: state_check.check_state(x, y) |
598 | + def fn(): |
599 | + state_check.check_state(x, y) |
600 | self.assertThat( |
601 | fn, |
602 | raises( |
603 | @@ -52,7 +53,8 @@ |
604 | ) |
605 | |
606 | def test_raises_exception_when_thresold_is_zero(self): |
607 | - fn = lambda: StagnantStateDetector(threshold=0) |
608 | + def fn(): |
609 | + StagnantStateDetector(threshold=0) |
610 | self.assertThat( |
611 | fn, |
612 | raises(ValueError("Threshold must be a positive integer.")) |
613 | @@ -64,5 +66,6 @@ |
614 | no_hash = UnHashable() |
615 | state_check = StagnantStateDetector(threshold=5) |
616 | |
617 | - fn = lambda: state_check.check_state(no_hash) |
618 | + def fn(): |
619 | + state_check.check_state(no_hash) |
620 | self.assertThat(fn, raises(TypeError("unhashable type: 'UnHashable'"))) |
621 | |
622 | === modified file 'autopilot/tests/unit/test_test_loader.py' |
623 | --- autopilot/tests/unit/test_test_loader.py 2014-05-20 08:53:21 +0000 |
624 | +++ autopilot/tests/unit/test_test_loader.py 2015-08-24 19:50:50 +0000 |
625 | @@ -57,9 +57,10 @@ |
626 | self.test_module_name = self._unique_module_name() |
627 | |
628 | def _unique_module_name(self): |
629 | - generator = lambda: ''.join( |
630 | - random.choice(string.ascii_letters) for letter in range(8) |
631 | - ) |
632 | + def generator(): |
633 | + return ''.join( |
634 | + random.choice(string.ascii_letters) for letter in range(8) |
635 | + ) |
636 | name = generator() |
637 | while name in self._previous_module_names: |
638 | name = generator() |
639 | |
640 | === modified file 'autopilot/tests/unit/test_testcase.py' |
641 | --- autopilot/tests/unit/test_testcase.py 2014-07-29 01:47:19 +0000 |
642 | +++ autopilot/tests/unit/test_testcase.py 2015-08-24 19:50:50 +0000 |
643 | @@ -39,10 +39,11 @@ |
644 | |
645 | def test_snapshot_raises_AssertionError_with_new_apps_opened(self): |
646 | with sleep.mocked(): |
647 | - fn = lambda: _compare_system_with_process_snapshot( |
648 | - lambda: ['foo'], |
649 | - [] |
650 | - ) |
651 | + def fn(): |
652 | + return _compare_system_with_process_snapshot( |
653 | + lambda: ['foo'], |
654 | + [] |
655 | + ) |
656 | self.assertThat(fn, raises(AssertionError( |
657 | "The following apps were started during the test and " |
658 | "not closed: ['foo']" |
659 | |
660 | === modified file 'autopilot/tests/unit/test_types.py' |
661 | --- autopilot/tests/unit/test_types.py 2014-10-30 02:06:55 +0000 |
662 | +++ autopilot/tests/unit/test_types.py 2015-08-24 19:50:50 +0000 |
663 | @@ -677,7 +677,8 @@ |
664 | ] |
665 | ) |
666 | |
667 | - fn = lambda: create_value_instance(data, None, None) |
668 | + def fn(): |
669 | + return create_value_instance(data, None, None) |
670 | |
671 | self.assertThat(fn, raises( |
672 | ValueError("Rectangle must be constructed with 4 arguments, not 1") |
673 | @@ -706,7 +707,8 @@ |
674 | ] |
675 | ) |
676 | |
677 | - fn = lambda: create_value_instance(data, None, None) |
678 | + def fn(): |
679 | + return create_value_instance(data, None, None) |
680 | |
681 | self.assertThat(fn, raises( |
682 | ValueError("Color must be constructed with 4 arguments, not 1") |
683 | @@ -735,7 +737,8 @@ |
684 | ] |
685 | ) |
686 | |
687 | - fn = lambda: create_value_instance(data, None, None) |
688 | + def fn(): |
689 | + create_value_instance(data, None, None) |
690 | |
691 | self.assertThat(fn, raises( |
692 | ValueError("Point must be constructed with 2 arguments, not 3") |
693 | @@ -762,7 +765,8 @@ |
694 | ] |
695 | ) |
696 | |
697 | - fn = lambda: create_value_instance(data, None, None) |
698 | + def fn(): |
699 | + return create_value_instance(data, None, None) |
700 | |
701 | self.assertThat(fn, raises( |
702 | ValueError("Size must be constructed with 2 arguments, not 1") |
703 | @@ -790,7 +794,8 @@ |
704 | ] |
705 | ) |
706 | |
707 | - fn = lambda: create_value_instance(data, None, None) |
708 | + def fn(): |
709 | + return create_value_instance(data, None, None) |
710 | |
711 | self.assertThat(fn, raises( |
712 | ValueError("DateTime must be constructed with 1 arguments, not 3") |
713 | @@ -821,7 +826,8 @@ |
714 | ] |
715 | ) |
716 | |
717 | - fn = lambda: create_value_instance(data, None, None) |
718 | + def fn(): |
719 | + return create_value_instance(data, None, None) |
720 | |
721 | self.assertThat(fn, raises( |
722 | ValueError("Time must be constructed with 4 arguments, not 3") |
723 | @@ -852,7 +858,9 @@ |
724 | dbus.Int32(0), |
725 | ] |
726 | ) |
727 | - fn = lambda: create_value_instance(data, None, None) |
728 | + |
729 | + def fn(): |
730 | + return create_value_instance(data, None, None) |
731 | |
732 | self.assertThat(fn, raises( |
733 | ValueError("Cannot create attribute, no data supplied") |
734 | @@ -881,7 +889,8 @@ |
735 | ] |
736 | ) |
737 | |
738 | - fn = lambda: create_value_instance(data, None, None) |
739 | + def fn(): |
740 | + return create_value_instance(data, None, None) |
741 | |
742 | self.assertThat(fn, raises( |
743 | ValueError("Point3D must be constructed with 3 arguments, not 2") |
744 | |
745 | === modified file 'autopilot/vis/dbus_search.py' |
746 | --- autopilot/vis/dbus_search.py 2014-04-10 21:39:39 +0000 |
747 | +++ autopilot/vis/dbus_search.py 2015-08-24 19:50:50 +0000 |
748 | @@ -43,12 +43,16 @@ |
749 | constructed from the provided bus, connection and object name. |
750 | |
751 | """ |
752 | - handler = lambda xml: self._xml_processor( |
753 | - conn_name, |
754 | - obj_name, |
755 | - xml |
756 | - ) |
757 | - error_handler = lambda *args: _logger.error("Error occured: %r" % args) |
758 | + def handler(xml): |
759 | + return self._xml_processor( |
760 | + conn_name, |
761 | + obj_name, |
762 | + xml |
763 | + ) |
764 | + |
765 | + def error_handler(*args): |
766 | + return _logger.error("Error occured: %r" % args) |
767 | + |
768 | obj = self._bus.get_object(conn_name, obj_name) |
769 | |
770 | # avoid introspecting our own PID, as that locks up with libdbus |
771 | |
772 | === modified file 'debian/changelog' |
773 | --- debian/changelog 2015-05-08 04:45:01 +0000 |
774 | +++ debian/changelog 2015-08-24 19:50:50 +0000 |
775 | @@ -1,12 +1,80 @@ |
776 | -autopilot (1.5.0) UNRELEASED; urgency=medium |
777 | - |
778 | +autopilot (1.5.1+15.10.20150723-0ubuntu1) wily; urgency=medium |
779 | + |
780 | + [ Christopher Lee ] |
781 | + * Bug fixes including updated dependencies. |
782 | + |
783 | + [ Ken VanDine ] |
784 | + * Bug fixes including updated dependencies. |
785 | + |
786 | + -- CI Train Bot <ci-train-bot@canonical.com> Thu, 23 Jul 2015 14:39:21 +0000 |
787 | + |
788 | +autopilot (1.5.1+15.10.20150716-0ubuntu1) wily; urgency=medium |
789 | + |
790 | + [ Christopher Lee ] |
791 | + * Add window manager support for autopilot sandbox run, fixes LP:1379508 |
792 | + * Add fixture that forces a gsetting for unity8 so that OSK is shown even |
793 | + when a UInput keyboard is present. |
794 | + * Fix unit test failures on Wily |
795 | + |
796 | + -- CI Train Bot <ci-train-bot@canonical.com> Thu, 16 Jul 2015 04:38:49 +0000 |
797 | + |
798 | +autopilot (1.5.1+15.04.20150708-0ubuntu1) vivid; urgency=medium |
799 | + |
800 | + [ Christopher Lee ] |
801 | + * Create correct touch device at creation to stop unity8 erroneously |
802 | + changing to windowed mode. (Fixes lp:1471598). |
803 | + |
804 | + -- CI Train Bot <ci-train-bot@canonical.com> Wed, 08 Jul 2015 03:44:43 +0000 |
805 | + |
806 | +autopilot (1.5.1+15.04.20150522-0ubuntu1) vivid; urgency=medium |
807 | + |
808 | + [ CI Train Bot ] |
809 | + * New rebuild forced. |
810 | + |
811 | + [ Christopher Lee, Federico Gimenez, Leo Arias, Richard Huddie, Vincent Ladeuil ] |
812 | + * Bug fixes for logging (debug level now -vv) and application of CPO |
813 | + bases for proxy objects. (LP: #1425721, #1376996, #1420949) |
814 | + |
815 | + -- CI Train Bot <ci-train-bot@canonical.com> Fri, 22 May 2015 16:54:03 +0000 |
816 | + |
817 | +autopilot (1.5.0+15.04.20150408-0ubuntu1) vivid; urgency=medium |
818 | + |
819 | + [ Albert Astals Cid ] |
820 | + * Bug fixes related to improving automated runs. |
821 | + |
822 | + [ Christopher Lee ] |
823 | + * Bug fixes related to improving automated runs. |
824 | + |
825 | + -- CI Train Bot <ci-train-bot@canonical.com> Wed, 08 Apr 2015 15:07:28 +0000 |
826 | + |
827 | +autopilot (1.5.0+15.04.20150323-0ubuntu1) vivid; urgency=medium |
828 | + |
829 | + [ CI Train Bot ] |
830 | + * New rebuild forced. |
831 | + |
832 | + [ Christopher Lee ] |
833 | + * Bugfix for Touch pointer pressed. Packaging req. due to python3-xlib |
834 | + bug. |
835 | + |
836 | + [ Leo Arias ] |
837 | + * Bugfix for Touch pointer pressed. Packaging req. due to python3-xlib |
838 | + bug. |
839 | + |
840 | + -- CI Train Bot <ci-train-bot@canonical.com> Mon, 23 Mar 2015 09:34:31 +0000 |
841 | + |
842 | +autopilot (1.5.0+15.04.20150226.1-0ubuntu1) vivid; urgency=medium |
843 | + |
844 | + [ Christopher Lee ] |
845 | * Fix for desktop file name change (lp:1411096) |
846 | Fix for config value containing '=' char (lp:1408317) |
847 | Fix for skipped tests not appearing in log (lp:1414632) |
848 | Add json docs build (for web publish) (lp:1409778) |
849 | Documentation improvements for API, Tutorial, FAQ, and Guidelines |
850 | |
851 | - -- Christopher Lee <chris.lee@canonical.com> Fri, 27 Feb 2015 10:16:42 +1300 |
852 | + [ CI Train Bot ] |
853 | + * New rebuild forced. |
854 | + |
855 | + -- CI Train Bot <ci-train-bot@canonical.com> Thu, 26 Feb 2015 22:47:35 +0000 |
856 | |
857 | autopilot (1.5.0+15.04.20141031-0ubuntu1) vivid; urgency=low |
858 | |
859 | @@ -131,7 +199,7 @@ |
860 | * Remove python2 packages and dependencies, as they're now handled by the |
861 | autopilot-legacy source package. (lp: #1308661) |
862 | * Autopilot vis tool can now search the introspection tree. (lp: #1097392) |
863 | - * Autopilot vis tool can now draw a transparent overlay over the selected |
864 | + * Autopilot vis tool can now draw a transparent overlay over the selected |
865 | widget. |
866 | * Autopilot vis tool now shows the root of the introspection tree (lp: #1298600) |
867 | * Autopilot vis tool can now be launched from the unity dash. |
868 | @@ -265,7 +333,7 @@ |
869 | |
870 | autopilot (1.4+14.04.20140310.1-0ubuntu1) trusty; urgency=low |
871 | |
872 | - * |
873 | + * |
874 | |
875 | -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 10 Mar 2014 19:39:00 +0000 |
876 | |
877 | |
878 | === modified file 'docs/otto.py' |
879 | --- docs/otto.py 2013-07-25 05:47:36 +0000 |
880 | +++ docs/otto.py 2015-08-24 19:50:50 +0000 |
881 | @@ -59,9 +59,6 @@ |
882 | image_container.children.append(nodes.image(uri='/images/otto-64.png')) |
883 | image_container['classes'] = ['otto-image-container'] |
884 | outer_container = nodes.container() |
885 | - outer_container.children.extend( |
886 | - [image_container] |
887 | - + ad |
888 | - ) |
889 | + outer_container.children.extend([image_container] + ad) |
890 | outer_container['classes'] = ['otto-says-container'] |
891 | return [outer_container] |
892 | |
893 | === modified file 'setup.py' |
894 | --- setup.py 2015-08-19 00:25:00 +0000 |
895 | +++ setup.py 2015-08-24 19:50:50 +0000 |
896 | @@ -20,9 +20,16 @@ |
897 | from setuptools import find_packages, setup, Extension |
898 | |
899 | import sys |
900 | + |
901 | +from setuptools import find_packages, setup, Extension |
902 | + |
903 | + |
904 | assert sys.version_info >= (3,), 'Python 3 is required' |
905 | - |
906 | - |
907 | +<<<<<<< TREE |
908 | + |
909 | + |
910 | +======= |
911 | +>>>>>>> MERGE-SOURCE |
912 | VERSION = '1.5.0' |
913 | |
914 |
FAILED: Continuous integration, rev:519 /code.launchpad .net/~barry/ autopilot/ lp1488175/ +merge/ 268967/ +edit-commit- message
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http:// jenkins. qa.ubuntu. com/job/ autopilot- ci/1133/ jenkins. qa.ubuntu. com/job/ autopilot- wily-amd64- ci/63/console jenkins. qa.ubuntu. com/job/ autopilot- wily-armhf- ci/62/console jenkins. qa.ubuntu. com/job/ autopilot- wily-i386- ci/63/console
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/autopilot- ci/1133/ rebuild
http://