Merge ~juliank/autopkgtest/+git/development:master into ~ubuntu-release/autopkgtest/+git/development:master
- Git
- lp:~juliank/autopkgtest/+git/development
- master
- Merge into master
Proposed by
Julian Andres Klode
Status: | Merged |
---|---|
Merge reported by: | Iain Lane |
Merged at revision: | 3c1043c40694e77c3c05fb3b07afcf35cc965fae |
Proposed branch: | ~juliank/autopkgtest/+git/development:master |
Merge into: | ~ubuntu-release/autopkgtest/+git/development:master |
Diff against target: |
1951 lines (+982/-127) (has conflicts) 18 files modified
.gitlab-ci.yml (+26/-0) debian/changelog (+109/-5) debian/control (+10/-3) debian/copyright (+1/-1) doc/README.package-tests.rst (+63/-3) lib/VirtSubproc.py (+11/-2) lib/adt_testbed.py (+137/-35) lib/adtlog.py (+8/-0) lib/autopkgtest_args.py (+8/-0) lib/testdesc.py (+94/-21) runner/autopkgtest (+44/-11) runner/autopkgtest.1 (+23/-8) tests/autopkgtest (+368/-19) tests/testdesc (+18/-16) virt/autopkgtest-virt-lxc (+12/-0) virt/autopkgtest-virt-lxd (+12/-0) virt/autopkgtest-virt-qemu (+30/-3) virt/autopkgtest-virt-qemu.1 (+8/-0) Conflict in virt/autopkgtest-virt-lxc Conflict in virt/autopkgtest-virt-lxd |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Iain Lane | Approve | ||
Review via email: mp+355422@code.launchpad.net |
Commit message
Description of the change
Should force push instead of merge
To post a comment you must log in.
Revision history for this message
Julian Andres Klode (juliank) wrote : | # |
Revision history for this message
Iain Lane (laney) wrote : | # |
Done, thanks (to pings on #debian-release).
I re-rebased, so this probably won't get marked as merged automatically.
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml |
2 | new file mode 100644 |
3 | index 0000000..f28fa86 |
4 | --- /dev/null |
5 | +++ b/.gitlab-ci.yml |
6 | @@ -0,0 +1,26 @@ |
7 | +quicktests: |
8 | + stage: test |
9 | + image: debian:sid |
10 | + script: |
11 | + - apt-get update |
12 | + - apt-get install -y autodep8 libdpkg-perl pycodestyle pyflakes3 python3-debian |
13 | + - tests/autopkgtest_args |
14 | + - tests/pycodestyle |
15 | + - tests/pyflakes |
16 | + - tests/testdesc |
17 | + |
18 | +tests-sid: |
19 | + stage: test |
20 | + image: debian:sid |
21 | + script: |
22 | + - apt-get update |
23 | + - apt-get install -y apt-utils autodep8 build-essential debhelper libdpkg-perl procps python3 python3-debian |
24 | + - tests/autopkgtest NullRunner NullRunnerRoot ChrootRunner |
25 | + |
26 | +tests-stretch: |
27 | + stage: test |
28 | + image: debian:stretch |
29 | + script: |
30 | + - apt-get update |
31 | + - apt-get install -y apt-utils autodep8 build-essential debhelper libdpkg-perl procps python3 python3-debian |
32 | + - tests/autopkgtest NullRunner NullRunnerRoot ChrootRunner |
33 | diff --git a/debian/changelog b/debian/changelog |
34 | index 212d827..ec56090 100644 |
35 | --- a/debian/changelog |
36 | +++ b/debian/changelog |
37 | @@ -1,3 +1,107 @@ |
38 | +autopkgtest (5.5) unstable; urgency=medium |
39 | + |
40 | + [ Antonio Terceiro ] |
41 | + * Different implementation to make sure to never pass an empty string as |
42 | + username to `su` |
43 | + * When using systemd, reboot via systemd-run |
44 | + * test_breaks_testbed: allow output on stderr |
45 | + |
46 | + [ Paul Gevers ] |
47 | + * Fix whitespace in old parts of d/changelog |
48 | + * Add gitlab pipelines to autopkgtest |
49 | + * Fixes to be conform pycodestyle |
50 | + * tests: replace encoding='utf-8' by universal_newlines=True for |
51 | + python3.5 (stretch) |
52 | + * Fix missing empty line in commit 30470603 |
53 | + * Add option to disable the apt fallback when dependencies can't be |
54 | + installed due to pinning |
55 | + * Make autopkgtest Build-Depends-Arch aware (Closes: #903398) |
56 | + * Add skip-not-installable restriction (Closes: #905311) |
57 | + * Drop ancient X-Python3-Version (thanks lintian) |
58 | + |
59 | + [ Ian Jackson ] |
60 | + * doc/README.package-tests.rst: document hint-testsuite-triggers |
61 | + (Closes: #905310) |
62 | + |
63 | + [ Simon McVittie ] |
64 | + * runner: Document exit status 14 as possible |
65 | + * tests: Don't rely on being able to install B-D as non-root (Closes: |
66 | + #905677) |
67 | + * tests: Skip test_tree_build_needed_success if necessary |
68 | + * runner: Exit 8 if every test was skipped or otherwise ignored |
69 | + (Closes: #901804) |
70 | + |
71 | + -- Paul Gevers <elbrus@debian.org> Fri, 10 Aug 2018 20:27:40 +0200 |
72 | + |
73 | +autopkgtest (5.4.2) unstable; urgency=medium |
74 | + |
75 | + [ Martin Pitt ] |
76 | + * doc: Document "test-name" feature |
77 | + * doc/README.package-tests.rst: Fix formatting of previous commit |
78 | + |
79 | + [ Niko Tyni ] |
80 | + * Fix versioned provides test dependency handling |
81 | + * Refactor _synthesize_deps() parameters |
82 | + * Properly handle virtual packages in alternative test dependencies |
83 | + (Closes: #903975) |
84 | + * Add test cases for alternative test dependencies improvements |
85 | + |
86 | + [ Antonio Terceiro ] |
87 | + * Always pass a non-empty user string to `su` (Closes: #904870) |
88 | + |
89 | + -- Antonio Terceiro <terceiro@debian.org> Sun, 29 Jul 2018 12:13:06 -0300 |
90 | + |
91 | +autopkgtest (5.4.1) unstable; urgency=medium |
92 | + |
93 | + * README.package-tests.rst: explain that architecture qualifiers are |
94 | + allowed in Test-Depends |
95 | + * SchrootRunner: add testcases for arch qualifiers in Test-Depends |
96 | + * Fix regression on arch-specific packages with '@' in Test-Depends |
97 | + (Closes: #903000) |
98 | + |
99 | + -- Paul Gevers <elbrus@debian.org> Fri, 06 Jul 2018 20:05:39 +0200 |
100 | + |
101 | +autopkgtest (5.4) unstable; urgency=medium |
102 | + |
103 | + [ Simon McVittie ] |
104 | + * README.package-tests.rst: Document AUTOPKGTEST_NORMAL_USER |
105 | + * d/tests/lxd: Don't assume all test-runners set AUTOPKGTEST_NORMAL_USER |
106 | + * qemu: Only set up base image device if requested (Closes: #892023, #862362) |
107 | + * qemu: Document --baseimage |
108 | + * qemu: Update test for --baseimage no longer being the default |
109 | + * qemu, lxc, lxd: Look for a user account in the 1000-59999 range |
110 | + (Closes: #897170) |
111 | + * qemu: Add a shortcut for running tests on an EFI-booted image (Closes: |
112 | + #898340) |
113 | + * doc: Describe how to parse Features, Restrictions, Classes |
114 | + * Add support for flaky tests (Closes: #851558) |
115 | + * Add support for tests declaring themselves to have been skipped |
116 | + * autopkgtest(1): Document FLAKY as a possible summary status |
117 | + |
118 | + [ Balint Reczey ] |
119 | + * Fix bashism in retrying apt update |
120 | + |
121 | + [ Paul Gevers ] |
122 | + * Enable testing to continue after badpkg (Closes: #832751) |
123 | + * adt_testbed.py fix missed piece of regular expression in commit a7e1dad |
124 | + * d/tests/autopkgtest Update SchrootRunner |
125 | + * runner/autopkgtest: Drop Ubuntu 12.04 fallback |
126 | + * manpage: make ordering consistent with --help (Closes: #901643) |
127 | + |
128 | + [ Julian Andres Klode ] |
129 | + * ssh-setup/nova: Add support for keystone v3 auth (LP: #1767433) |
130 | + |
131 | + [ Rafael Laboissiere ] |
132 | + * Set Maintainer email address to team+ci@tracker.debian.org |
133 | + |
134 | + [ Niko Tyni ] |
135 | + * Add a couple of testcases for versioned provides support |
136 | + * Ensure synthesized test dependencies are not satisfied by versioned Provides |
137 | + (Closes: #867081) |
138 | + * Remove the old '(>= 0)' hack for ensuring '@' pulls in real packages |
139 | + |
140 | + -- Paul Gevers <elbrus@debian.org> Mon, 02 Jul 2018 11:50:21 +0200 |
141 | + |
142 | autopkgtest (5.3.1) unstable; urgency=medium |
143 | |
144 | [ Iain Lane ] |
145 | @@ -3398,11 +3502,11 @@ autopkgtest (1.0.4) gutsy; urgency=low |
146 | * adt-run: |
147 | - Options for setting timeouts. |
148 | - Increase default timeouts 100s, 3ks, 10ks, 100ks. |
149 | - |
150 | + |
151 | * hosts/chinstrap/*: |
152 | Screen-scrape Launchpad to get existing bugs and generate a suitable |
153 | - suppression file so we file a bug iff there isn't one already. |
154 | - |
155 | + suppression file so we file a bug iff there isn't one already. |
156 | + |
157 | * hosts/cadmium/*: |
158 | New directory for convenience scripts etc on Canonical buildd. |
159 | |
160 | @@ -3541,7 +3645,7 @@ autopkgtest (0.8.2) gutsy unstable; urgency=low |
161 | * Fix handling of pre-built source trees. |
162 | * Fix cleanup handling not to delete tmpdir before resetting testbed's |
163 | apt. |
164 | - |
165 | + |
166 | -- Ian Jackson <ian@davenant.greenend.org.uk> Fri, 27 Apr 2007 16:06:15 +0100 |
167 | |
168 | autopkgtest (0.8.1) feisty; urgency=low |
169 | @@ -3571,7 +3675,7 @@ autopkgtest (0.8.0) feisty; urgency=low |
170 | * Longer timeouts by default. |
171 | * Print `adt-run: trace' for trace output. |
172 | * Show all apt stdout (including dpkg stdout) in contemporaneous trace. |
173 | - |
174 | + |
175 | -- Ian Jackson <iwj@ubuntu.com> Tue, 3 Apr 2007 20:08:13 +0100 |
176 | |
177 | autopkgtest (0.7.2) feisty; urgency=low |
178 | diff --git a/debian/control b/debian/control |
179 | index eed730e..d1a838b 100644 |
180 | --- a/debian/control |
181 | +++ b/debian/control |
182 | @@ -1,5 +1,5 @@ |
183 | Source: autopkgtest |
184 | -Maintainer: Debian CI team <debian-ci@lists.debian.org> |
185 | +Maintainer: Debian CI team <team+ci@tracker.debian.org> |
186 | Uploaders: Ian Jackson <ijackson@chiark.greenend.org.uk>, Martin Pitt <mpitt@debian.org>, Antonio Terceiro <terceiro@debian.org>, Paul Gevers <elbrus@debian.org> |
187 | Section: devel |
188 | Priority: optional |
189 | @@ -14,7 +14,6 @@ Build-Depends: debhelper (>= 9), |
190 | pycodestyle | pep8, |
191 | Vcs-Git: https://salsa.debian.org/ci-team/autopkgtest.git |
192 | Vcs-Browser: https://salsa.debian.org/ci-team/autopkgtest |
193 | -X-Python3-Version: >= 3.1 |
194 | |
195 | Package: autopkgtest |
196 | Architecture: all |
197 | @@ -25,7 +24,15 @@ Depends: python3, |
198 | procps, |
199 | ${misc:Depends} |
200 | Recommends: autodep8 |
201 | -Suggests: schroot, lxc, lxd-client, qemu-system, qemu-utils |
202 | +Suggests: |
203 | + lxc, |
204 | + lxd-client, |
205 | + ovmf, |
206 | + qemu-efi-aarch64, |
207 | + qemu-efi-arm, |
208 | + qemu-system, |
209 | + qemu-utils, |
210 | + schroot, |
211 | Breaks: debci (<< 1.7~) |
212 | Description: automatic as-installed testing for Debian packages |
213 | autopkgtest runs tests on binary packages. The tests are run on the |
214 | diff --git a/debian/copyright b/debian/copyright |
215 | index d592b1c..7b35827 100644 |
216 | --- a/debian/copyright |
217 | +++ b/debian/copyright |
218 | @@ -1,4 +1,4 @@ |
219 | -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ |
220 | +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ |
221 | |
222 | Files: * |
223 | Copyright: Copyright (C) 2006-2014 Canonical Ltd. |
224 | diff --git a/doc/README.package-tests.rst b/doc/README.package-tests.rst |
225 | index b212597..4cb60ba 100644 |
226 | --- a/doc/README.package-tests.rst |
227 | +++ b/doc/README.package-tests.rst |
228 | @@ -107,14 +107,19 @@ Restrictions: restriction-name [, another-restriction-name ...] |
229 | declare unknown restrictions will be skipped. See below for the |
230 | defined restrictions. |
231 | |
232 | + Restrictions are separated by commas and/or whitespace. |
233 | + |
234 | Features: feature-name [, another-feature-name ...] |
235 | Declares some additional capabilities or good properties of the |
236 | tests defined in this stanza. Any unknown features declared will be |
237 | completely ignored. See below for the defined features. |
238 | |
239 | + Features are separated by commas and/or whitespace. |
240 | + |
241 | Depends: dpkg dependency field syntax |
242 | Declares that the specified packages must be installed for the test |
243 | - to go ahead. This supports all features of dpkg dependencies (see |
244 | + to go ahead. This supports all features of dpkg dependencies, including |
245 | + the architecture qualifiers (see |
246 | https://www.debian.org/doc/debian-policy/ch-relationships.html), |
247 | plus the following extensions: |
248 | |
249 | @@ -126,7 +131,7 @@ Depends: dpkg dependency field syntax |
250 | once and without a version restriction). |
251 | |
252 | ``@builddeps@`` will be replaced by the package's |
253 | - ``Build-Depends:``, ``Build-Depends-Indep:``, and |
254 | + ``Build-Depends:``, ``Build-Depends-Indep:``, ``Build-Depends-Arch:``, and |
255 | ``build-essential``. This is useful if you have many build |
256 | dependencies which are only necessary for running the test suite and |
257 | you don't want to replicate them in the test ``Depends:``. However, |
258 | @@ -164,6 +169,8 @@ Classes: class-1 [, class-2 ...] |
259 | This is purely an informational field for autopkgtest itself and |
260 | will be ignored. |
261 | |
262 | + Classes are separated by commas and/or whitespace. |
263 | + |
264 | Any unknown fields will cause the whole stanza to be skipped. |
265 | |
266 | Defined restrictions |
267 | @@ -241,10 +248,63 @@ needs-recommends |
268 | Enable installation of recommended packages in apt for the test |
269 | dependencies. This does not affect build dependencies. |
270 | |
271 | +flaky |
272 | + The test is expected to fail intermittently, and is not suitable for |
273 | + gating continuous integration. This indicates a bug in either the |
274 | + package under test, a dependency or the test itself, but such bugs |
275 | + can be difficult to fix, and it is often difficult to know when the |
276 | + bug has been fixed without running the test for a while. If a |
277 | + ``flaky`` test succeeds, it will be treated like any other |
278 | + successful test, but if it fails it will be treated as though it |
279 | + had been skipped. |
280 | + |
281 | +skippable |
282 | + The test might need to be skipped for reasons that cannot be |
283 | + described by an existing restriction such as isolation-machine or |
284 | + breaks-testbed, but must instead be detected at runtime. If the |
285 | + test exits with status 77 (a convention borrowed from Automake), it |
286 | + will be treated as though it had been skipped. If it exits with any |
287 | + other status, its success or failure will be derived from the exit |
288 | + status and stderr as usual. Test authors must be careful to ensure |
289 | + that ``skippable`` tests never exit with status 77 for reasons that |
290 | + should be treated as a failure. |
291 | + |
292 | +skip-not-installable |
293 | + This test might have test dependencies that can't be fulfilled on all |
294 | + architectures. Therefore, when apt-get installs the dependencies, it will |
295 | + fail. Don't treat this as a test failure, but instead treat it as if the |
296 | + test was skipped. |
297 | + |
298 | +hint-testsuite-triggers |
299 | + This test exists purely as a hint to suggest when rerunning the |
300 | + tests is likely to be useful. Specifically, it exists to |
301 | + influence the way dpkg-source generates the Testsuite-Triggers |
302 | + .dsc header from test metadata: the Depends for this test are |
303 | + to be added to Testsuite-Triggers. (Just as they are for any other |
304 | + test.) |
305 | + |
306 | + The test with the hint-testsuite-triggers restriction should not |
307 | + actually be run. Future systems which understand per-test update |
308 | + triggering should treat the Depends of the test with this |
309 | + restriction, as triggering packages for all tests in this |
310 | + debian/tests/control. |
311 | + |
312 | + The packages listed as Depends for this test are usually indirect |
313 | + dependencies, updates to which are considered to pose a risk of |
314 | + regressions in other tests defined in this package. |
315 | + |
316 | + There is currently no way to specify this hint on a per-test |
317 | + basis; but in any case the debian.org machinery is not able to |
318 | + think about triggering individual tests. |
319 | + |
320 | Defined features |
321 | ---------------- |
322 | |
323 | -There are no currently defined Features. |
324 | +test-name |
325 | + Set an explicit test name for the log heading and the ``summary`` file |
326 | + for a ``Test-Command:`` inline test. When not given, these are |
327 | + enumerated like ``command1``. |
328 | + |
329 | |
330 | Source package header |
331 | --------------------- |
332 | diff --git a/lib/VirtSubproc.py b/lib/VirtSubproc.py |
333 | index c12d0c6..671a21d 100644 |
334 | --- a/lib/VirtSubproc.py |
335 | +++ b/lib/VirtSubproc.py |
336 | @@ -337,8 +337,17 @@ def cmd_reboot(c, ce): |
337 | if len(c) > 1 and c[1] == 'prepare-only': |
338 | adtlog.info('state saved, waiting for testbed to reboot...') |
339 | else: |
340 | - execute_timeout(None, 30, auxverb + |
341 | - ['sh', '-c', '(sleep 3; reboot) >/dev/null 2>&1 &']) |
342 | + (systemd_check, out, err) = execute_timeout( |
343 | + None, 10, auxverb + ['test', '-d', '/run/systemd/system'], |
344 | + stdout=subprocess.PIPE, stderr=subprocess.PIPE |
345 | + ) |
346 | + if systemd_check == 0: |
347 | + reboot_cmd = ['systemd-run', '--no-block', '--quiet', 'sh', '-c', 'sleep 3; reboot'] |
348 | + else: |
349 | + reboot_cmd = ['sh', '-c', '(sleep 3; reboot) >/dev/null 2>&1 &'] |
350 | + |
351 | + adtlog.debug('rebooting with command: %s' % reboot_cmd) |
352 | + execute_timeout(None, 30, auxverb + reboot_cmd) |
353 | caller.hook_wait_reboot() |
354 | |
355 | # restore downtmp |
356 | diff --git a/lib/adt_testbed.py b/lib/adt_testbed.py |
357 | index b52f5b8..6f228a4 100644 |
358 | --- a/lib/adt_testbed.py |
359 | +++ b/lib/adt_testbed.py |
360 | @@ -47,7 +47,8 @@ class Testbed: |
361 | def __init__(self, vserver_argv, output_dir, user, |
362 | setup_commands=[], setup_commands_boot=[], add_apt_pockets=[], |
363 | copy_files=[], pin_packages=[], add_apt_sources=[], |
364 | - add_apt_releases=[], apt_default_release=None): |
365 | + add_apt_releases=[], apt_default_release=None, |
366 | + enable_apt_fallback=True): |
367 | self.sp = None |
368 | self.lastsend = None |
369 | self.scratch = None |
370 | @@ -77,6 +78,7 @@ class Testbed: |
371 | self.last_reboot_marker = '' |
372 | self.eatmydata_prefix = [] |
373 | self.apt_pin_for_releases = [] |
374 | + self.enable_apt_fallback = enable_apt_fallback |
375 | self.nproc = None |
376 | self.cpu_model = None |
377 | self.cpu_flags = None |
378 | @@ -88,6 +90,10 @@ class Testbed: |
379 | |
380 | adtlog.debug('testbed init') |
381 | |
382 | + @property |
383 | + def su_user(self): |
384 | + return self.user or 'root' |
385 | + |
386 | def start(self): |
387 | # are we running from a checkout? |
388 | root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) |
389 | @@ -389,7 +395,7 @@ class Testbed: |
390 | self._opened(pl) |
391 | self.modified = False |
392 | |
393 | - def install_deps(self, deps_new, recommends, shell_on_failure=False): |
394 | + def install_deps(self, deps_new, recommends, shell_on_failure=False, synth_deps=[]): |
395 | '''Install dependencies into testbed''' |
396 | adtlog.debug('install_deps: deps_new=%s, recommends=%s' % (deps_new, recommends)) |
397 | |
398 | @@ -397,7 +403,7 @@ class Testbed: |
399 | self.recommends_installed = recommends |
400 | if not deps_new: |
401 | return |
402 | - self.satisfy_dependencies_string(', '.join(deps_new), 'install-deps', recommends, shell_on_failure=shell_on_failure) |
403 | + self.satisfy_dependencies_string(', '.join(deps_new), 'install-deps', recommends, shell_on_failure=shell_on_failure, synth_deps=synth_deps) |
404 | |
405 | def needs_reset(self): |
406 | # show what caused a reset |
407 | @@ -412,7 +418,9 @@ class Testbed: |
408 | raise _type(m) |
409 | |
410 | def badpkg(self, m): |
411 | - self.bomb(m, adtlog.BadPackageError) |
412 | + _type = adtlog.BadPackageError |
413 | + adtlog.debug('%s %s' % (_type.__name__, m)) |
414 | + raise _type(m) |
415 | |
416 | def send(self, string): |
417 | try: |
418 | @@ -541,7 +549,60 @@ class Testbed: |
419 | adtlog.AutopkgtestError) |
420 | return out |
421 | |
422 | - def install_apt(self, deps, recommends=False, shell_on_failure=False): |
423 | + def is_real_package_installed(self, package): |
424 | + '''Check if a non-virtual package is installed in the testbed''' |
425 | + (rc, out, err) = self.execute(['dpkg-query', '--show', '-f', '${Status}', package], |
426 | + stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
427 | + if rc != 0: |
428 | + if 'no packages found' in err: |
429 | + return False |
430 | + self.badpkg('Failed to run dpkg-query: %s (exit code %d)' % (err, rc)) |
431 | + if out == 'install ok installed': |
432 | + return True |
433 | + return False |
434 | + |
435 | + def is_virtual_package_installed(self, package): |
436 | + '''Check if a package is installed in the testbed''' |
437 | + (rc, out, err) = self.execute(['dpkg-query', '--show', '-f', '${Status} ${Provides}\n', '*'], |
438 | + stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
439 | + if rc != 0: |
440 | + self.badpkg('Failed to run dpkg-query: %s (exit code %d)' % (err, rc)) |
441 | + return False |
442 | + if not out: |
443 | + return False |
444 | + for line in out.splitlines(): |
445 | + prefix = 'install ok installed ' |
446 | + if not line.startswith(prefix): |
447 | + continue |
448 | + line = line[len(prefix):] |
449 | + for p in line.split(','): |
450 | + (p, _, _) = p.lstrip().partition(' ') # ' foo (== 1.0)' => 'foo' |
451 | + if p == package: |
452 | + return True |
453 | + return False |
454 | + |
455 | + def _run_apt_install(self, what, prefix, recommends): |
456 | + '''actually run apt-get install''' |
457 | + |
458 | + # capture status-fd to stderr |
459 | + (rc, _, serr) = self.execute(['/bin/sh', '-ec', '%s apt-get install ' |
460 | + '--assume-yes %s ' |
461 | + '-o APT::Status-Fd=3 ' |
462 | + '-o APT::Install-Recommends=%s ' |
463 | + '-o Dpkg::Options::=--force-confnew ' |
464 | + '-o Debug::pkgProblemResolver=true 3>&2 2>&1' % |
465 | + (prefix, what, recommends)], |
466 | + kind='install', stderr=subprocess.PIPE) |
467 | + if rc != 0: |
468 | + adtlog.debug('apt-get install %s failed; status-fd:\n%s' % (what, serr)) |
469 | + # check if apt failed during package download, which might be a |
470 | + # transient error, so retry |
471 | + if 'dlstatus:' in serr and 'pmstatus:' not in serr: |
472 | + raise adtlog.AptDownloadError |
473 | + else: |
474 | + raise adtlog.AptPermanentError |
475 | + |
476 | + def install_apt(self, deps, recommends=False, shell_on_failure=False, synth_deps=[]): |
477 | '''Install dependencies with apt-get into testbed |
478 | |
479 | This requires root privileges and a writable file system. |
480 | @@ -575,28 +636,60 @@ Description: satisfy autopkgtest test dependencies |
481 | download_fail_retries = 3 |
482 | while True: |
483 | self.check_exec(['dpkg', '--unpack', deb.tb], stdout=subprocess.PIPE) |
484 | - # capture status-fd to stderr |
485 | - (rc, _, serr) = self.execute(['/bin/sh', '-ec', '%s apt-get install ' |
486 | - '--assume-yes --fix-broken ' |
487 | - '-o APT::Status-Fd=3 ' |
488 | - '-o APT::Install-Recommends=%s ' |
489 | - '-o Dpkg::Options::=--force-confnew ' |
490 | - '-o Debug::pkgProblemResolver=true 3>&2 2>&1' % |
491 | - (' '.join(self.eatmydata_prefix), recommends)], |
492 | - kind='install', stderr=subprocess.PIPE) |
493 | - if rc != 0: |
494 | - adtlog.debug('apt-get install failed; status-fd:\n%s' % serr) |
495 | - # check if apt failed during package download, which might be a |
496 | - # transient error, so retry |
497 | - if 'dlstatus:' in serr and 'pmstatus:' not in serr: |
498 | - download_fail_retries -= 1 |
499 | - if download_fail_retries > 0: |
500 | - adtlog.warning('apt failed to download packages, retrying in 10s...') |
501 | - time.sleep(10) |
502 | - continue |
503 | + |
504 | + # if '@' was provided as a test dependency (as seen in synth_deps), |
505 | + # we explicitly install the corresponding binary packages in case |
506 | + # the dependencies on those were satisfied by versioned Provides |
507 | + |
508 | + rc = 0 |
509 | + try: |
510 | + self._run_apt_install('--fix-broken', ' '.join(self.eatmydata_prefix), recommends) |
511 | + need_explicit_install = [] |
512 | + for dep in synth_deps: |
513 | + if dep != list(dep): |
514 | + # simple test dependency (no alternatives) |
515 | + if self.is_real_package_installed(dep): |
516 | + continue |
517 | + if self.is_virtual_package_installed(dep): |
518 | + need_explicit_install.append(dep) |
519 | + continue |
520 | + adtlog.warning('package %s is not installed though it should be' % dep) |
521 | else: |
522 | - self.bomb('apt repeatedly failed to download packages') |
523 | + # figure out which test dependency alternative got installed |
524 | + installed_virtual_packages = [] |
525 | + found_a_real_package = False |
526 | + for pkg in dep: |
527 | + if self.is_real_package_installed(pkg): |
528 | + # no need to install anything from this set of alternatives |
529 | + found_a_real_package = True |
530 | + break |
531 | + if self.is_virtual_package_installed(pkg): |
532 | + installed_virtual_packages.append(pkg) |
533 | + if found_a_real_package: |
534 | + continue |
535 | + if not installed_virtual_packages: |
536 | + adtlog.warning('no alternative in %s is installed though one should be' % dep) |
537 | + if len(installed_virtual_packages) > 1: |
538 | + adtlog.warning('more than one test dependency alternative in %s installed as a virtual package, installing the first one (%s) as the real package' % (dep, installed_virtual_packages[0])) |
539 | + need_explicit_install.append(installed_virtual_packages[0]) |
540 | + |
541 | + if need_explicit_install: |
542 | + adtlog.debug('installing real packages of test dependencies: %s' % need_explicit_install) |
543 | + self._run_apt_install(' '.join(need_explicit_install), ' '.join(self.eatmydata_prefix), recommends) |
544 | + |
545 | + # check if apt failed during package download, which might be a |
546 | + # transient error, so retry |
547 | + except adtlog.AptDownloadError: |
548 | + download_fail_retries -= 1 |
549 | + if download_fail_retries > 0: |
550 | + adtlog.warning('apt failed to download packages, retrying in 10s...') |
551 | + time.sleep(10) |
552 | + continue |
553 | + else: |
554 | + self.bomb('apt repeatedly failed to download packages') |
555 | |
556 | + except adtlog.AptPermanentError: |
557 | + rc = -1 |
558 | if shell_on_failure: |
559 | self.run_shell() |
560 | else: |
561 | @@ -607,7 +700,7 @@ Description: satisfy autopkgtest test dependencies |
562 | stderr=subprocess.PIPE)[0] |
563 | |
564 | if rc != 0: |
565 | - if self.apt_pin_for_releases: |
566 | + if self.apt_pin_for_releases and self.enable_apt_fallback: |
567 | release = self.apt_pin_for_releases.pop() |
568 | adtlog.warning('Test dependencies are unsatisfiable with using apt pinning. ' |
569 | 'Retrying with using all packages from %s' % release) |
570 | @@ -618,10 +711,17 @@ Description: satisfy autopkgtest test dependencies |
571 | |
572 | if shell_on_failure: |
573 | self.run_shell() |
574 | - self.badpkg('Test dependencies are unsatisfiable. A common reason is ' |
575 | - 'that your testbed is out of date with respect to the ' |
576 | - 'archive, and you need to use a current testbed or run ' |
577 | - 'apt-get update or use -U.') |
578 | + if self.enable_apt_fallback: |
579 | + self.badpkg('Test dependencies are unsatisfiable. A common reason is ' |
580 | + 'that your testbed is out of date with respect to the ' |
581 | + 'archive, and you need to use a current testbed or run ' |
582 | + 'apt-get update or use -U.') |
583 | + else: |
584 | + self.badpkg('Test dependencies are unsatisfiable. A common reason is ' |
585 | + 'that the requested apt pinning prevented dependencies ' |
586 | + 'from the non-default suite to be installed. In that ' |
587 | + 'case you need to add those dependencies to the pinning ' |
588 | + 'list.') |
589 | break |
590 | |
591 | # remove autopkgtest-satdep to avoid confusing tests, but avoid marking our |
592 | @@ -842,7 +942,7 @@ fi |
593 | # we don't want su -l here which resets the environment from |
594 | # self.execute(); so emulate the parts that we want |
595 | # FIXME: move "run as user" as an argument of execute()/check_exec() and run with -l |
596 | - self.check_exec(['su', '--shell=/bin/sh', self.user, '-c', |
597 | + self.check_exec(['su', '--shell=/bin/sh', self.su_user, '-c', |
598 | ('export USER=%s;' % self.user) + |
599 | '. /etc/profile >/dev/null 2>&1 || true; ' |
600 | ' . ~/.profile >/dev/null 2>&1 || true; ' |
601 | @@ -915,7 +1015,7 @@ fi |
602 | self.bomb('Failed to update click AppArmor rules') |
603 | |
604 | def satisfy_dependencies_string(self, deps, what, recommends=False, |
605 | - build_dep=False, shell_on_failure=False): |
606 | + build_dep=False, shell_on_failure=False, synth_deps=[]): |
607 | '''Install dependencies from a string into the testbed''' |
608 | |
609 | adtlog.debug('%s: satisfying %s' % (what, deps)) |
610 | @@ -957,7 +1057,7 @@ fi |
611 | adtlog.debug('can use apt-get on testbed: %s' % can_apt_get) |
612 | |
613 | if can_apt_get: |
614 | - self.install_apt(deps, recommends, shell_on_failure) |
615 | + self.install_apt(deps, recommends, shell_on_failure, synth_deps) |
616 | else: |
617 | self.install_tmp(deps, recommends) |
618 | |
619 | @@ -1069,7 +1169,7 @@ fi |
620 | # we don't want -l here which resets the environment from |
621 | # self.execute(); so emulate the parts that we want |
622 | # FIXME: move "run as user" as an argument of execute()/check_exec() and run with -l |
623 | - test_argv = ['su', '-s', '/bin/bash', self.user, '-c'] |
624 | + test_argv = ['su', '-s', '/bin/bash', self.su_user, '-c'] |
625 | |
626 | if 'rw-build-tree' in test.restrictions: |
627 | self.check_exec(['chown', '-R', self.user, tree.tb]) |
628 | @@ -1160,6 +1260,8 @@ fi |
629 | |
630 | if timeout: |
631 | test.failed('timed out') |
632 | + elif rc == 77 and 'skippable' in test.restrictions: |
633 | + test.set_skipped('exit status 77 and marked as skippable') |
634 | elif rc != 0: |
635 | test.failed('non-zero exit status %d' % rc) |
636 | elif se_size != 0 and 'allow-stderr' not in test.restrictions: |
637 | @@ -1265,7 +1367,7 @@ fi |
638 | if self.default_release is None: |
639 | |
640 | script = 'SRCS=$(ls /etc/apt/sources.list /etc/apt/sources.list.d/*.list 2>/dev/null|| true); ' |
641 | - script += '''sed -rn '/^(deb|deb-src) (http|https|file):/ { s/\[.*\] +//; s/^[^ ]+ +[^ ]* +([^ -]+) +.*$/\\1/p }' $SRCS | head -n1''' |
642 | + script += '''sed -rn '/^(deb|deb-src) +(\[.*\] *)?(http|https|file):/ { s/\[.*\] +//; s/^[^ ]+ +[^ ]* +([^ -]+) +.*$/\\1/p }' $SRCS | head -n1''' |
643 | self.default_release = self.check_exec(['sh', '-ec', script], stdout=True).strip() |
644 | |
645 | return self.default_release |
646 | diff --git a/lib/adtlog.py b/lib/adtlog.py |
647 | index 8f18386..4405c76 100644 |
648 | --- a/lib/adtlog.py |
649 | +++ b/lib/adtlog.py |
650 | @@ -125,3 +125,11 @@ def bomb(m): |
651 | |
652 | def badpkg(m): |
653 | raise BadPackageError(m) |
654 | + |
655 | + |
656 | +class AptPermanentError(Exception): |
657 | + pass |
658 | + |
659 | + |
660 | +class AptDownloadError(Exception): |
661 | + pass |
662 | diff --git a/lib/autopkgtest_args.py b/lib/autopkgtest_args.py |
663 | index e681bc0..0e8e0f3 100644 |
664 | --- a/lib/autopkgtest_args.py |
665 | +++ b/lib/autopkgtest_args.py |
666 | @@ -339,6 +339,14 @@ for details.''' |
667 | help='Set "parallel=N" DEB_BUILD_OPTION for building ' |
668 | 'packages (default: number of available processors)') |
669 | g_misc.add_argument( |
670 | + '--no-apt-fallback', |
671 | + dest='enable_apt_fallback', |
672 | + action='store_false', |
673 | + default=True, |
674 | + help='Disable the apt fallback which is used with --apt-pocket or ' |
675 | + '--pin-packages in case installation of dependencies fails due to ' |
676 | + 'strict pinning') |
677 | + g_misc.add_argument( |
678 | '-h', '--help', action='help', default=argparse.SUPPRESS, |
679 | help='show this help message and exit') |
680 | |
681 | diff --git a/lib/testdesc.py b/lib/testdesc.py |
682 | index c6bb3ed..dfc9ee3 100644 |
683 | --- a/lib/testdesc.py |
684 | +++ b/lib/testdesc.py |
685 | @@ -42,7 +42,9 @@ import adtlog |
686 | |
687 | known_restrictions = ['rw-build-tree', 'breaks-testbed', 'needs-root', |
688 | 'build-needed', 'allow-stderr', 'isolation-container', |
689 | - 'isolation-machine', 'needs-recommends', 'needs-reboot'] |
690 | + 'isolation-machine', 'needs-recommends', 'needs-reboot', |
691 | + 'flaky', 'skippable', |
692 | + 'skip-not-installable'] |
693 | |
694 | |
695 | class Unsupported(Exception): |
696 | @@ -80,7 +82,7 @@ class Test: |
697 | actions. |
698 | ''' |
699 | def __init__(self, name, path, command, restrictions, features, depends, |
700 | - clicks, installed_clicks): |
701 | + clicks, installed_clicks, synth_depends): |
702 | '''Create new test description |
703 | |
704 | A test must have either "path" or "command", the respective other value |
705 | @@ -93,6 +95,7 @@ class Test: |
706 | @depends: string list of test dependencies (packages) |
707 | @clicks: path list of click packages to install for this test |
708 | @installed_clicks: names of already installed clicks for this test |
709 | + @synth_depends: string list of synthesized test dependencies (packages) |
710 | ''' |
711 | if '/' in name: |
712 | raise Unsupported(name, 'test name may not contain / character') |
713 | @@ -111,8 +114,10 @@ class Test: |
714 | self.depends = depends |
715 | self.clicks = clicks |
716 | self.installed_clicks = installed_clicks |
717 | + self.synth_depends = synth_depends |
718 | # None while test hasn't run yet; True: pass, False: fail |
719 | self.result = None |
720 | + self.skipped = False |
721 | adtlog.debug('Test defined: name %s path %s command "%s" ' |
722 | 'restrictions %s features %s depends %s clicks %s ' |
723 | 'installed clicks %s' % |
724 | @@ -125,11 +130,23 @@ class Test: |
725 | self.result = True |
726 | adtlog.report(self.name, 'PASS') |
727 | |
728 | + def set_skipped(self, reason): |
729 | + '''Mark test as skipped''' |
730 | + # This isn't called skipped() to avoid clashing with the boolean |
731 | + # attribute. |
732 | + |
733 | + self.skipped = True |
734 | + self.result = True |
735 | + adtlog.report(self.name, 'SKIP ' + reason) |
736 | + |
737 | def failed(self, reason): |
738 | '''Mark test as failed''' |
739 | |
740 | self.result = False |
741 | - adtlog.report(self.name, 'FAIL ' + reason) |
742 | + if 'flaky' in self.restrictions: |
743 | + adtlog.report(self.name, 'FLAKY ' + reason) |
744 | + else: |
745 | + adtlog.report(self.name, 'FAIL ' + reason) |
746 | |
747 | def check_testbed_compat(self, caps): |
748 | '''Check for restrictions incompatible with test bed capabilities. |
749 | @@ -221,6 +238,7 @@ def _debian_check_unknown_fields(name, record): |
750 | |
751 | def _debian_packages_from_source(srcdir): |
752 | packages = [] |
753 | + packages_no_arch = [] |
754 | |
755 | for st in parse_rfc822(os.path.join(srcdir, 'debian/control')): |
756 | if 'Package' not in st: |
757 | @@ -232,11 +250,12 @@ def _debian_packages_from_source(srcdir): |
758 | continue |
759 | arch = st['Architecture'] |
760 | if arch in ('all', 'any'): |
761 | - packages.append('%s (>= 0~)' % st['Package']) |
762 | + packages.append(st['Package']) |
763 | else: |
764 | - packages.append('%s (>= 0~) [%s]' % (st['Package'], arch)) |
765 | + packages.append('%s [%s]' % (st['Package'], arch)) |
766 | + packages_no_arch.append(st['Package']) |
767 | |
768 | - return packages |
769 | + return (packages, packages_no_arch) |
770 | |
771 | |
772 | def _debian_build_deps_from_source(srcdir, testbed_arch): |
773 | @@ -246,6 +265,8 @@ def _debian_build_deps_from_source(srcdir, testbed_arch): |
774 | deps += st['Build-depends'] |
775 | if 'Build-depends-indep' in st: |
776 | deps += ', ' + st['Build-depends-indep'] |
777 | + if 'Build-depends-arch' in st: |
778 | + deps += ', ' + st['Build-depends-arch'] |
779 | |
780 | # resolve arch specific dependencies and build profiles |
781 | perl = subprocess.Popen(['perl', '-'], stdin=subprocess.PIPE, |
782 | @@ -274,7 +295,7 @@ def _debian_build_deps_from_source(srcdir, testbed_arch): |
783 | dep_re = re.compile( |
784 | r'(?P<package>[a-z0-9+-.]+)(?::native)?\s*' |
785 | r'(\((?P<relation><<|<=|>=|=|>>)\s*(?P<version>[^\)]*)\))?' |
786 | - r'(\s*\[[[a-z0-9+-.! ]+\])?$') |
787 | + r'(\s*\[(?P<arch>[[a-z0-9+-.! ]+)\])?$') |
788 | |
789 | |
790 | def _debian_check_dep(testname, dep): |
791 | @@ -296,6 +317,38 @@ def _debian_check_dep(testname, dep): |
792 | # too old python-debian, skip the check |
793 | pass |
794 | |
795 | + return (m.group('package'), m.group('version')) |
796 | + |
797 | + |
798 | +def _synthesize_deps(dep, testbed_arch): |
799 | + '''Ensure that apt can install synthesized Depends |
800 | + |
801 | + Test Depends may have architecture qualifiers we need to check, |
802 | + because apt command-line can't handle those and can't do the check. |
803 | + (Policy says the architecture qualifier should not be in the binary |
804 | + control file). We'll ignore the version here. |
805 | + ''' |
806 | + |
807 | + dep = dep.strip() |
808 | + m = dep_re.match(dep) |
809 | + |
810 | + arch_matches = 'y' |
811 | + if m.group('arch') is not None: |
812 | + arch_matches = False |
813 | + perl = subprocess.Popen(['perl', '-'], stdin=subprocess.PIPE, |
814 | + stdout=subprocess.PIPE) |
815 | + code = '''use Dpkg::Deps; |
816 | + print deps_parse('foo [%s]')->arch_is_concerned('%s') ? 'y' : 'n'; |
817 | + ''' % (m.group('arch'), testbed_arch) |
818 | + arch_matches = perl.communicate(code.encode('UTF-8'))[0].decode('UTF-8').strip() |
819 | + if perl.returncode != 0: |
820 | + raise InvalidControl('source', 'Invalid (Test-)Depends architecture qualifiers') |
821 | + |
822 | + if arch_matches == 'y': |
823 | + return m.group('package') |
824 | + else: |
825 | + return None |
826 | + |
827 | |
828 | def _parse_debian_depends(testname, dep_str, srcdir, testbed_arch): |
829 | '''Parse Depends: line in a Debian package |
830 | @@ -307,6 +360,8 @@ def _parse_debian_depends(testname, dep_str, srcdir, testbed_arch): |
831 | dependencies. |
832 | ''' |
833 | deps = [] |
834 | + synthdeps = [] |
835 | + (my_packages, my_packages_no_arch) = _debian_packages_from_source(srcdir) |
836 | for alt_group_str in dep_str.split(','): |
837 | alt_group_str = alt_group_str.strip() |
838 | if not alt_group_str: |
839 | @@ -314,19 +369,35 @@ def _parse_debian_depends(testname, dep_str, srcdir, testbed_arch): |
840 | continue |
841 | adtlog.debug('processing dependency %s' % alt_group_str) |
842 | if alt_group_str == '@': |
843 | - for d in _debian_packages_from_source(srcdir): |
844 | + for d in my_packages: |
845 | adtlog.debug('synthesised dependency %s' % d) |
846 | deps.append(d) |
847 | + s = _synthesize_deps(d, testbed_arch) |
848 | + if s: |
849 | + synthdeps.append(s) |
850 | elif alt_group_str == '@builddeps@': |
851 | for d in _debian_build_deps_from_source(srcdir, testbed_arch): |
852 | adtlog.debug('synthesised dependency %s' % d) |
853 | deps.append(d) |
854 | else: |
855 | + synthdep_alternatives = [] |
856 | for dep in alt_group_str.split('|'): |
857 | - _debian_check_dep(testname, dep) |
858 | + (pkg, version) = _debian_check_dep(testname, dep) |
859 | + if pkg not in my_packages_no_arch: |
860 | + synthdep_alternatives = [] |
861 | + break |
862 | + s = _synthesize_deps(dep, testbed_arch) |
863 | + if s: |
864 | + synthdep_alternatives.append(s) |
865 | + if synthdep_alternatives: |
866 | + adtlog.debug('marked alternatives %s as a synthesised dependency' % synthdep_alternatives) |
867 | + if len(synthdep_alternatives) > 1: |
868 | + synthdeps.append(synthdep_alternatives) |
869 | + else: |
870 | + synthdeps.append(synthdep_alternatives[0]) |
871 | deps.append(alt_group_str) |
872 | |
873 | - return deps |
874 | + return (deps, synthdeps) |
875 | |
876 | |
877 | def _autodep8(srcdir): |
878 | @@ -411,10 +482,11 @@ def parse_debian_source(srcdir, testbed_caps, testbed_arch, control_path=None, |
879 | |
880 | if 'Tests' in record: |
881 | test_names = record['Tests'].replace(',', ' ').split() |
882 | - depends = _parse_debian_depends(test_names[0], |
883 | - record.get('Depends', '@'), |
884 | - srcdir, |
885 | - testbed_arch) |
886 | + (depends, synth_depends) = _parse_debian_depends( |
887 | + test_names[0], |
888 | + record.get('Depends', '@'), |
889 | + srcdir, |
890 | + testbed_arch) |
891 | if 'Test-command' in record: |
892 | raise InvalidControl('*', 'Only one of "Tests" or ' |
893 | '"Test-Command" may be given') |
894 | @@ -426,15 +498,16 @@ def parse_debian_source(srcdir, testbed_caps, testbed_arch, control_path=None, |
895 | _debian_check_unknown_fields(test_names[0], record) |
896 | for n in test_names: |
897 | test = Test(n, os.path.join(test_dir, n), None, |
898 | - restrictions, features, depends, [], []) |
899 | + restrictions, features, depends, [], [], synth_depends) |
900 | test.check_testbed_compat(testbed_caps) |
901 | tests.append(test) |
902 | elif 'Test-command' in record: |
903 | command = record['Test-command'] |
904 | - depends = _parse_debian_depends(command, |
905 | - record.get('Depends', '@'), |
906 | - srcdir, |
907 | - testbed_arch) |
908 | + (depends, synth_depends) = _parse_debian_depends( |
909 | + command, |
910 | + record.get('Depends', '@'), |
911 | + srcdir, |
912 | + testbed_arch) |
913 | if feature_test_name is None: |
914 | command_counter += 1 |
915 | name = 'command%i' % command_counter |
916 | @@ -442,7 +515,7 @@ def parse_debian_source(srcdir, testbed_caps, testbed_arch, control_path=None, |
917 | name = feature_test_name |
918 | _debian_check_unknown_fields(name, record) |
919 | test = Test(name, None, command, restrictions, features, |
920 | - depends, [], []) |
921 | + depends, [], [], synth_depends) |
922 | test.check_testbed_compat(testbed_caps) |
923 | tests.append(test) |
924 | else: |
925 | @@ -532,7 +605,7 @@ def parse_click_manifest(manifest, testbed_caps, clickdeps, use_installed, |
926 | try: |
927 | test = Test(name, desc.get('path'), desc.get('command'), |
928 | desc.get('restrictions', []), desc.get('features', []), |
929 | - desc.get('depends', []), clickdeps, installed_clicks) |
930 | + desc.get('depends', []), clickdeps, installed_clicks, []) |
931 | test.check_testbed_compat(testbed_caps) |
932 | tests.append(test) |
933 | except Unsupported as u: |
934 | diff --git a/runner/autopkgtest b/runner/autopkgtest |
935 | index 90a3fd9..24fd84a 100755 |
936 | --- a/runner/autopkgtest |
937 | +++ b/runner/autopkgtest |
938 | @@ -138,27 +138,60 @@ def setup_trace(): |
939 | def run_tests(tests, tree): |
940 | global errorcode, testbed |
941 | |
942 | + # We should not get here if we have had an error other than skipping |
943 | + # tests |
944 | + assert errorcode in (0, 2), errorcode |
945 | + |
946 | if not tests: |
947 | # if we have skipped tests, don't claim that we don't have any |
948 | - if not errorcode & 2: |
949 | + if errorcode == 0: |
950 | adtlog.report('*', 'SKIP no tests in this package') |
951 | - errorcode |= 8 |
952 | + |
953 | + errorcode = 8 |
954 | return |
955 | |
956 | + any_passed = False |
957 | + |
958 | for t in tests: |
959 | # Set up clean test bed with given dependencies |
960 | adtlog.info('test %s: preparing testbed' % t.name) |
961 | testbed.reset(t.depends, 'needs-recommends' in t.restrictions) |
962 | binaries.publish() |
963 | - testbed.install_deps(t.depends, 'needs-recommends' in t.restrictions, opts.shell_fail) |
964 | + doTest = True |
965 | + try: |
966 | + testbed.install_deps(t.depends, 'needs-recommends' in t.restrictions, opts.shell_fail, t.synth_depends) |
967 | + except adtlog.BadPackageError as e: |
968 | + if 'skip-not-installable' in t.restrictions: |
969 | + errorcode |= 2 |
970 | + adtlog.report(t.name, 'SKIP installation fails and skip-not-installable set') |
971 | + else: |
972 | + errorcode |= 12 |
973 | + adtlog.report(t.name, 'FAIL badpkg') |
974 | + adtlog.preport('blame: ' + ' '.join(blamed)) |
975 | + adtlog.preport('badpkg: ' + str(e)) |
976 | + doTest = False |
977 | + |
978 | + if doTest: |
979 | + testbed.run_test(tree, t, opts.env, opts.shell_fail, opts.shell, |
980 | + opts.build_parallel) |
981 | + if t.skipped: |
982 | + errorcode |= 2 |
983 | + elif not t.result: |
984 | + if 'flaky' in t.restrictions: |
985 | + errorcode |= 2 |
986 | + else: |
987 | + errorcode |= 4 |
988 | + else: |
989 | + any_passed = True |
990 | |
991 | - testbed.run_test(tree, t, opts.env, opts.shell_fail, opts.shell, |
992 | - opts.build_parallel) |
993 | - if not t.result: |
994 | - errorcode |= 4 |
995 | if 'breaks-testbed' in t.restrictions: |
996 | testbed.needs_reset() |
997 | |
998 | + if errorcode in (0, 2) and not any_passed: |
999 | + # If we have skipped or ignored every test, set the same exit |
1000 | + # status as if we didn't have any tests |
1001 | + errorcode = 8 |
1002 | + |
1003 | testbed.needs_reset() |
1004 | |
1005 | |
1006 | @@ -362,11 +395,9 @@ def build_source(kind, arg, built_binaries): |
1007 | # |
1008 | # apt-get source is terribly noisy; only show what gets downloaded |
1009 | # |
1010 | - # apt in Ubuntu 12.04 does not yet understand --only-source, so add a fallback |
1011 | - # |
1012 | # very old source packages don't have Package-List: yet, fall back to Binary: |
1013 | # (Binary: is generally not sufficient as it gets truncated for long lists) |
1014 | - create_command = ('pkgs=$(apt-cache showsrc --only-source %(src)s || [ $? != 100 ] || apt-cache showsrc %(src)s); ' |
1015 | + create_command = ('pkgs=$(apt-cache showsrc --only-source %(src)s); ' |
1016 | 'pkgs=$(echo "$pkgs\n" | awk "/^Package: / { if (\$2 != \\"%(src)s\\") { skippar=1; next; } else { skippar=0}} { if (skippar) next; } /^Binary:/ { sub(/^Binary:/, \\"\\"); gsub(/,/, \\"\\"); split(\$0,oldpkgs)}; /^Package-List:/ { inlist=1; have_pl=1; delete thissrc; if (\$2) thissrc[\$2] = 1; next } (/^ / && inlist == 1) { thissrc[\$1] = 1; next } { if (!inlist) next; inlist=0; if (intersect) {for (p in pkgs) { if (!(p in thissrc)) delete pkgs[p]; else remaining=1}; if (!remaining) {for (p in thissrc) {pkgs[p] = 1}} } else { for (p in thissrc) { pkgs[p] = 1}; intersect=1 } } END {if (have_pl) { for (p in pkgs) print p } else {for (p in oldpkgs) print oldpkgs[p]} }");' |
1017 | ' [ -n "$pkgs" ] || exit 1; ' |
1018 | ' for pkg in $pkgs; do' |
1019 | @@ -460,6 +491,7 @@ def build_source(kind, arg, built_binaries): |
1020 | d = deb822.Deb822(sequence=f) |
1021 | bd = d.get('Build-Depends', '') |
1022 | bdi = d.get('Build-Depends-Indep', '') |
1023 | + bda = d.get('Build-Depends-Arch', '') |
1024 | |
1025 | # determine build command and build-essential packages |
1026 | build_essential = ['build-essential'] |
1027 | @@ -472,7 +504,7 @@ def build_source(kind, arg, built_binaries): |
1028 | if testbed.user or 'root-on-testbed' not in testbed.caps: |
1029 | build_essential += ['fakeroot'] |
1030 | |
1031 | - testbed.satisfy_dependencies_string(bd + ', ' + bdi + ', ' + ', '.join(build_essential), arg, |
1032 | + testbed.satisfy_dependencies_string(bd + ', ' + bdi + ', ' + bda + ', ' + ', '.join(build_essential), arg, |
1033 | build_dep=True, shell_on_failure=opts.shell_fail) |
1034 | |
1035 | # keep patches applied for tests |
1036 | @@ -650,6 +682,7 @@ def main(): |
1037 | setup_commands_boot=opts.setup_commands_boot, |
1038 | add_apt_pockets=opts.apt_pocket, |
1039 | copy_files=opts.copy, |
1040 | + enable_apt_fallback=opts.enable_apt_fallback, |
1041 | add_apt_sources=getattr(opts, 'add_apt_sources', []), |
1042 | add_apt_releases=getattr(opts, 'add_apt_releases', []), |
1043 | pin_packages=opts.pin_packages, |
1044 | diff --git a/runner/autopkgtest.1 b/runner/autopkgtest.1 |
1045 | index 168a7a0..2a98c32 100644 |
1046 | --- a/runner/autopkgtest.1 |
1047 | +++ b/runner/autopkgtest.1 |
1048 | @@ -3,8 +3,9 @@ |
1049 | autopkgtest \- test an installed binary package using the source package's tests |
1050 | .SH SYNOPSIS |
1051 | .B autopkgtest |
1052 | -.IR [ options ...] |
1053 | -.IR "testsrc " [ testbinary "...]" |
1054 | +.RI [ options ...] |
1055 | +.RI [ testbinary ...] |
1056 | +.I testsrc |
1057 | .B \-\- |
1058 | .I virt\-server |
1059 | .RI [ virt\-server\-arg ...] |
1060 | @@ -442,12 +443,15 @@ individual servers for how to use them. |
1061 | .SH OUTPUT FORMAT |
1062 | During a normal test run, one line is printed for each test. This |
1063 | consists of a short string identifying the test, some horizontal |
1064 | -whitespace, and either |
1065 | -.B PASS |
1066 | -or |
1067 | -.BR FAIL " reason" |
1068 | +whitespace, and one of |
1069 | +.BR PASS , |
1070 | +.B FAIL |
1071 | +.IR reason , |
1072 | +.B SKIP |
1073 | +.IR reason , |
1074 | or |
1075 | -.BR SKIP " reason" |
1076 | +.B FLAKY |
1077 | +.I reason |
1078 | where the pass/fail indication is separated by any reason by some |
1079 | horizontal whitespace. |
1080 | |
1081 | @@ -455,6 +459,15 @@ The string to identify the test consists of a short alphanumeric |
1082 | string invented by \fBautopkgtest\fR to distinguish different command-line |
1083 | arguments, the \fIargid\fR, followed by a hyphen and the test name. |
1084 | |
1085 | +.B SKIP |
1086 | +indicates that a test was not run, or that the test code was started |
1087 | +but detected that the test could not complete, for instance because a |
1088 | +required resource was not available. |
1089 | + |
1090 | +.B FLAKY |
1091 | +indicates that a test would ordinarily have failed, but because this |
1092 | +particular test is known to be unreliable, the failure was ignored. |
1093 | + |
1094 | Sometimes a |
1095 | .B SKIP |
1096 | will be reported when the name of the test is not known or not |
1097 | @@ -520,7 +533,7 @@ argument value. |
1098 | .SH EXIT STATUS |
1099 | 0 all tests passed |
1100 | .br |
1101 | -2 at least one test skipped |
1102 | +2 at least one test was skipped (or at least one flaky test failed) |
1103 | .br |
1104 | 4 at least one test failed |
1105 | .br |
1106 | @@ -530,6 +543,8 @@ argument value. |
1107 | .br |
1108 | 12 erroneous package |
1109 | .br |
1110 | +14 erroneous package and at least one test skipped |
1111 | +.br |
1112 | 16 testbed failure |
1113 | .br |
1114 | 20 other unexpected failures including bad usage |
1115 | diff --git a/tests/autopkgtest b/tests/autopkgtest |
1116 | index aa6e431..ceb0ee6 100755 |
1117 | --- a/tests/autopkgtest |
1118 | +++ b/tests/autopkgtest |
1119 | @@ -64,6 +64,12 @@ have_ubuntu_device_flash = subprocess.call(['which', 'ubuntu-device-flash'], |
1120 | have_autodep8 = subprocess.call(['which', 'autodep8'], stdout=subprocess.PIPE) == 0 |
1121 | have_git = subprocess.call(['which', 'git'], stdout=subprocess.PIPE) == 0 |
1122 | |
1123 | +host_arch = subprocess.run(['dpkg-architecture', '-q', 'DEB_HOST_ARCH'], |
1124 | + stdout=subprocess.PIPE, universal_newlines=True).stdout.strip() |
1125 | + |
1126 | +host_os = subprocess.run(['dpkg-architecture', '-q', 'DEB_HOST_ARCH_OS'], |
1127 | + stdout=subprocess.PIPE, universal_newlines=True).stdout.strip() |
1128 | + |
1129 | |
1130 | class AdtTestCase(unittest.TestCase): |
1131 | '''Base class with common test setup''' |
1132 | @@ -158,6 +164,18 @@ class AdtTestCase(unittest.TestCase): |
1133 | (out, err) = p.communicate() |
1134 | return (p.returncode, out.decode('UTF-8', 'replace'), err.decode('UTF-8', 'replace')) |
1135 | |
1136 | + @property |
1137 | + def has_isolation_machine(self): |
1138 | + return self.virt_args[0] in ['qemu', 'null'] |
1139 | + |
1140 | + @property |
1141 | + def has_isolation_container(self): |
1142 | + return self.virt_args[0] not in ['chroot', 'schroot'] |
1143 | + |
1144 | + @property |
1145 | + def can_revert_full_system(self): |
1146 | + return self.virt_args[0] not in ['null', 'chroot', 'schroot'] |
1147 | + |
1148 | |
1149 | class DebTestsAll: |
1150 | '''Common deb tests for all runners''' |
1151 | @@ -189,21 +207,35 @@ class DebTestsAll: |
1152 | # should log package version |
1153 | self.assertIn('testing package testpkg version 1\n', err) |
1154 | |
1155 | + @unittest.skipIf((os.getuid() == 0 and |
1156 | + os.path.exists('/usr/share/doc/aspell-doc')), |
1157 | + 'needs aspell-doc uninstalled if run as root') |
1158 | def test_tree_build_needed_success(self): |
1159 | '''source tree, build-needed restriction, test success''' |
1160 | |
1161 | p = self.build_src('Tests: pass\nDepends: coreutils\nRestrictions: build-needed\n', |
1162 | {'pass': '#!/bin/sh -e\n./test_built | grep -q "built script OK"\n' |
1163 | './test_abspath | grep -q "built script OK"\necho GOOD'}) |
1164 | - # add Build-Depends-Indep: and a build profile package |
1165 | - subprocess.check_call(['sed', '-i', '/^Build-Depends:/ a\Build-Depends-Indep: dpkg-dev, nonexisting <cross>', |
1166 | - os.path.join(p, 'debian', 'control')]) |
1167 | + |
1168 | + if os.getuid() == 0: |
1169 | + # Add Build-Depends-Indep:, Build-Depends-Arch and a build |
1170 | + # profile package. We can only do this if we're root, because |
1171 | + # packages that we unpack non-system-wide as non-root are |
1172 | + # invisible to dpkg-checkbuilddeps so the build would fail. |
1173 | + subprocess.check_call(['sed', '-i', '-e', |
1174 | + '/^Build-Depends:/ a\Build-Depends-Indep: dpkg-dev, nonexisting <cross>', |
1175 | + '-e', |
1176 | + '/^Build-Depends:/ a\Build-Depends-Arch: aspell-doc', |
1177 | + os.path.join(p, 'debian', 'control')]) |
1178 | |
1179 | (code, out, err) = self.runtest(['--no-built-binaries', p]) |
1180 | # test should succeed |
1181 | self.assertEqual(code, 0, out + err) |
1182 | self.assertRegex(out, 'pass\s+PASS', out) |
1183 | |
1184 | + if os.getuid() == 0: |
1185 | + self.assertIn('Unpacking aspell-doc', out) |
1186 | + |
1187 | # should build package |
1188 | self.assertIn('dh build', err) |
1189 | |
1190 | @@ -222,26 +254,34 @@ class DebTestsAll: |
1191 | |
1192 | (code, out, err) = self.runtest(['-B', p]) |
1193 | |
1194 | - if self.virt_args[0] in ['qemu', 'null']: |
1195 | - self.assertEqual(code, 0, out + err) |
1196 | + if self.has_isolation_machine: |
1197 | self.assertRegex(out, 'im\s+PASS', out) |
1198 | self.assertIn('\nmachine ok\n', out) |
1199 | else: |
1200 | - self.assertEqual(code, 2, out + err) |
1201 | self.assertRegex(out, 'im\s+SKIP .*machine', out) |
1202 | self.assertNotIn('machine ok', out) |
1203 | |
1204 | - if self.virt_args[0] in ['chroot', 'schroot']: |
1205 | + if not self.has_isolation_container: |
1206 | self.assertRegex(out, 'ic\s+SKIP .*container', out) |
1207 | self.assertNotIn('container ok', out) |
1208 | else: |
1209 | self.assertRegex(out, 'ic\s+PASS', out) |
1210 | self.assertIn('container ok\n', out) |
1211 | |
1212 | + if self.has_isolation_machine and self.has_isolation_container: |
1213 | + # all tests run |
1214 | + self.assertEqual(code, 0, out + err) |
1215 | + elif self.has_isolation_machine or self.has_isolation_container: |
1216 | + # one test run, one skipped |
1217 | + self.assertEqual(code, 2, out + err) |
1218 | + else: |
1219 | + # all skipped |
1220 | + self.assertEqual(code, 8, out + err) |
1221 | + |
1222 | def test_breaks_testbed(self): |
1223 | '''breaks-testbed restriction''' |
1224 | |
1225 | - p = self.build_src('Tests: zap boom\nDepends:\nRestrictions: needs-root breaks-testbed', |
1226 | + p = self.build_src('Tests: zap boom\nDepends:\nRestrictions: allow-stderr needs-root breaks-testbed', |
1227 | {'zap': '#!/bin/sh\ntouch /zap\n' |
1228 | '[ -d "$HOME" ] || echo "HOME not set" >&2\n' |
1229 | 'apt-get install -y aspell-doc\n', |
1230 | @@ -249,9 +289,9 @@ class DebTestsAll: |
1231 | |
1232 | (code, out, err) = self.runtest(['--no-built-binaries', p, '-d']) |
1233 | |
1234 | - if self.virt_args[0] in ['null', 'chroot', 'schroot']: |
1235 | + if not self.can_revert_full_system: |
1236 | # test should be skipped as these runners doesn't provide revert-full-system |
1237 | - self.assertEqual(code, 2, err) |
1238 | + self.assertEqual(code, 8, err) |
1239 | self.assertRegex(out, 'zap\s+SKIP Test breaks testbed') |
1240 | # FIXME: we should see this too! |
1241 | # self.assertRegex(out, 'boom\s+SKIP Test breaks testbed') |
1242 | @@ -264,6 +304,22 @@ class DebTestsAll: |
1243 | self.assertRegex(out, 'boom\s+PASS') |
1244 | self.assertIn('Unpacking aspell-doc', out) |
1245 | |
1246 | + def test_flaky_success(self): |
1247 | + '''A flaky test succeeds''' |
1248 | + p = self.build_src('Tests: unreproducible\nRestrictions: flaky\nDepends: coreutils\n', |
1249 | + {'unreproducible': '#!/bin/sh\necho I am fine\n'}) |
1250 | + (code, out, err) = self.runtest(['-d', '--no-built-binaries', p]) |
1251 | + self.assertEqual(code, 0, err) |
1252 | + self.assertRegex(out, 'unreproducible\s+PASS', out) |
1253 | + |
1254 | + def test_skippable_success(self): |
1255 | + '''A skippable test succeeds''' |
1256 | + p = self.build_src('Tests: needs-magic\nRestrictions: skippable\nDepends: coreutils\n', |
1257 | + {'needs-magic': '#!/bin/sh\necho I am fine\n'}) |
1258 | + (code, out, err) = self.runtest(['-d', '--no-built-binaries', p]) |
1259 | + self.assertEqual(code, 0, err) |
1260 | + self.assertRegex(out, 'needs-magic\s+PASS', out) |
1261 | + |
1262 | |
1263 | class DebTestsFailureModes: |
1264 | '''Common deb tests for handling various failure modes |
1265 | @@ -359,6 +415,59 @@ class DebTestsFailureModes: |
1266 | # no restricted dependencies functionality as they are already installed |
1267 | self.assertNotIn('will only work for some packages', err) |
1268 | |
1269 | + def test_flaky_fail(self): |
1270 | + '''A flaky test fails''' |
1271 | + p = self.build_src('Tests: unreproducible\nRestrictions: flaky\nDepends: coreutils\n', |
1272 | + {'unreproducible': '#!/bin/sh\nexit 1\n'}) |
1273 | + (code, out, err) = self.runtest(['-d', '--no-built-binaries', p]) |
1274 | + self.assertEqual(code, 8, err) |
1275 | + self.assertRegex(out, 'unreproducible\s+FLAKY', out) |
1276 | + |
1277 | + def test_flaky_fail_mixed(self): |
1278 | + '''A flaky test fails, but another test passes''' |
1279 | + p = self.build_src('Tests: unreproducible ok\nRestrictions: flaky\nDepends: coreutils\n', |
1280 | + {'unreproducible': '#!/bin/sh\nexit 1\n', |
1281 | + 'ok': '#!/bin/sh\n'}) |
1282 | + (code, out, err) = self.runtest(['-d', '--no-built-binaries', p]) |
1283 | + self.assertEqual(code, 2, err) |
1284 | + self.assertRegex(out, 'unreproducible\s+FLAKY', out) |
1285 | + self.assertRegex(out, 'ok\s+PASS', out) |
1286 | + |
1287 | + def test_skippable_skipped(self): |
1288 | + '''A skippable test skips itself''' |
1289 | + p = self.build_src('Tests: needs-magic\nRestrictions: skippable\nDepends: coreutils\n', |
1290 | + {'needs-magic': '#!/bin/sh\necho Cannot run >&2\nexit 77\n'}) |
1291 | + (code, out, err) = self.runtest(['-d', '--no-built-binaries', p]) |
1292 | + self.assertEqual(code, 8, err) |
1293 | + self.assertRegex(out, 'needs-magic\s+SKIP', out) |
1294 | + |
1295 | + def test_skippable_skipped_mixed(self): |
1296 | + '''A skippable test skips itself, but another test passes''' |
1297 | + p = self.build_src('Tests: needs-magic ok\nRestrictions: skippable\nDepends: coreutils\n', |
1298 | + {'needs-magic': '#!/bin/sh\necho Cannot run >&2\nexit 77\n', |
1299 | + 'ok': '#!/bin/sh\n'}) |
1300 | + (code, out, err) = self.runtest(['-d', '--no-built-binaries', p]) |
1301 | + self.assertEqual(code, 2, err) |
1302 | + self.assertRegex(out, 'needs-magic\s+SKIP', out) |
1303 | + self.assertRegex(out, 'ok\s+PASS', out) |
1304 | + |
1305 | + def test_skippable_fail(self): |
1306 | + '''A skippable test fails''' |
1307 | + p = self.build_src('Tests: needs-magic\nRestrictions: skippable\nDepends: coreutils\n', |
1308 | + {'needs-magic': '#!/bin/sh\nexit 1\n'}) |
1309 | + (code, out, err) = self.runtest(['-d', '--no-built-binaries', p]) |
1310 | + self.assertEqual(code, 4, err) |
1311 | + self.assertRegex(out, 'needs-magic\s+FAIL non-zero exit status 1', out) |
1312 | + |
1313 | + def test_unskippable(self): |
1314 | + '''A non-skippable test fails with an exit status that happens |
1315 | + to match the skippable API.''' |
1316 | + p = self.build_src('Tests: no-magic\nDepends: coreutils\n', |
1317 | + {'no-magic': '#!/bin/sh\nexit 77\n'}) |
1318 | + (code, out, err) = self.runtest(['-d', '--no-built-binaries', p]) |
1319 | + self.assertEqual(code, 4, err) |
1320 | + self.assertRegex(out, 'no-magic\s+FAIL non-zero exit status 77', out) |
1321 | + |
1322 | |
1323 | class NullRunner(AdtTestCase, DebTestsAll, DebTestsFailureModes): |
1324 | def __init__(self, *args, **kwargs): |
1325 | @@ -1399,8 +1508,69 @@ if ($pid) { # parent |
1326 | |
1327 | p = self.build_src('Test-Command: true\nDepends:\nRestrictions: needs-reboot', {}) |
1328 | (code, out, err) = self.runtest(['-d', '-B', p]) |
1329 | + self.assertEqual(code, 8, err) |
1330 | + self.assertRegex(out, 'command1\s+SKIP Test needs to reboot testbed but testbed does not provide reboot capability') |
1331 | + |
1332 | + def test_reboot_mixed(self): |
1333 | + '''needs-reboot test gets skipped but another test is still run''' |
1334 | + |
1335 | + p = self.build_src('Test-Command: true 1\nDepends:\nRestrictions: needs-reboot\n' |
1336 | + '\n' |
1337 | + 'Test-Command: true 2\nDepends:\n', |
1338 | + {}) |
1339 | + (code, out, err) = self.runtest(['-d', '-B', p]) |
1340 | self.assertEqual(code, 2, err) |
1341 | self.assertRegex(out, 'command1\s+SKIP Test needs to reboot testbed but testbed does not provide reboot capability') |
1342 | + self.assertRegex(out, 'command2\s+PASS') |
1343 | + |
1344 | + def test_broken_test_deps(self): |
1345 | + '''unsatisfiable test dependencies''' |
1346 | + |
1347 | + p = self.build_src('Tests: p\nDepends: unknown, libc6 (>= 99:99)\n', |
1348 | + {'p': '#!/bin/sh -e\ntrue'}) |
1349 | + |
1350 | + (code, out, err) = self.runtest(['--no-built-binaries', p]) |
1351 | + self.assertEqual(code, 12, err) |
1352 | + self.assertIn('Test dependencies are unsatisfiable', err) |
1353 | + self.assertIn('Test dependencies are unsatisfiable', out) |
1354 | + |
1355 | + def test_continue_with_other_tests(self): |
1356 | + '''Install failure should continue with next test''' |
1357 | + |
1358 | + p = self.build_src('Test-Command: false\nDepends:\n\nTest-Command: true\nDepends:unknown\n\nTest-Command: true\nDepends:\n', {}) |
1359 | + |
1360 | + summary = os.path.join(self.workdir, 'summary.log') |
1361 | + |
1362 | + (code, out, err) = self.runtest(['--debug', '--no-built-binaries', p, '--summary=' + summary]) |
1363 | + self.assertEqual(code, 12, err) |
1364 | + self.assertRegex(out, 'command1\s+FAIL') |
1365 | + self.assertRegex(out, 'command2\s+FAIL') |
1366 | + self.assertRegex(out, 'command3\s+PASS') |
1367 | + |
1368 | + def test_skip_not_installable(self): |
1369 | + '''Test skip-not-installable restriction''' |
1370 | + p = self.build_src('Tests: notinstallable\n' |
1371 | + 'Restrictions: skip-not-installable\n' |
1372 | + 'Depends: nonexisting\n', |
1373 | + {'notinstallable': '#!/bin/sh\necho But I am fine\n'}) |
1374 | + (code, out, err) = self.runtest(['-d', '--no-built-binaries', p]) |
1375 | + self.assertEqual(code, 8, err) |
1376 | + self.assertRegex(out, 'notinstallable\s+SKIP installation fails', out) |
1377 | + |
1378 | + def test_skip_not_installable_mixed(self): |
1379 | + '''Test skip-not-installable restriction combined with a passing test''' |
1380 | + p = self.build_src('Tests: notinstallable\n' |
1381 | + 'Restrictions: skip-not-installable\n' |
1382 | + 'Depends: nonexisting\n' |
1383 | + '\n' |
1384 | + 'Tests: ok\n' |
1385 | + 'Depends:\n', |
1386 | + {'notinstallable': '#!/bin/sh\necho But I am fine\n', |
1387 | + 'ok': '#!/bin/sh\nexit 0\n'}) |
1388 | + (code, out, err) = self.runtest(['-d', '--no-built-binaries', p]) |
1389 | + self.assertEqual(code, 2, err) |
1390 | + self.assertRegex(out, 'notinstallable\s+SKIP installation fails', out) |
1391 | + self.assertRegex(out, 'ok\s+PASS', out) |
1392 | |
1393 | |
1394 | @unittest.skipIf(os.getuid() > 0, |
1395 | @@ -1629,7 +1799,8 @@ Restrictions: needs-root |
1396 | |
1397 | # add extra build dependencies to testpkg |
1398 | subprocess.check_call(['sed', '-i', '/^Build-Depends:/ s/:.*/: bdep1 , bdep2,\\n bdep3, # moo\\n#comment\\n bdep4\\n' |
1399 | - 'Build-Depends-Indep: bdep5/', |
1400 | + 'Build-Depends-Indep: bdep5\\n' |
1401 | + 'Build-Depends-Arch: bdep6/', |
1402 | os.path.join(p, 'debian', 'control')]) |
1403 | |
1404 | # run this under C locale to test that UTF-8 debian/control is still |
1405 | @@ -1639,13 +1810,14 @@ Restrictions: needs-root |
1406 | self.assertEqual(code, 0, err) |
1407 | self.assertRegex(out, 'pass\s+PASS') |
1408 | |
1409 | - self.assertIn('synthesised dependency testpkg (>= 0~)\n', err) |
1410 | + self.assertIn('synthesised dependency testpkg\n', err) |
1411 | self.assertIn('processing dependency testdep1\n', err) |
1412 | self.assertIn('synthesised dependency bdep1\n', err) |
1413 | self.assertIn('synthesised dependency bdep2\n', err) |
1414 | self.assertIn('synthesised dependency bdep3\n', err) |
1415 | self.assertIn('synthesised dependency bdep4\n', err) |
1416 | self.assertIn('synthesised dependency bdep5\n', err) |
1417 | + self.assertIn('synthesised dependency bdep6\n', err) |
1418 | self.assertIn('synthesised dependency build-essential\n', err) |
1419 | self.assertIn('processing dependency testdep2\n', err) |
1420 | |
1421 | @@ -3094,9 +3266,9 @@ class SchrootRunner(AdtTestCase, DebTestsVirtFS, DebTestsFailureModes): |
1422 | (code, out, err) = self.runtest(['--no-built-binaries', p]) |
1423 | self.assertEqual(code, 12, err) |
1424 | self.assertRegex(out, '[dD]epends.*unknown', err) |
1425 | - self.assertRegex(err, 'Test dependencies are unsatisfiable', err) |
1426 | + self.assertIn('Test dependencies are unsatisfiable', err) |
1427 | self.assertRegex(out, 'blame: .*/testpkg', out) |
1428 | - self.assertRegex(out, 'Test dependencies are unsatisfiable', out) |
1429 | + self.assertIn('Test dependencies are unsatisfiable', out) |
1430 | |
1431 | def test_install_failure(self): |
1432 | '''test dependency install failure''' |
1433 | @@ -3110,7 +3282,7 @@ class SchrootRunner(AdtTestCase, DebTestsVirtFS, DebTestsFailureModes): |
1434 | self.assertEqual(code, 12, err) |
1435 | self.assertIn('BROKEN POSTINST', out) |
1436 | self.assertRegex(out, 'blame: .*/testpkg', out) |
1437 | - self.assertRegex(out, 'badpkg: Test dependencies are unsatisfiable', out) |
1438 | + self.assertIn('badpkg: Test dependencies are unsatisfiable', out) |
1439 | |
1440 | def do_apt_pocket_test(self, pocket_args, ver_checks, |
1441 | add_deb_src=False, expect_success=True, |
1442 | @@ -3197,7 +3369,7 @@ class SchrootRunner(AdtTestCase, DebTestsVirtFS, DebTestsFailureModes): |
1443 | # FIXME: our current apt pinning does not resolve this properly, so |
1444 | # this will re-try with the entirety of -proposed |
1445 | self.assertIn('WARNING: Test dependencies are unsatisfiable with using apt pinning.', err) |
1446 | - self.assertIn('Retrying with using all packages from proposed\n', err) |
1447 | + self.assertIn('Retrying with using all packages from testy-proposed\n', err) |
1448 | |
1449 | def test_apt_pocket_multi_nonexisting_dep(self): |
1450 | '''--apt-pocket with multiple tests and a nonexisting dependency''' |
1451 | @@ -3219,7 +3391,16 @@ Depends: nonexisting''' |
1452 | self.assertRegex(out, 'command1\s+PASS', out) |
1453 | self.assertNotIn(out, 'command2') |
1454 | self.assertIn('WARNING: Test dependencies are unsatisfiable with using apt pinning.', err) |
1455 | - self.assertIn('ERROR: erroneous package: Test dependencies are unsatisfiable', err) |
1456 | + self.assertIn('badpkg: Test dependencies are unsatisfiable', err) |
1457 | + |
1458 | + def test_apt_pocket_no_fallback_fail(self): |
1459 | + '''--apt-pocket selecting package from -proposed with dep from -proposed with --no-apt-fallback''' |
1460 | + |
1461 | + (code, out, err) = self.do_apt_pocket_test( |
1462 | + ['--apt-pocket=proposed=vanilla', '--no-apt-fallback'], |
1463 | + '[ "$V" = 2 ]; [ "$C" = 2 ]; [ "$M" = 2 ]; [ "$H" = 2 ]; [ "$L" = 2 ]', |
1464 | + expect_success=False) |
1465 | + self.assertIn('Test dependencies are unsatisfiable.', err) |
1466 | |
1467 | def test_apt_pocket_nbs(self): |
1468 | '''--apt-pocket with only NBS binaries in unstable''' |
1469 | @@ -3273,7 +3454,7 @@ Depends: nonexisting''' |
1470 | '--apt-upgrade', |
1471 | '--debug', 'flavor']) |
1472 | # we cannot actually download the package, but it should get as far as trying |
1473 | - self.assertTrue('apt-get source -q --only-source flavor=3' in err, err) |
1474 | + self.assertTrue('apt-get source -d -q --only-source flavor=3' in err, err) |
1475 | self.assertEqual(code, 12, err) |
1476 | |
1477 | def test_git_source_build(self): |
1478 | @@ -3281,7 +3462,7 @@ Depends: nonexisting''' |
1479 | |
1480 | (code, out, err) = self.runtest( |
1481 | ['--env=DEB_BUILD_OPTIONS=nocheck', |
1482 | - 'git://anonscm.debian.org/autopkgtest/autopkgtest.git']) |
1483 | + 'https://salsa.debian.org/ci-team/autopkgtest.git']) |
1484 | # test should succeed, but skip the Lxd test (if present) |
1485 | self.assertIn(code, [0, 2], err) |
1486 | self.assertRegex(err, 'autopkgtest\s+PASS') |
1487 | @@ -3359,6 +3540,174 @@ Depends: nonexisting''' |
1488 | # should build package |
1489 | self.assertIn('dh build', err) |
1490 | |
1491 | + def _build_testprovider(self, provision, arch='all'): |
1492 | + '''mangle a 'testprovider' package from the testpkg sources''' |
1493 | + |
1494 | + pr = self.build_src('Tests: pass', |
1495 | + {}, |
1496 | + 'testprovider') |
1497 | + |
1498 | + subprocess.check_call(['sed', '-i', 's/testpkg/testprovider/g'] + |
1499 | + [os.path.join(pr, 'debian', f) for f in ('control', 'changelog', 'links')]) |
1500 | + |
1501 | + # fix conflicting files |
1502 | + subprocess.check_call(['sed', '-i', 's,/usr/bin,/usr/share/testprovider,'] + |
1503 | + [os.path.join(pr, 'Makefile')]) |
1504 | + |
1505 | + # add a (possibly versioned) Provides |
1506 | + subprocess.check_call(['sed', '-i', '/^Depends:/ a\Provides: %s' % provision, |
1507 | + os.path.join(pr, 'debian', 'control')]) |
1508 | + |
1509 | + if arch != 'all': |
1510 | + subprocess.check_call(['sed', '-i', 's/Architecture: all/Architecture: %s/' % arch, |
1511 | + os.path.join(pr, 'debian', 'control')]) |
1512 | + subprocess.check_call(['dpkg-buildpackage', '-b', '-us', '-uc', '-tc'], |
1513 | + stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
1514 | + cwd=pr) |
1515 | + |
1516 | + shutil.rmtree(pr) |
1517 | + |
1518 | + if arch == 'all': |
1519 | + deb = os.path.join(os.path.dirname(pr), 'testprovider_1_all.deb') |
1520 | + else: |
1521 | + deb = os.path.join(os.path.dirname(pr), 'testprovider_1_' + host_arch + '.deb') |
1522 | + return deb |
1523 | + |
1524 | + def _test_provides(self, provision, test_dependency, arch='all', control_extra='', expect_failure=False): |
1525 | + '''internal function to test different combinations of provides support''' |
1526 | + |
1527 | + testprovider_deb = self._build_testprovider(provision, arch) |
1528 | + |
1529 | + # we check that the testprovider package isn't enough to satisfy |
1530 | + # testpkg's test-dependency on itself via 'test_dependency' |
1531 | + # even though testprovider Provides: a versioned testpkg |
1532 | + p = self.build_src('Tests: pass\nDepends: testprovider, %s' % test_dependency, |
1533 | + {'pass': '#!/bin/sh -e\ndpkg-query -W -f\${Status} testpkg | grep -q ^install'}) |
1534 | + |
1535 | + if arch != 'all': |
1536 | + subprocess.check_call(['sed', '-i', 's/Architecture: all/Architecture: %s/' % arch, |
1537 | + os.path.join(p, 'debian', 'control')]) |
1538 | + |
1539 | + extradebs = [] |
1540 | + if control_extra: |
1541 | + with open(os.path.join(p, 'debian', 'control'), 'a') as f: |
1542 | + f.write('\n' + control_extra) |
1543 | + extradebs = [os.path.join(os.path.dirname(p), pkg + '_1_all.deb') |
1544 | + for pkg in re.findall('^Package: (.+)', control_extra, re.MULTILINE)] |
1545 | + |
1546 | + subprocess.check_call(['dpkg-buildpackage', '-b', '-us', '-uc', '-tc'], |
1547 | + stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
1548 | + cwd=p) |
1549 | + |
1550 | + if arch == 'all': |
1551 | + testpkg_deb = os.path.join(os.path.dirname(p), 'testpkg_1_all.deb') |
1552 | + else: |
1553 | + testpkg_deb = os.path.join(os.path.dirname(p), 'testpkg_1_' + host_arch + '.deb') |
1554 | + |
1555 | + failed = False |
1556 | + (code, out, err) = self.runtest([p, testpkg_deb, testprovider_deb] + extradebs) |
1557 | + try: |
1558 | + self.assertEqual(code, 0, out + err) |
1559 | + self.assertRegex(out, 'pass\s+PASS', out) |
1560 | + except BaseException: |
1561 | + if not expect_failure: |
1562 | + raise |
1563 | + failed = True |
1564 | + |
1565 | + shutil.rmtree(p) |
1566 | + |
1567 | + # now check that testpkg gets installed even if testprovider already is |
1568 | + p = self.build_src('Tests: pass\nDepends: %s' % test_dependency, |
1569 | + {'pass': '#!/bin/sh -e\ndpkg-query -W -f\${Status} testpkg | grep -q ^install'}) |
1570 | + if control_extra: |
1571 | + with open(os.path.join(p, 'debian', 'control'), 'a') as f: |
1572 | + f.write('\n' + control_extra) |
1573 | + |
1574 | + tb_deb = '/tmp/provider.deb' |
1575 | + |
1576 | + (code, out, err) = self.runtest(['--copy', '%s:%s' % (testprovider_deb, tb_deb), |
1577 | + '--setup-commands', 'dpkg -i %s' % tb_deb, p]) |
1578 | + try: |
1579 | + self.assertEqual(code, 0, out + err) |
1580 | + self.assertRegex(out, 'pass\s+PASS', out) |
1581 | + except BaseException: |
1582 | + if not expect_failure: |
1583 | + raise |
1584 | + failed = True |
1585 | + |
1586 | + self.assertTrue(failed or not expect_failure) |
1587 | + |
1588 | + shutil.rmtree(p) |
1589 | + |
1590 | + def test_unversioned_provides_with_at(self): |
1591 | + '''test that unversioned provides do not satisfy test dependencies on "@"''' |
1592 | + self._test_provides('testpkg', '@') |
1593 | + |
1594 | + def test_unversioned_provides_with_unversioned_explicit(self): |
1595 | + '''test that unversioned provides do not satisfy unversioned test dependencies on binary package from the same source''' |
1596 | + self._test_provides('testpkg', 'testpkg') |
1597 | + |
1598 | + # this is somewhat silly but included for completeness |
1599 | + def test_unversioned_provides_with_versioned_explicit(self): |
1600 | + '''test that unversioned provides do not satisfy versioned test dependencies on binary package from the same source''' |
1601 | + self._test_provides('testpkg', 'testpkg (>= 0.5)') |
1602 | + |
1603 | + def test_versioned_provides_with_at(self): |
1604 | + '''test that versioned provides do not satisfy test dependencies on "@"''' |
1605 | + self._test_provides('testpkg (= 0.5)', '@') |
1606 | + |
1607 | + def test_versioned_provides_with_unversioned_explicit(self): |
1608 | + '''test that versioned provides do not satisfy unversioned test dependencies on binary package from the same source''' |
1609 | + self._test_provides('testpkg (= 0.5)', 'testpkg') |
1610 | + |
1611 | + def test_versioned_provides_with_versioned_explicit(self): |
1612 | + '''test that versioned provides do not satisfy versioned test dependencies on binary package from the same source''' |
1613 | + self._test_provides('testpkg (= 0.5)', 'testpkg (>= 0.5)') |
1614 | + |
1615 | + def test_versioned_provides_with_at_arch_any(self): |
1616 | + '''test that versioned provides do not satisfy test dependencies on "@" with arch:any binary packages''' |
1617 | + self._test_provides('testpkg (= 0.5)', '@', arch='any') |
1618 | + |
1619 | + def test_versioned_provides_with_at_arch_multiple(self): |
1620 | + '''test that versioned provides do not satisfy test dependencies on "@" with <os>-any + hurd-any binary packages''' |
1621 | + self._test_provides('testpkg (= 0.5)', '@', arch=host_os + '-any hurd-any') |
1622 | + |
1623 | + def test_versioned_provides_with_unversioned_explicit_on_not_hurd(self): |
1624 | + '''test that versioned provides do not satisfy test dependencies on !hurd-any''' |
1625 | + self._test_provides('testpkg (= 0.5)', 'testpkg [!hurd-any]') |
1626 | + |
1627 | + def test_versioned_provides_with_unversioned_explicit_on_host_arch(self): |
1628 | + '''test that versioned provides do not satisfy test dependencies on <host_arch>''' |
1629 | + self._test_provides('testpkg (= 0.5)', 'testpkg [' + host_arch + ']') |
1630 | + |
1631 | + def test_unversioned_provides_with_conflicting_alternatives(self): |
1632 | + '''test that unversioned provides do not satisfy test dependencies on conflicting alternatives from the same source''' |
1633 | + testpkg2 = '\nPackage: testpkg2\nArchitecture: all\nDescription: test package 2\nConflicts: testpkg' |
1634 | + self._test_provides('testpkg', 'testpkg | testpkg2', control_extra=testpkg2) |
1635 | + |
1636 | + def test_versioned_provides_with_conflicting_alternatives(self): |
1637 | + '''test that versioned provides do not satisfy test dependencies on conflicting alternatives from the same source''' |
1638 | + testpkg2 = '\nPackage: testpkg2\nArchitecture: all\nDescription: test package 2\nConflicts: testpkg' |
1639 | + self._test_provides('testpkg (= 0.5)', 'testpkg | testpkg2', control_extra=testpkg2) |
1640 | + |
1641 | + def test_unversioned_provides_with_nonconflicting_alternatives(self): |
1642 | + '''test that unversioned provides do not satisfy test dependencies on not conflicting alternatives from the same source''' |
1643 | + testpkg2 = '\nPackage: testpkg2\nArchitecture: all\nDescription: test package 2' |
1644 | + self._test_provides('testpkg', 'testpkg | testpkg2', control_extra=testpkg2) |
1645 | + |
1646 | + def test_versioned_provides_with_nonconflicting_alternatives(self): |
1647 | + '''test that versioned provides do not satisfy test dependencies on not conflicting alternatives from the same source''' |
1648 | + testpkg2 = '\nPackage: testpkg2\nArchitecture: all\nDescription: test package 2' |
1649 | + self._test_provides('testpkg (= 0.5)', 'testpkg | testpkg2', control_extra=testpkg2) |
1650 | + |
1651 | + def test_unversioned_provides_with_unrelated_alternatives(self): |
1652 | + '''test that unversioned provides satisfy test dependencies on alternatives not all from the same source''' |
1653 | + self._test_provides('testpkg', 'testpkg | dpkg', expect_failure=True) |
1654 | + |
1655 | + def test_versioned_provides_with_unrelated_alternatives(self): |
1656 | + '''test that versioned provides satisfy test dependencies on alternatives not all from the same source''' |
1657 | + self._test_provides('testpkg (= 0.5)', 'testpkg | dpkg', expect_failure=True) |
1658 | + |
1659 | |
1660 | @unittest.skipUnless('AUTOPKGTEST_TEST_SCHROOT_CLICK' in os.environ, |
1661 | 'Set $AUTOPKGTEST_TEST_SCHROOT_CLICK to an existing schroot') |
1662 | diff --git a/tests/testdesc b/tests/testdesc |
1663 | index b749018..4a51f44 100755 |
1664 | --- a/tests/testdesc |
1665 | +++ b/tests/testdesc |
1666 | @@ -108,7 +108,7 @@ class Test(unittest.TestCase): |
1667 | '''valid Test instantiation with path''' |
1668 | |
1669 | t = testdesc.Test('foo', 'tests/do_foo', None, ['needs-root'], |
1670 | - ['unknown_feature'], ['coreutils >= 7'], [], []) |
1671 | + ['unknown_feature'], ['coreutils >= 7'], [], [], []) |
1672 | self.assertEqual(t.name, 'foo') |
1673 | self.assertEqual(t.path, 'tests/do_foo') |
1674 | self.assertEqual(t.command, None) |
1675 | @@ -120,7 +120,7 @@ class Test(unittest.TestCase): |
1676 | '''valid Test instantiation with command''' |
1677 | |
1678 | t = testdesc.Test('foo', None, 'echo hi', ['needs-root'], |
1679 | - ['unknown_feature'], ['coreutils >= 7'], [], []) |
1680 | + ['unknown_feature'], ['coreutils >= 7'], [], [], []) |
1681 | self.assertEqual(t.name, 'foo') |
1682 | self.assertEqual(t.path, None) |
1683 | self.assertEqual(t.command, 'echo hi') |
1684 | @@ -130,7 +130,7 @@ class Test(unittest.TestCase): |
1685 | '''Test with invalid name''' |
1686 | |
1687 | with self.assertRaises(testdesc.Unsupported) as cm: |
1688 | - testdesc.Test('foo/bar', 'do_foo', None, [], [], [], [], []) |
1689 | + testdesc.Test('foo/bar', 'do_foo', None, [], [], [], [], [], []) |
1690 | self.assertIn('may not contain /', str(cm.exception)) |
1691 | |
1692 | def test_unknown_restriction(self): |
1693 | @@ -138,28 +138,28 @@ class Test(unittest.TestCase): |
1694 | |
1695 | with self.assertRaises(testdesc.Unsupported) as cm: |
1696 | testdesc.Test('foo', 'tests/do_foo', None, ['needs-red'], [], [], |
1697 | - [], []) |
1698 | + [], [], []) |
1699 | self.assertIn('unknown restriction needs-red', str(cm.exception)) |
1700 | |
1701 | def test_neither_path_nor_command(self): |
1702 | '''Test without path nor command''' |
1703 | |
1704 | with self.assertRaises(testdesc.InvalidControl) as cm: |
1705 | - testdesc.Test('foo', None, None, [], [], [], [], []) |
1706 | + testdesc.Test('foo', None, None, [], [], [], [], [], []) |
1707 | self.assertIn('either path or command', str(cm.exception)) |
1708 | |
1709 | def test_both_path_and_command(self): |
1710 | '''Test with path and command''' |
1711 | |
1712 | with self.assertRaises(testdesc.InvalidControl) as cm: |
1713 | - testdesc.Test('foo', 'do_foo', 'echo hi', [], [], [], [], []) |
1714 | + testdesc.Test('foo', 'do_foo', 'echo hi', [], [], [], [], [], []) |
1715 | self.assertIn('either path or command', str(cm.exception)) |
1716 | |
1717 | def test_capabilities_compat(self): |
1718 | '''Test compatibility with testbed capabilities''' |
1719 | |
1720 | t = testdesc.Test('foo', 'tests/do_foo', None, |
1721 | - ['needs-root', 'isolation-container'], [], [], [], []) |
1722 | + ['needs-root', 'isolation-container'], [], [], [], [], []) |
1723 | |
1724 | self.assertRaises(testdesc.Unsupported, |
1725 | t.check_testbed_compat, ['isolation-container']) |
1726 | @@ -221,7 +221,7 @@ class Debian(unittest.TestCase): |
1727 | for t in ts: |
1728 | self.assertEqual(t.restrictions, []) |
1729 | self.assertEqual(t.features, []) |
1730 | - self.assertEqual(t.depends, ['one (>= 0~)', 'two (>= 0~)']) |
1731 | + self.assertEqual(t.depends, ['one', 'two']) |
1732 | self.assertFalse(skipped) |
1733 | |
1734 | def test_arch_specific(self): |
1735 | @@ -238,8 +238,8 @@ class Debian(unittest.TestCase): |
1736 | self.assertEqual(ts[0].restrictions, []) |
1737 | self.assertEqual(ts[0].features, []) |
1738 | self.assertEqual(ts[0].depends, |
1739 | - ['one (>= 0~)', 'two (>= 0~) [linux-any]', |
1740 | - 'three (>= 0~) [c64 vax]']) |
1741 | + ['one', 'two [linux-any]', |
1742 | + 'three [c64 vax]']) |
1743 | self.assertFalse(skipped) |
1744 | |
1745 | def test_test_name_feature(self): |
1746 | @@ -375,10 +375,12 @@ class Debian(unittest.TestCase): |
1747 | 'Tests: t\nDepends: @, @builddeps@, foo (>= 7)', |
1748 | 'Source: nums\nBuild-Depends: bd1, bd2 [armhf], bd3:native (>= 7) | bd4 [linux-any]\n' |
1749 | 'Build-Depends-Indep: bdi1, bdi2 [amd64]\n' |
1750 | + 'Build-Depends-Arch: bda1, bda2 [amd64]\n' |
1751 | '\n' |
1752 | 'Package: one\nArchitecture: any') |
1753 | - self.assertEqual(ts[0].depends, ['one (>= 0~)', 'bd1', 'bd3:native (>= 7) | bd4', |
1754 | - 'bdi1', 'bdi2', 'build-essential', 'foo (>= 7)']) |
1755 | + self.assertEqual(ts[0].depends, ['one', 'bd1', 'bd3:native (>= 7) | bd4', |
1756 | + 'bdi1', 'bdi2', 'bda1', 'bda2', |
1757 | + 'build-essential', 'foo (>= 7)']) |
1758 | self.assertFalse(skipped) |
1759 | |
1760 | @unittest.skipUnless(have_dpkg_build_profiles, |
1761 | @@ -391,7 +393,7 @@ class Debian(unittest.TestCase): |
1762 | 'Source: nums\nBuild-Depends: bd1, bd2 <!check>, bd3 <!cross>, bdnotme <stage1> <cross>\n' |
1763 | '\n' |
1764 | 'Package: one\nArchitecture: any') |
1765 | - self.assertEqual(ts[0].depends, ['one (>= 0~)', 'bd1', 'bd2', 'bd3', 'build-essential']) |
1766 | + self.assertEqual(ts[0].depends, ['one', 'bd1', 'bd2', 'bd3', 'build-essential']) |
1767 | self.assertFalse(skipped) |
1768 | |
1769 | def test_complex_deps(self): |
1770 | @@ -401,7 +403,7 @@ class Debian(unittest.TestCase): |
1771 | 'Tests: t\nDepends: @,\n foo (>= 7) [linux-any],\n' |
1772 | ' bd3:native (>= 4) | bd4 [armhf megacpu],\n', |
1773 | 'Source: nums\n\nPackage: one\nArchitecture: any') |
1774 | - self.assertEqual(ts[0].depends, ['one (>= 0~)', 'foo (>= 7) [linux-any]', |
1775 | + self.assertEqual(ts[0].depends, ['one', 'foo (>= 7) [linux-any]', |
1776 | 'bd3:native (>= 4) | bd4 [armhf megacpu]']) |
1777 | self.assertFalse(skipped) |
1778 | |
1779 | @@ -433,7 +435,7 @@ class Debian(unittest.TestCase): |
1780 | ' , bd2\n' |
1781 | '\n' |
1782 | 'Package: one\nArchitecture: any') |
1783 | - self.assertEqual(ts[0].depends, ['one (>= 0~)', 'bd1', 'bd2', 'build-essential']) |
1784 | + self.assertEqual(ts[0].depends, ['one', 'bd1', 'bd2', 'build-essential']) |
1785 | self.assertFalse(skipped) |
1786 | |
1787 | @patch('adtlog.report') |
1788 | @@ -565,7 +567,7 @@ class Debian(unittest.TestCase): |
1789 | self.assertGreaterEqual(len(ts), 1) |
1790 | self.assertIn('pkg-perl-autopkgtest', ts[0].command) |
1791 | self.assertIn('pkg-perl-autopkgtest', ts[0].depends) |
1792 | - self.assertIn('libfoo-perl (>= 0~)', ts[0].depends) |
1793 | + self.assertIn('libfoo-perl', ts[0].depends) |
1794 | else: |
1795 | self.assertEqual(len(ts), 0) |
1796 | |
1797 | diff --git a/virt/autopkgtest-virt-lxc b/virt/autopkgtest-virt-lxc |
1798 | index 7a9a682..a2ecd76 100755 |
1799 | --- a/virt/autopkgtest-virt-lxc |
1800 | +++ b/virt/autopkgtest-virt-lxc |
1801 | @@ -139,10 +139,18 @@ def determine_normal_user(lxc_name): |
1802 | |
1803 | global capabilities, normal_user |
1804 | |
1805 | +<<<<<<< virt/autopkgtest-virt-lxc |
1806 | # get the first UID >= 1000 |
1807 | cmd = ['lxc-attach', '--name', lxc_name, '--', 'sh', '-c', |
1808 | 'getent passwd | sort -t: -nk3 | ' |
1809 | "awk -F: '{if ($3 >= 1000) { print $1; exit } }'"] |
1810 | +======= |
1811 | + # get the first UID in the Debian Policy §9.2.2 "dynamically allocated |
1812 | + # user account" range |
1813 | + cmd = ['lxc-attach', '--name', lxc_name, '--', 'sh', '-c', |
1814 | + 'getent passwd | sort -t: -nk3 | ' |
1815 | + "awk -F: '{if ($3 >= 1000 && $3 <= 59999) { print $1; exit } }'"] |
1816 | +>>>>>>> virt/autopkgtest-virt-lxc |
1817 | out = VirtSubproc.execute_timeout(None, 10, sudoify(cmd), |
1818 | stdout=subprocess.PIPE)[1].strip() |
1819 | if out: |
1820 | @@ -150,7 +158,11 @@ def determine_normal_user(lxc_name): |
1821 | capabilities.append('suggested-normal-user=' + normal_user) |
1822 | adtlog.debug('determine_normal_user: got user "%s"' % normal_user) |
1823 | else: |
1824 | +<<<<<<< virt/autopkgtest-virt-lxc |
1825 | adtlog.debug('determine_normal_user: no uid >= 1000 available') |
1826 | +======= |
1827 | + adtlog.debug('determine_normal_user: no uid in [1000,59999] available') |
1828 | +>>>>>>> virt/autopkgtest-virt-lxc |
1829 | |
1830 | |
1831 | def start_lxc1(): |
1832 | diff --git a/virt/autopkgtest-virt-lxd b/virt/autopkgtest-virt-lxd |
1833 | index 46394da..f51ca34 100755 |
1834 | --- a/virt/autopkgtest-virt-lxd |
1835 | +++ b/virt/autopkgtest-virt-lxd |
1836 | @@ -115,10 +115,18 @@ def determine_normal_user(): |
1837 | |
1838 | global capabilities, normal_user |
1839 | |
1840 | +<<<<<<< virt/autopkgtest-virt-lxd |
1841 | # get the first UID >= 1000 |
1842 | cmd = ['lxc', 'exec', container_name, '--', 'sh', '-c', |
1843 | 'getent passwd | sort -t: -nk3 | ' |
1844 | "awk -F: '{if ($3 >= 1000) { print $1; exit } }'"] |
1845 | +======= |
1846 | + # get the first UID in the Debian Policy §9.2.2 "dynamically allocated |
1847 | + # user account" range |
1848 | + cmd = ['lxc', 'exec', container_name, '--', 'sh', '-c', |
1849 | + 'getent passwd | sort -t: -nk3 | ' |
1850 | + "awk -F: '{if ($3 >= 1000 && $3 <= 59999) { print $1; exit } }'"] |
1851 | +>>>>>>> virt/autopkgtest-virt-lxd |
1852 | out = VirtSubproc.execute_timeout(None, 10, cmd, |
1853 | stdout=subprocess.PIPE)[1].strip() |
1854 | if out: |
1855 | @@ -126,7 +134,11 @@ def determine_normal_user(): |
1856 | capabilities.append('suggested-normal-user=' + normal_user) |
1857 | adtlog.debug('determine_normal_user: got user "%s"' % normal_user) |
1858 | else: |
1859 | +<<<<<<< virt/autopkgtest-virt-lxd |
1860 | adtlog.debug('determine_normal_user: no uid >= 1000 available') |
1861 | +======= |
1862 | + adtlog.debug('determine_normal_user: no uid in [1000,59999] available') |
1863 | +>>>>>>> virt/autopkgtest-virt-lxd |
1864 | |
1865 | |
1866 | def hook_open(): |
1867 | diff --git a/virt/autopkgtest-virt-qemu b/virt/autopkgtest-virt-qemu |
1868 | index afb82e7..416c4c8 100755 |
1869 | --- a/virt/autopkgtest-virt-qemu |
1870 | +++ b/virt/autopkgtest-virt-qemu |
1871 | @@ -90,6 +90,8 @@ def parse_args(): |
1872 | help='Pass through arguments to QEMU command.') |
1873 | parser.add_argument('--baseimage', action='store_true', default=False, |
1874 | help='Provide a read-only copy of the base image at /dev/baseimage') |
1875 | + parser.add_argument('--efi', action='store_true', default=False, |
1876 | + help='Use OVMF or AAVMF to boot virtual machine using EFI (default: BIOS)') |
1877 | parser.add_argument('image', nargs='+', |
1878 | help='disk image to add to the VM (in order)') |
1879 | |
1880 | @@ -494,10 +496,11 @@ def determine_normal_user(shared_dir): |
1881 | normal_user = args.user |
1882 | return |
1883 | |
1884 | - # get the first UID >= 500 |
1885 | + # get the first UID in the Debian Policy §9.2.2 "dynamically allocated |
1886 | + # user account" range |
1887 | term = VirtSubproc.get_unix_socket(os.path.join(workdir, 'ttyS1')) |
1888 | term.send(b"getent passwd | sort -t: -nk3 | " |
1889 | - b"awk -F: '{if ($3 >= 500) { print $1; exit } }'" |
1890 | + b"awk -F: '{if ($3 >= 1000 && $3 <= 59999) { print $1; exit } }'" |
1891 | b"> /run/autopkgtest/shared/normal_user\n") |
1892 | with VirtSubproc.timeout(5, 'timed out on determining normal user'): |
1893 | outfile = os.path.join(shared_dir, 'normal_user') |
1894 | @@ -509,7 +512,7 @@ def determine_normal_user(shared_dir): |
1895 | normal_user = out.strip() |
1896 | adtlog.debug('determine_normal_user: got user "%s"' % normal_user) |
1897 | else: |
1898 | - adtlog.debug('determine_normal_user: no uid >= 500 available') |
1899 | + adtlog.debug('determine_normal_user: no uid in [1000,59999] available') |
1900 | |
1901 | |
1902 | def hook_open(): |
1903 | @@ -544,6 +547,30 @@ def hook_open(): |
1904 | '-virtfs', |
1905 | 'local,id=autopkgtest,path=%s,security_model=none,mount_tag=autopkgtest' % shareddir, |
1906 | '-drive', 'file=%s,cache=unsafe,if=virtio,index=0' % overlay] |
1907 | + |
1908 | + if args.efi: |
1909 | + code = None |
1910 | + data = None |
1911 | + |
1912 | + if 'qemu-system-x86_64' in args.qemu_command or \ |
1913 | + 'qemu-system-i386' in args.qemu_command: |
1914 | + code = '/usr/share/OVMF/OVMF_CODE.fd' |
1915 | + data = '/usr/share/OVMF/OVMF_VARS.fd' |
1916 | + elif 'qemu-system-aarch64' in args.qemu_command: |
1917 | + code = '/usr/share/AAVMF/AAVMF_CODE.fd' |
1918 | + data = '/usr/share/AAVMF/AAVMF_VARS.fd' |
1919 | + elif 'qemu-system-arm' in args.qemu_command: |
1920 | + code = '/usr/share/AAVMF/AAVMF32_CODE.fd' |
1921 | + data = '/usr/share/AAVMF/AAVMF32_VARS.fd' |
1922 | + else: |
1923 | + VirtSubproc.bomb('Unknown architecture for EFI boot') |
1924 | + |
1925 | + shutil.copy(data, '%s/efivars.fd' % workdir) |
1926 | + argv.append('-drive') |
1927 | + argv.append('if=pflash,format=raw,read-only,file=' + code) |
1928 | + argv.append('-drive') |
1929 | + argv.append('if=pflash,format=raw,file=%s/efivars.fd' % workdir) |
1930 | + |
1931 | for i, image in enumerate(args.image[1:]): |
1932 | argv.append('-drive') |
1933 | argv.append('file=%s,if=virtio,index=%i,readonly' % (image, i + 1)) |
1934 | diff --git a/virt/autopkgtest-virt-qemu.1 b/virt/autopkgtest-virt-qemu.1 |
1935 | index cdfe190..2137725 100644 |
1936 | --- a/virt/autopkgtest-virt-qemu.1 |
1937 | +++ b/virt/autopkgtest-virt-qemu.1 |
1938 | @@ -110,6 +110,14 @@ will not be accessible between calling |
1939 | and the next boot, thus make sure to stop accessing it before. |
1940 | |
1941 | .TP |
1942 | +.B \-\-efi |
1943 | +Configure QEMU with OVMF or AAVMF firmware images suitable for booting |
1944 | +an x86, ARM or ARM64 virtual machine using EFI. This requires the |
1945 | +ovmf, qemu-efi-arm or qemu-efi-aarch64 package. The correct firmware |
1946 | +image is guessed from the |
1947 | +.BR --qemu-command . |
1948 | + |
1949 | +.TP |
1950 | .BI "--qemu-options=" arguments |
1951 | Pass through arguments to QEMU command; e. g. --qemu-options='-readconfig qemu.cfg' |
1952 |
The following commit has been dropped:
commit b442efb577be63f 4f03fcc5d821a68 dc553628a3 (ubuntu/master)
Author: Steve Langasek <email address hidden>
Date: Fri Aug 31 21:53:49 2018 -0700
Don't pick uids between 500 and 999 for our container user
these are system users under Debian Policy, and we now have a common user
in this uid range, systemd-coredump, with a non-writable homedir. That
messes up a lot of tests.
It was no longer necessary, upstream took it a bit further and made it pick in [1000,59999] rather than any >= 1000.