Merge lp:~james-w/linaro-image-tools/fetch-packages into lp:linaro-image-tools/11.11
- fetch-packages
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 64 |
Proposed branch: | lp:~james-w/linaro-image-tools/fetch-packages |
Merge into: | lp:linaro-image-tools/11.11 |
Prerequisite: | lp:~james-w/linaro-image-tools/create-hwpack-skeleton |
Diff against target: |
547 lines (+510/-0) 4 files modified
hwpack/packages.py (+165/-0) hwpack/testing.py (+112/-0) hwpack/tests/__init__.py (+1/-0) hwpack/tests/test_packages.py (+232/-0) |
To merge this branch: | bzr merge lp:~james-w/linaro-image-tools/fetch-packages |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Hudson-Doyle (community) | Approve | ||
Review via email: mp+34355@code.launchpad.net |
Commit message
Description of the change
Hi,
Here's the result of my investigations in to how we can fetch packages.
It's small code which I like. It's currently limited to fetching just the
specified packages, as we don't really know what we want there yet. I'm
assuming that if we need to then python-apt can allow us to resolve
dependencies too.
I have two issues with this change, the first being the possible licensing
impact of using python-apt. It is GPL2+ licensed, which I believe would
mean that this code would need to be too, which means getting a TSC
exemption, unless that has already been done for linaro-image-tools.
The second is the growing amount of code in testing.py with no tests.
Should I be testing that code too?
Thanks,
James
- 84. By James Westby
-
Use apt rather than bzrlib, with our own method to avoid the stdout output.
- 85. By James Westby
-
Remove the unneeded ugly code.
- 86. By James Westby
-
Merged create-
hwpack- skeleton into fetch-packages. - 87. By James Westby
-
Use a class as the return value such that we can return name, version and filename.
Michael Hudson-Doyle (mwhudson) wrote : | # |
James Westby (james-w) wrote : | # |
> FWIW about the licensing, http://
> /linaro-
> under GPLv3.
I just saw that too. I guess that means we will have no problem, but I will
check with Loïc.
Thanks,
James
Michael Hudson-Doyle (mwhudson) wrote : | # |
As for the code, I think it's by and large fine, bearing in mind that I don't know how python-apt works.
I don't know if the stuff in testing.py needs tests, but it certainly needs documentation. I also wonder if AptSource should be called AptSourceFixture or something.
DummyProgress could use a docstring. After all that effort going to silencing python-apt, some kind of progress is likely to be desired, I think... I guess this can be carefully added back in later.
fetch_packages doesn't document its return type correctly any more.
All else looks good.
- 88. By James Westby
-
Add some documentation for the Package object.
- 89. By James Westby
-
Make Package in to DummyFetchedPackage to share an interface.
- 90. By James Westby
-
Add more documentation to testing.py and rename AptSource to AptSourceFixture
- 91. By James Westby
-
Tweak docstrings in hwpack.packages. Thanks Michael.
Preview Diff
1 | === added file 'hwpack/packages.py' |
2 | --- hwpack/packages.py 1970-01-01 00:00:00 +0000 |
3 | +++ hwpack/packages.py 2010-09-02 15:37:46 +0000 |
4 | @@ -0,0 +1,165 @@ |
5 | +import os |
6 | +import shutil |
7 | +import tempfile |
8 | + |
9 | +from apt.cache import Cache |
10 | +from apt.package import FetchError |
11 | +import apt_pkg |
12 | + |
13 | + |
14 | +class DummyProgress(object): |
15 | + """An AcquireProgress that silences all output. |
16 | + |
17 | + This can be used to ensure that apt produces no output |
18 | + when fetching files. |
19 | + """ |
20 | + |
21 | + def start(self): |
22 | + pass |
23 | + |
24 | + def ims_hit(self, item): |
25 | + pass |
26 | + |
27 | + def fail(self, item): |
28 | + pass |
29 | + |
30 | + def fetch(self, item): |
31 | + pass |
32 | + |
33 | + def pulse(self, owner): |
34 | + return True |
35 | + |
36 | + def media_change(self): |
37 | + return False |
38 | + |
39 | + def stop(self): |
40 | + pass |
41 | + |
42 | + |
43 | +class FetchedPackage(object): |
44 | + """The result of fetching packages. |
45 | + |
46 | + :ivar name: the name of the fetched package. |
47 | + :type name: str |
48 | + :ivar version: the version of the fetched package. |
49 | + :type version: str |
50 | + :ivar filename: the filename that the package has. |
51 | + :type filename: str |
52 | + :ivar content: a file that the content of the package can be read from. |
53 | + :type content: a file-like object |
54 | + :ivar size: the size of the package |
55 | + :type size: int |
56 | + :ivar md5: the hex representation of the md5sum of the contents of |
57 | + the package. |
58 | + :type md5: str |
59 | + """ |
60 | + |
61 | + def __init__(self, name, version, filename, content, size, md5): |
62 | + """Create a FetchedPackage. |
63 | + |
64 | + See the instance variables for the arguments. |
65 | + """ |
66 | + self.name = name |
67 | + self.version = version |
68 | + self.filename = filename |
69 | + self.content = content |
70 | + self.size = size |
71 | + self.md5 = md5 |
72 | + |
73 | + def __eq__(self, other): |
74 | + return (self.name == other.name |
75 | + and self.version == other.version |
76 | + and self.filename == other.filename |
77 | + and self.content.read() == other.content.read() |
78 | + and self.size == other.size |
79 | + and self.md5 == other.md5) |
80 | + |
81 | + def __hash__(self): |
82 | + return hash( |
83 | + (self.name, self.version, self.filename, self.size, self.md5)) |
84 | + |
85 | + |
86 | +class PackageFetcher(object): |
87 | + """A class to fetch packages from a defined list of sources.""" |
88 | + |
89 | + def __init__(self, sources): |
90 | + """Create a PackageFetcher. |
91 | + |
92 | + Once created a PackageFetcher should have its `prepare` method |
93 | + called before use. |
94 | + |
95 | + :param sources: a list of sources such that they can be prefixed |
96 | + with "deb " and fed to apt. |
97 | + :type sources: an iterable of str |
98 | + """ |
99 | + self.sources = sources |
100 | + self.tempdir = None |
101 | + |
102 | + def prepare(self): |
103 | + """Prepare a PackageFetcher for use. |
104 | + |
105 | + Should be called before use, and after any modification to the list |
106 | + of sources. |
107 | + """ |
108 | + self.cleanup() |
109 | + self.tempdir = tempfile.mkdtemp(prefix="hwpack-apt-cache-") |
110 | + files = ["var/lib/dpkg/status", |
111 | + ] |
112 | + dirs = ["var/lib/dpkg", |
113 | + "etc/apt/", |
114 | + "var/cache/apt/archives/partial", |
115 | + "var/lib/apt/lists/partial", |
116 | + ] |
117 | + for d in dirs: |
118 | + os.makedirs(os.path.join(self.tempdir, d)) |
119 | + for fn in files: |
120 | + with open(os.path.join(self.tempdir, fn), 'w'): |
121 | + pass |
122 | + sources_list = os.path.join( |
123 | + self.tempdir, "etc", "apt", "sources.list") |
124 | + with open(sources_list, 'w') as f: |
125 | + for source in self.sources: |
126 | + f.write("deb %s\n" % source) |
127 | + self.cache = Cache(rootdir=self.tempdir, memonly=True) |
128 | + self.cache.update() |
129 | + self.cache.open() |
130 | + |
131 | + def cleanup(self): |
132 | + """Cleanup any remaining artefacts. |
133 | + |
134 | + Should be called on all PackageFetchers when they are finished |
135 | + with. |
136 | + """ |
137 | + if self.tempdir is not None and os.path.exists(self.tempdir): |
138 | + shutil.rmtree(self.tempdir) |
139 | + |
140 | + def fetch_packages(self, packages): |
141 | + """Fetch the files for the given list of package names. |
142 | + |
143 | + :param packages: a list of package names to install |
144 | + :type packages: an iterable of str |
145 | + :return: a list of the packages that were fetched, with relevant |
146 | + metdata and the contents of the files available. |
147 | + :rtype: an iterable of FetchedPackages. |
148 | + :raises KeyError: if any of the package names in the list couldn't |
149 | + be found. |
150 | + """ |
151 | + results = [] |
152 | + for package in packages: |
153 | + candidate = self.cache[package].candidate |
154 | + base = os.path.basename(candidate.filename) |
155 | + destfile = os.path.join(self.tempdir, base) |
156 | + acq = apt_pkg.Acquire(DummyProgress()) |
157 | + acqfile = apt_pkg.AcquireFile( |
158 | + acq, candidate.uri, candidate.md5, candidate.size, |
159 | + base, destfile=destfile) |
160 | + acq.run() |
161 | + if acqfile.status != acqfile.STAT_DONE: |
162 | + raise FetchError( |
163 | + "The item %r could not be fetched: %s" % |
164 | + (acqfile.destfile, acqfile.error_text)) |
165 | + result_package = FetchedPackage( |
166 | + candidate.package.name, candidate.version, base, |
167 | + open(destfile), candidate.size, candidate.md5) |
168 | + results.append(result_package) |
169 | + return results |
170 | |
171 | === modified file 'hwpack/testing.py' |
172 | --- hwpack/testing.py 2010-08-31 15:10:50 +0000 |
173 | +++ hwpack/testing.py 2010-09-02 15:37:46 +0000 |
174 | @@ -1,8 +1,15 @@ |
175 | from contextlib import contextmanager |
176 | +import hashlib |
177 | +import os |
178 | +import shutil |
179 | +import tempfile |
180 | from StringIO import StringIO |
181 | import tarfile |
182 | |
183 | +from testtools import TestCase |
184 | + |
185 | from hwpack.better_tarfile import writeable_tarfile |
186 | +from hwpack.packages import FetchedPackage |
187 | |
188 | |
189 | @contextmanager |
190 | @@ -37,3 +44,108 @@ |
191 | yield tf |
192 | finally: |
193 | tf.close() |
194 | + |
195 | + |
196 | +class DummyFetchedPackage(FetchedPackage): |
197 | + """A FetchedPackage with dummy information. |
198 | + |
199 | + See FetchedPackage for the instance variables. |
200 | + """ |
201 | + |
202 | + def __init__(self, name, version): |
203 | + """Create a DummyFetchedPackage. |
204 | + |
205 | + :param name: the name of the package. |
206 | + :type name: str |
207 | + :param version: the version of the package. |
208 | + :type version: str |
209 | + """ |
210 | + self.name = name |
211 | + self.version = version |
212 | + |
213 | + @property |
214 | + def filename(self): |
215 | + return "%s_%s_all.deb" % (self.name, self.version) |
216 | + |
217 | + @property |
218 | + def content(self): |
219 | + return StringIO("Content of %s" % self.filename) |
220 | + |
221 | + @property |
222 | + def size(self): |
223 | + return len(self.content.read()) |
224 | + |
225 | + @property |
226 | + def md5(self): |
227 | + md5sum = hashlib.md5() |
228 | + md5sum.update(self.content.read()) |
229 | + return md5sum.hexdigest() |
230 | + |
231 | + |
232 | +class AptSourceFixture(object): |
233 | + """A fixture that provides an apt source, with packages and indices. |
234 | + |
235 | + An apt source provides a set of package files, and a Packages file |
236 | + that allows apt to determine the contents of the source. |
237 | + |
238 | + :ivar sources_entry: the URI and suite to give to apt to view the |
239 | + source (i.e. a sources.list line without the "deb" prefix |
240 | + :type sources_entry: str |
241 | + """ |
242 | + |
243 | + def __init__(self, packages): |
244 | + """Create an AptSourceFixture. |
245 | + |
246 | + :param packages: a list of packages to add to the source |
247 | + and index. |
248 | + :type packages: an iterable of FetchedPackages |
249 | + """ |
250 | + self.packages = packages |
251 | + |
252 | + def setUp(self): |
253 | + self.rootdir = tempfile.mkdtemp(prefix="hwpack-apt-source-") |
254 | + for package in self.packages: |
255 | + with open( |
256 | + os.path.join(self.rootdir, package.filename), 'wb') as f: |
257 | + f.write(package.content.read()) |
258 | + with open(os.path.join(self.rootdir, "Packages"), 'wb') as f: |
259 | + for package in self.packages: |
260 | + f.write('Package: %s\n' % package.name) |
261 | + f.write('Version: %s\n' % package.version) |
262 | + f.write('Filename: %s\n' % package.filename) |
263 | + f.write('Size: %d\n' % package.size) |
264 | + f.write('Architecture: all\n') |
265 | + f.write('MD5sum: %s\n' % package.md5) |
266 | + f.write('\n') |
267 | + |
268 | + def tearDown(self): |
269 | + if os.path.exists(self.rootdir): |
270 | + shutil.rmtree(self.rootdir) |
271 | + |
272 | + @property |
273 | + def sources_entry(self): |
274 | + return "file:" + os.path.abspath(self.rootdir) +" ./" |
275 | + |
276 | + |
277 | +class TestCaseWithFixtures(TestCase): |
278 | + """A TestCase with the ability to easily add 'fixtures'. |
279 | + |
280 | + A fixture is an object which can be created and cleaned up, and |
281 | + this test case knows how to manage them to ensure that they will |
282 | + always be cleaned up at the end of the test. |
283 | + """ |
284 | + |
285 | + def useFixture(self, fixture): |
286 | + """Make use of a fixture, ensuring that it will be cleaned up. |
287 | + |
288 | + Given a fixture, this method will run the `setUp` method of |
289 | + the fixture, and ensure that its `tearDown` method will be |
290 | + called at the end of the test, regardless of success or failure. |
291 | + |
292 | + :param fixture: the fixture to use. |
293 | + :type fixture: an object with setUp and tearDown methods. |
294 | + :return: the fixture that was passed in. |
295 | + """ |
296 | + self.addCleanup(fixture.tearDown) |
297 | + fixture.setUp() |
298 | + return fixture |
299 | |
300 | === modified file 'hwpack/tests/__init__.py' |
301 | --- hwpack/tests/__init__.py 2010-08-31 01:05:12 +0000 |
302 | +++ hwpack/tests/__init__.py 2010-09-02 15:37:46 +0000 |
303 | @@ -4,6 +4,7 @@ |
304 | module_names = ['hwpack.tests.test_config', |
305 | 'hwpack.tests.test_better_tarfile', |
306 | 'hwpack.tests.test_hardwarepack', |
307 | + 'hwpack.tests.test_packages', |
308 | 'hwpack.tests.test_tarfile_matchers', |
309 | ] |
310 | loader = unittest.TestLoader() |
311 | |
312 | === added file 'hwpack/tests/test_packages.py' |
313 | --- hwpack/tests/test_packages.py 1970-01-01 00:00:00 +0000 |
314 | +++ hwpack/tests/test_packages.py 2010-09-02 15:37:46 +0000 |
315 | @@ -0,0 +1,232 @@ |
316 | +import os |
317 | +from StringIO import StringIO |
318 | + |
319 | +from testtools import TestCase |
320 | + |
321 | +from hwpack.packages import ( |
322 | + FetchedPackage, |
323 | + PackageFetcher, |
324 | + ) |
325 | +from hwpack.testing import ( |
326 | + AptSourceFixture, |
327 | + DummyFetchedPackage, |
328 | + TestCaseWithFixtures, |
329 | + ) |
330 | + |
331 | + |
332 | +class FetchedPackageTests(TestCase): |
333 | + |
334 | + def test_attributes(self): |
335 | + package = FetchedPackage( |
336 | + "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa") |
337 | + self.assertEqual("foo", package.name) |
338 | + self.assertEqual("1.1", package.version) |
339 | + self.assertEqual("foo_1.1.deb", package.filename) |
340 | + self.assertEqual("xxxx", package.content.read()) |
341 | + self.assertEqual(4, package.size) |
342 | + self.assertEqual("aaaa", package.md5) |
343 | + |
344 | + def test_equal(self): |
345 | + package1 = FetchedPackage( |
346 | + "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa") |
347 | + package2 = FetchedPackage( |
348 | + "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa") |
349 | + self.assertEqual(package1, package2) |
350 | + |
351 | + def test_not_equal_different_name(self): |
352 | + package1 = FetchedPackage( |
353 | + "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa") |
354 | + package2 = FetchedPackage( |
355 | + "bar", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa") |
356 | + self.assertNotEqual(package1, package2) |
357 | + |
358 | + def test_not_equal_different_version(self): |
359 | + package1 = FetchedPackage( |
360 | + "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa") |
361 | + package2 = FetchedPackage( |
362 | + "foo", "1.2", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa") |
363 | + self.assertNotEqual(package1, package2) |
364 | + |
365 | + def test_not_equal_different_filename(self): |
366 | + package1 = FetchedPackage( |
367 | + "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa") |
368 | + package2 = FetchedPackage( |
369 | + "foo", "1.1", "afoo_1.1.deb", StringIO("xxxx"), 4, "aaaa") |
370 | + self.assertNotEqual(package1, package2) |
371 | + |
372 | + def test_not_equal_different_content(self): |
373 | + package1 = FetchedPackage( |
374 | + "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa") |
375 | + package2 = FetchedPackage( |
376 | + "foo", "1.1", "foo_1.1.deb", StringIO("yyyy"), 4, "aaaa") |
377 | + self.assertNotEqual(package1, package2) |
378 | + |
379 | + def test_not_equal_different_size(self): |
380 | + package1 = FetchedPackage( |
381 | + "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa") |
382 | + package2 = FetchedPackage( |
383 | + "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 5, "aaaa") |
384 | + self.assertNotEqual(package1, package2) |
385 | + |
386 | + def test_not_equal_different_md5(self): |
387 | + package1 = FetchedPackage( |
388 | + "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa") |
389 | + package2 = FetchedPackage( |
390 | + "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "bbbb") |
391 | + self.assertNotEqual(package1, package2) |
392 | + |
393 | + def test_equal_hash_equal(self): |
394 | + package1 = FetchedPackage( |
395 | + "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa") |
396 | + package2 = FetchedPackage( |
397 | + "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa") |
398 | + self.assertEqual(hash(package1), hash(package2)) |
399 | + |
400 | + |
401 | +class PackageFetcherTests(TestCaseWithFixtures): |
402 | + |
403 | + def test_cleanup_removes_tempdir(self): |
404 | + fetcher = PackageFetcher([]) |
405 | + fetcher.prepare() |
406 | + tempdir = fetcher.tempdir |
407 | + fetcher.cleanup() |
408 | + self.assertFalse(os.path.exists(tempdir)) |
409 | + |
410 | + def test_cleanup_ignores_missing_tempdir(self): |
411 | + fetcher = PackageFetcher([]) |
412 | + fetcher.prepare() |
413 | + tempdir = fetcher.tempdir |
414 | + fetcher.cleanup() |
415 | + # Check that there is no problem removing it again |
416 | + fetcher.cleanup() |
417 | + |
418 | + def test_cleanup_before_prepare(self): |
419 | + fetcher = PackageFetcher([]) |
420 | + # Check that there is no problem cleaning up before we start |
421 | + fetcher.cleanup() |
422 | + |
423 | + def test_prepare_creates_tempdir(self): |
424 | + fetcher = PackageFetcher([]) |
425 | + self.addCleanup(fetcher.cleanup) |
426 | + fetcher.prepare() |
427 | + self.assertTrue(os.path.isdir(fetcher.tempdir)) |
428 | + |
429 | + def test_prepare_creates_var_lib_dpkg_status_file(self): |
430 | + fetcher = PackageFetcher([]) |
431 | + self.addCleanup(fetcher.cleanup) |
432 | + fetcher.prepare() |
433 | + self.assertEqual( |
434 | + '', |
435 | + open(os.path.join( |
436 | + fetcher.tempdir, "var", "lib", "dpkg", "status")).read()) |
437 | + |
438 | + def test_prepare_creates_var_cache_apt_archives_partial_dir(self): |
439 | + fetcher = PackageFetcher([]) |
440 | + self.addCleanup(fetcher.cleanup) |
441 | + fetcher.prepare() |
442 | + self.assertTrue( |
443 | + os.path.isdir(os.path.join( |
444 | + fetcher.tempdir, "var", "cache", "apt", "archives", |
445 | + "partial"))) |
446 | + |
447 | + def test_prepare_creates_var_lib_apt_lists_partial_dir(self): |
448 | + fetcher = PackageFetcher([]) |
449 | + self.addCleanup(fetcher.cleanup) |
450 | + fetcher.prepare() |
451 | + self.assertTrue( |
452 | + os.path.isdir(os.path.join( |
453 | + fetcher.tempdir, "var", "lib", "apt", "lists", "partial"))) |
454 | + |
455 | + def test_prepare_creates_etc_apt_sources_list_file(self): |
456 | + source1 = self.useFixture(AptSourceFixture([])) |
457 | + source2 = self.useFixture(AptSourceFixture([])) |
458 | + fetcher = PackageFetcher( |
459 | + [source1.sources_entry, source2.sources_entry]) |
460 | + self.addCleanup(fetcher.cleanup) |
461 | + fetcher.prepare() |
462 | + self.assertEqual( |
463 | + "deb %s\ndeb %s\n" % ( |
464 | + source1.sources_entry, source2.sources_entry), |
465 | + open(os.path.join( |
466 | + fetcher.tempdir, "etc", "apt", "sources.list")).read()) |
467 | + |
468 | + def get_fetcher(self, sources): |
469 | + fetcher = PackageFetcher([s.sources_entry for s in sources]) |
470 | + self.addCleanup(fetcher.cleanup) |
471 | + fetcher.prepare() |
472 | + return fetcher |
473 | + |
474 | + def test_fetch_packages_not_found_because_no_sources(self): |
475 | + fetcher = self.get_fetcher([]) |
476 | + self.assertRaises(KeyError, fetcher.fetch_packages, ["nothere"]) |
477 | + |
478 | + def test_fetch_packages_not_found_because_not_in_sources(self): |
479 | + available_package = DummyFetchedPackage("foo", "1.0") |
480 | + source = self.useFixture(AptSourceFixture([available_package])) |
481 | + fetcher = self.get_fetcher([source]) |
482 | + self.assertRaises(KeyError, fetcher.fetch_packages, ["nothere"]) |
483 | + |
484 | + def test_fetch_packages_not_found_one_of_two_missing(self): |
485 | + available_package = DummyFetchedPackage("foo", "1.0") |
486 | + source = self.useFixture(AptSourceFixture([available_package])) |
487 | + fetcher = self.get_fetcher([source]) |
488 | + self.assertRaises( |
489 | + KeyError, fetcher.fetch_packages, ["foo", "nothere"]) |
490 | + |
491 | + def test_fetch_packges_fetches_no_packages(self): |
492 | + available_package = DummyFetchedPackage("foo", "1.0") |
493 | + source = self.useFixture(AptSourceFixture([available_package])) |
494 | + fetcher = self.get_fetcher([source]) |
495 | + self.assertEqual(0, len(fetcher.fetch_packages([]))) |
496 | + |
497 | + def test_fetch_packges_fetches_single_package(self): |
498 | + available_package = DummyFetchedPackage("foo", "1.0") |
499 | + source = self.useFixture(AptSourceFixture([available_package])) |
500 | + fetcher = self.get_fetcher([source]) |
501 | + self.assertEqual(1, len(fetcher.fetch_packages(["foo"]))) |
502 | + |
503 | + def test_fetch_packges_fetches_correct_packge(self): |
504 | + available_package = DummyFetchedPackage("foo", "1.0") |
505 | + source = self.useFixture(AptSourceFixture([available_package])) |
506 | + fetcher = self.get_fetcher([source]) |
507 | + self.assertEqual( |
508 | + available_package, fetcher.fetch_packages(["foo"])[0]) |
509 | + |
510 | + def test_fetch_packges_fetches_multiple_packages(self): |
511 | + available_packages = [ |
512 | + DummyFetchedPackage("bar", "1.0"), |
513 | + DummyFetchedPackage("foo", "1.0"), |
514 | + ] |
515 | + source = self.useFixture(AptSourceFixture(available_packages)) |
516 | + fetcher = self.get_fetcher([source]) |
517 | + self.assertEqual(2, len(fetcher.fetch_packages(["foo", "bar"]))) |
518 | + |
519 | + def test_fetch_packges_fetches_multiple_packages_correctly(self): |
520 | + available_packages = [ |
521 | + DummyFetchedPackage("foo", "1.0"), |
522 | + DummyFetchedPackage("bar", "1.0"), |
523 | + ] |
524 | + source = self.useFixture(AptSourceFixture(available_packages)) |
525 | + fetcher = self.get_fetcher([source]) |
526 | + fetched = fetcher.fetch_packages(["foo", "bar"]) |
527 | + self.assertEqual(available_packages[0], fetched[0]) |
528 | + self.assertEqual(available_packages[1], fetched[1]) |
529 | + |
530 | + def test_fetch_packages_fetches_newest(self): |
531 | + available_packages = [ |
532 | + DummyFetchedPackage("bar", "1.0"), |
533 | + DummyFetchedPackage("bar", "1.1"), |
534 | + ] |
535 | + source = self.useFixture(AptSourceFixture(available_packages)) |
536 | + fetcher = self.get_fetcher([source]) |
537 | + fetched = fetcher.fetch_packages(["bar"]) |
538 | + self.assertEqual(available_packages[1], fetched[0]) |
539 | + |
540 | + def test_fetch_packages_fetches_newest_from_multiple_sources(self): |
541 | + old_source_packages = [DummyFetchedPackage("bar", "1.0")] |
542 | + new_source_packages = [DummyFetchedPackage("bar", "1.1")] |
543 | + old_source = self.useFixture(AptSourceFixture(old_source_packages)) |
544 | + new_source = self.useFixture(AptSourceFixture(new_source_packages)) |
545 | + fetcher = self.get_fetcher([old_source, new_source]) |
546 | + fetched = fetcher.fetch_packages(["bar"]) |
547 | + self.assertEqual(new_source_packages[0], fetched[0]) |
FWIW about the licensing, http:// bazaar. launchpad. net/~linaro- maintainers/ linaro- image-tools/ linaro- image-tools/ revision/ 44 put linaro-media-create under GPLv3.