Merge lp:~jml/pkgme-service/move-utils-from-tasks into lp:pkgme-service

Proposed by Jonathan Lange
Status: Merged
Approved by: James Westby
Approved revision: 129
Merged at revision: 120
Proposed branch: lp:~jml/pkgme-service/move-utils-from-tasks
Merge into: lp:pkgme-service
Diff against target: 953 lines (+430/-372)
5 files modified
src/djpkgme/osutils.py (+120/-0)
src/djpkgme/tasks.py (+54/-150)
src/djpkgme/tests/__init__.py (+1/-0)
src/djpkgme/tests/test_osutils.py (+150/-0)
src/djpkgme/tests/test_tasks.py (+105/-222)
To merge this branch: bzr merge lp:~jml/pkgme-service/move-utils-from-tasks
Reviewer Review Type Date Requested Status
James Westby (community) Approve
Review via email: mp+124905@code.launchpad.net

Commit message

Move utilities out of tasks.py

Description of the change

I was going to add lzma support, but then realized that tasks.py is still
a huge mess.

This branch makes it slightly less messy by moving the tarball/zipfile stuff
somewhere else (osutils.py), and moving some of the stateless helper methods
to be functions.

To post a comment you must log in.
Revision history for this message
James Westby (james-w) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'src/djpkgme/osutils.py'
2--- src/djpkgme/osutils.py 1970-01-01 00:00:00 +0000
3+++ src/djpkgme/osutils.py 2012-09-18 12:37:20 +0000
4@@ -0,0 +1,120 @@
5+"""Tools for handling archives, and like things."""
6+
7+__all__ = [
8+ 'make_tarball',
9+ 'prepare_file',
10+ ]
11+
12+import errno
13+import itertools
14+import os
15+import shutil
16+import tarfile
17+import zipfile
18+
19+
20+def _try_extract_tarfile(path, target_dir):
21+ """Try extracting `path` to `target_dir` as a tarfile.
22+
23+ :return: True if successful, False if the file at `path` isn't
24+ a `tarfile`.
25+ """
26+ # XXX: untested.
27+ try:
28+ t = tarfile.open(path)
29+ except tarfile.ReadError:
30+ return False
31+ else:
32+ try:
33+ t.extractall(target_dir)
34+ finally:
35+ t.close()
36+ os.unlink(path)
37+ return True
38+
39+
40+def _try_extract_zipfile(path, target_dir):
41+ """Try extracting `path` to `target_dir` as a zipfile.
42+
43+ :return: True if successful, False if the file at `path` isn't
44+ a zipfile.
45+ """
46+ # XXX: untested.
47+ try:
48+ zf = zipfile.ZipFile(path, 'r')
49+ except zipfile.BadZipfile:
50+ return False
51+ else:
52+ try:
53+ zf.extractall(target_dir)
54+ finally:
55+ zf.close()
56+ os.unlink(path)
57+ return True
58+
59+
60+# The name of the directory that we create inside of the temporary build
61+# directory that pkgme does all of its work on.
62+DEFAULT_WORKING_DIRECTORY_NAME = 'working'
63+
64+def prepare_file(path, output_dir,
65+ working_dir_name=DEFAULT_WORKING_DIRECTORY_NAME):
66+ """Get 'path' ready for automated packaging inside 'output_dir'.
67+
68+ If 'path' is a tarball or zip file, then preparing it means extracting
69+ it into a new directory inside 'output_dir'.
70+
71+ If 'path' is a single file, like a PDF, then preparing it means
72+ copying it into a new directory inside 'output_dir'.
73+
74+ Either way, the key thing is that there is a new directory inside
75+ 'output_dir', inside which is the actual contents we want to try to
76+ automatically package.
77+
78+ :param path: The path where the uploaded file lives. Will be a
79+ tarball, PDF or something like that.
80+ :param output_dir: The directory in which to put the prepared version.
81+ :return: A path to a newly-created directory.
82+ """
83+ working_path = os.path.join(output_dir, working_dir_name)
84+ for method in [_try_extract_tarfile, _try_extract_zipfile]:
85+ if method(path, working_path):
86+ break
87+ else:
88+ os.makedirs(working_path)
89+ shutil.copy(path, working_path)
90+ return working_path
91+
92+
93+def make_tarball(source_dir, destination_dir, tarball_name):
94+ """Archive source_dir to destination_dir as a tarball.
95+
96+ Writes a tarball to $destination_dir/$tarball_name.tar.gz. If that
97+ exists, adds a number on the end to make sure we don't override an
98+ existing one.
99+
100+ :param source_dir: A directory that contains the thing to archive.
101+ :param destination_dir: The directory to write the tarball to.
102+ :param package_name: The name of the tarball.
103+ :return: the path of the created tarball.
104+ """
105+ try:
106+ os.makedirs(destination_dir, mode=0755)
107+ except OSError, e:
108+ if not (e.errno == errno.EEXIST
109+ and os.path.isdir(destination_dir)):
110+ raise
111+ destination = os.path.join(
112+ destination_dir, '%s.tar.gz' % (tarball_name,))
113+ disambiguator = itertools.count(1)
114+ while os.path.exists(destination):
115+ destination = os.path.join(
116+ destination_dir,
117+ '%s-%s.tar.gz' % (tarball_name, disambiguator.next()))
118+ t = tarfile.open(destination, 'w:gz')
119+ try:
120+ for name in os.listdir(source_dir):
121+ t.add(os.path.join(source_dir, name), name)
122+ finally:
123+ t.close()
124+ return destination
125
126=== modified file 'src/djpkgme/tasks.py'
127--- src/djpkgme/tasks.py 2012-09-14 13:59:48 +0000
128+++ src/djpkgme/tasks.py 2012-09-18 12:37:20 +0000
129@@ -1,12 +1,8 @@
130-import errno
131-import itertools
132 import json
133 import os
134 import shutil
135 import sys
136-import tarfile
137 import traceback
138-import zipfile
139
140 from bzrlib.urlutils import join as urljoin
141 from celery.registry import tasks
142@@ -36,6 +32,10 @@
143 submit_error,
144 submit_pkgme_info,
145 )
146+from .osutils import (
147+ make_tarball,
148+ prepare_file,
149+ )
150
151
152 class MissingFieldsError(Exception):
153@@ -119,6 +119,45 @@
154 return urljoin(settings.PKGME_OUTPUT_URL, filename)
155
156
157+def prepare_icons(working_dir, icons, logger=None):
158+ """Download the icons we are told about by the client.
159+
160+ :param icons: A dict mapping resolutions (e.g. '48x48' to URLs).
161+ :return: A dict mapping resolutions to local paths.
162+ """
163+ icon_files = {}
164+ for resolution, url in icons.items():
165+ if logger:
166+ logger.info("Downloading icon from %s", url)
167+ icon_files[resolution] = download_file(
168+ url, name=os.path.basename(url), working_dir=working_dir)
169+ if logger:
170+ logger.info("Downloaded icon from %s", url)
171+ return icon_files
172+
173+
174+def prepare_metadata(working_dir, metadata, logger=None):
175+ metadata = dict(metadata)
176+ icons = metadata.pop('icons', None)
177+ if icons:
178+ metadata[MetadataBackend.ICONS] = prepare_icons(
179+ working_dir, icons, logger)
180+ return metadata
181+
182+
183+def write_metadata(metadata, working_path):
184+ """Write metadata in a way that the binary backend expects.
185+
186+ :param metadata: Metadata from the client as a dictionary.
187+ :param working_path: The directory to store the metadata JSON file in.
188+ """
189+ metadata_filename = os.path.join(
190+ working_path, MetadataBackend.METADATA_FILE)
191+ with open(metadata_filename, 'w') as metadata_file:
192+ json.dump(as_pkgme_dict(metadata), metadata_file)
193+ return metadata_filename
194+
195+
196 class BuildPackageTask(Task):
197
198 # If one of these exceptions is raised when building packaging, we report
199@@ -135,142 +174,6 @@
200 'package_url',
201 ]
202
203- # The name of the directory that we create inside of the temporary build
204- # directory that pkgme does all of its work on.
205- WORKING_DIRECTORY_NAME = 'working'
206-
207- def _try_extract_tarfile(self, path, target_dir):
208- """Try extracting `path` to `target_dir` as a tarfile.
209-
210- :return: True if successful, False if the file at `path` isn't
211- a `tarfile`.
212- """
213- try:
214- t = tarfile.open(path)
215- except tarfile.ReadError:
216- return False
217- else:
218- try:
219- t.extractall(target_dir)
220- finally:
221- t.close()
222- os.unlink(path)
223- return True
224-
225- def _try_extract_zipfile(self, path, target_dir):
226- """Try extracting `path` to `target_dir` as a zipfile.
227-
228- :return: True if successful, False if the file at `path` isn't
229- a zipfile.
230- """
231- try:
232- zf = zipfile.ZipFile(path, 'r')
233- except zipfile.BadZipfile:
234- return False
235- else:
236- try:
237- zf.extractall(target_dir)
238- finally:
239- zf.close()
240- os.unlink(path)
241- return True
242-
243- def prepare_file(self, path, output_dir):
244- """Get 'path' ready for automated packaging inside 'output_dir'.
245-
246- If 'path' is a tarball or zip file, then preparing it means extracting
247- it into a new directory inside 'output_dir'.
248-
249- If 'path' is a single file, like a PDF, then preparing it means
250- copying it into a new directory inside 'output_dir'.
251-
252- Either way, the key thing is that there is a new directory inside
253- 'output_dir', inside which is the actual contents we want to try to
254- automatically package.
255-
256- :param path: The path where the uploaded file lives. Will be a
257- tarball, PDF or something like that.
258- :param output_dir: The directory in which to put the prepared version.
259- :return: A path to a newly-created directory.
260- """
261- working_path = os.path.join(output_dir, self.WORKING_DIRECTORY_NAME)
262- for method in (self._try_extract_tarfile, self._try_extract_zipfile):
263- if method(path, working_path):
264- break
265- else:
266- os.makedirs(working_path)
267- shutil.copy(path, working_path)
268- return working_path
269-
270- def prepare_icons(self, working_dir, icons):
271- """Download the icons we are told about by the client.
272-
273- :param icons: A dict mapping resolutions (e.g. '48x48' to URLs).
274- :return: A dict mapping resolutions to local paths.
275- """
276- icon_files = {}
277- for resolution, url in icons.items():
278- self.get_logger().info("Downloading icon from %s", url)
279- icon_files[resolution] = download_file(
280- url, name=os.path.basename(url), working_dir=working_dir)
281- self.get_logger().info("Downloaded icon from %s", url)
282- return icon_files
283-
284- def prepare_metadata(self, working_dir, metadata):
285- metadata = dict(metadata)
286- icons = metadata.pop('icons', None)
287- if icons:
288- metadata[MetadataBackend.ICONS] = self.prepare_icons(
289- working_dir, icons)
290- return metadata
291-
292- def write_metadata(self, metadata, working_path):
293- """Write metadata in a way that the binary backend expects.
294-
295- :param metadata: Metadata from the client as a dictionary.
296- :param working_path: The directory to store the metadata JSON file in.
297- """
298- metadata_filename = os.path.join(
299- working_path, MetadataBackend.METADATA_FILE)
300- with open(metadata_filename, 'w') as metadata_file:
301- json.dump(as_pkgme_dict(metadata), metadata_file)
302- return metadata_filename
303-
304- def save_package(self, source_package_location, destination_dir,
305- package_name):
306- """Save the source package to destination_dir as a tarball.
307-
308- Writes a tarball to $destination_dir/$package_name.tar.gz. If that
309- exists, adds a number on the end to make sure we don't override an
310- existing one.
311-
312- :param source_package_location: A directory that contains the built
313- source package.
314- :param destination_dir: The directory to write the tarball to.
315- :param package_name: The name of the source package. Will be used
316- as part of the tarball name.
317- """
318- try:
319- os.makedirs(destination_dir, mode=0755)
320- except OSError, e:
321- if not (e.errno == errno.EEXIST
322- and os.path.isdir(destination_dir)):
323- raise
324- destination = os.path.join(
325- destination_dir, '%s.tar.gz' % (package_name,))
326- disambiguator = itertools.count(1)
327- while os.path.exists(destination):
328- destination = os.path.join(
329- destination_dir,
330- '%s-%s.tar.gz' % (package_name, disambiguator.next()))
331- t = tarfile.open(destination, 'w:gz')
332- try:
333- for name in os.listdir(source_package_location):
334- t.add(os.path.join(source_package_location, name), name)
335- finally:
336- t.close()
337- return destination
338-
339 def build_package(self, metadata, overrides):
340 """Build packaging for 'metadata'.
341
342@@ -300,34 +203,35 @@
343 # devportal-metadata.json) are tarred up and stored in a preconfigured
344 # location: PKGME_OUTPUT_DIRECTORY.
345 working_dirs = []
346+ logger = self.get_logger()
347 with TempDir() as temp_dir:
348 output_dir = temp_dir.path
349 download_dir = os.path.join(
350 output_dir, self.DOWNLOAD_DIRECTORY_NAME)
351 os.mkdir(download_dir)
352 working_dirs.append(download_dir)
353- self.get_logger().info(
354+ logger.info(
355 "Downloading package from %s", metadata['package_url'])
356 download_path = download_file(
357 metadata['package_url'], download_dir)
358- self.get_logger().info(
359+ logger.info(
360 "Downloaded package from %s", metadata['package_url'])
361- working_path = self.prepare_file(download_path, output_dir)
362+ working_path = prepare_file(download_path, output_dir)
363 # XXX: Missing tests to show that the downloaded icons don't
364 # appear in the saved tarball.
365 icons_dir = os.path.join(output_dir, 'icons')
366 os.mkdir(icons_dir)
367 working_dirs.append(icons_dir)
368- metadata = self.prepare_metadata(icons_dir, metadata)
369- metadata_filename = self.write_metadata(metadata, working_path)
370+ metadata = prepare_metadata(icons_dir, metadata, logger)
371+ metadata_filename = write_metadata(metadata, working_path)
372 # Run pkgme: generate the packaging and build a source package.
373- self.get_logger().info("Running pkgme")
374+ logger.info("Running pkgme")
375 backends = load_backends()
376 pkgme_decision = get_all_info(
377 working_path, backends, settings.PKGME_ALLOWED_BACKENDS)
378 pkgme_info = pkgme_decision['info']
379 write_packaging_info(working_path, pkgme_info)
380- self.get_logger().info("pkgme completed")
381+ logger.info("pkgme completed")
382 # XXX: This duplicates work done by write_packaging. However, we
383 # need the package_name in order to create the tarball in a way
384 # that looks good.
385@@ -344,14 +248,14 @@
386 new_working_path = os.path.join(output_dir, package_name)
387 os.rename(working_path, new_working_path)
388 working_dirs.append(new_working_path)
389- self.get_logger().info("Building source package")
390+ logger.info("Building source package")
391 build_package(new_working_path, False)
392- self.get_logger().info("Finished building source package")
393+ logger.info("Finished building source package")
394 # Create a new tarball containing the source package and the
395 # metadata file, but not the pkgme working set.
396 for working_dir in working_dirs:
397 shutil.rmtree(working_dir)
398- return self.save_package(
399+ return make_tarball(
400 output_dir, settings.PKGME_OUTPUT_DIRECTORY, package_name)
401
402 def _submit_error(self, metadata, error_data):
403
404=== modified file 'src/djpkgme/tests/__init__.py'
405--- src/djpkgme/tests/__init__.py 2012-07-23 14:15:09 +0000
406+++ src/djpkgme/tests/__init__.py 2012-09-18 12:37:20 +0000
407@@ -1,5 +1,6 @@
408 from .test_handlers import *
409 from .test_integration import *
410+from .test_osutils import *
411 from .test_preflight import *
412 from .test_tasks import *
413 from .test_version import *
414
415=== added file 'src/djpkgme/tests/test_osutils.py'
416--- src/djpkgme/tests/test_osutils.py 1970-01-01 00:00:00 +0000
417+++ src/djpkgme/tests/test_osutils.py 2012-09-18 12:37:20 +0000
418@@ -0,0 +1,150 @@
419+import errno
420+import os
421+import tarfile
422+import zipfile
423+
424+from fixtures import TempDir
425+from testtools import TestCase
426+from testtools.matchers import (
427+ DirExists,
428+ FileContains,
429+ HasPermissions,
430+ TarballContains,
431+ )
432+
433+from ..osutils import (
434+ DEFAULT_WORKING_DIRECTORY_NAME,
435+ make_tarball,
436+ prepare_file,
437+ )
438+from .factory import PkgmeObjectFactory
439+
440+
441+class TestPrepareFile(TestCase):
442+
443+ def test_extracts_tarball(self):
444+ # If the file downloaded is a tarball then it is extracted
445+ factory = self.useFixture(PkgmeObjectFactory())
446+ path = factory.make_tarball(self.useFixture(TempDir()).path)
447+ tarball = tarfile.open(path)
448+ top_level_dir = tarball.getnames()[0]
449+ tempdir = self.useFixture(TempDir()).path
450+ new_path = prepare_file(path, tempdir)
451+ self.assertEqual(
452+ os.path.join(tempdir, DEFAULT_WORKING_DIRECTORY_NAME),
453+ new_path)
454+ self.assertThat(new_path, DirExists())
455+ self.assertThat(os.path.join(new_path, top_level_dir), DirExists())
456+
457+ def test_extracts_zips(self):
458+ # If the file downloaded is a .zip then it is extracted
459+ top_level_dir = 'top_level'
460+ tempdir = self.useFixture(TempDir()).path
461+ path = os.path.join(tempdir, 'application.zip')
462+ zf = zipfile.ZipFile(path, 'w')
463+ try:
464+ zf.writestr(os.path.join(top_level_dir, 'afile'), 'some content\n')
465+ finally:
466+ zf.close()
467+ new_path = prepare_file(path, tempdir)
468+ self.assertEqual(
469+ os.path.join(tempdir, DEFAULT_WORKING_DIRECTORY_NAME),
470+ new_path)
471+ self.assertThat(new_path, DirExists())
472+ self.assertThat(os.path.join(new_path, top_level_dir), DirExists())
473+
474+ def test_copies_non_tarball(self):
475+ # If the file downloaded is not a tarball then it is copied
476+ source_dir = self.useFixture(TempDir()).path
477+ filename = 'afile'
478+ content = 'some content\n'
479+ with open(os.path.join(source_dir, filename), 'w') as f:
480+ f.write(content)
481+ tempdir = self.useFixture(TempDir()).path
482+ new_path = prepare_file(
483+ os.path.join(source_dir, filename), tempdir)
484+ self.assertEqual(
485+ os.path.join(tempdir, DEFAULT_WORKING_DIRECTORY_NAME),
486+ new_path)
487+ self.assertThat(new_path, DirExists())
488+ self.assertThat(os.path.join(new_path, filename),
489+ FileContains(content))
490+
491+
492+class TestMakeTarball(TestCase):
493+
494+ def test_destination_multiple_parents_dont_exist(self):
495+ # make_tarball creates the destination directory if it doesn't already
496+ # exist.
497+ factory = self.useFixture(PkgmeObjectFactory())
498+ metadata = factory.make_metadata()
499+ package_name = metadata['package_name']
500+ path = self.useFixture(TempDir()).path
501+ destination_dir = os.path.join(path, 'does', 'not', 'exist')
502+ make_tarball(path, destination_dir, package_name)
503+ self.assertThat(destination_dir, DirExists())
504+ self.assertThat(destination_dir, HasPermissions('0755'))
505+
506+ def test_destination_is_file(self):
507+ # If the destination directory is actually a file, we raise an error.
508+ factory = self.useFixture(PkgmeObjectFactory())
509+ metadata = factory.make_metadata()
510+ package_name = metadata['package_name']
511+ path = self.useFixture(TempDir()).path
512+ destination_dir = os.path.join(path, 'some-file')
513+ with open(destination_dir, 'w') as fp:
514+ fp.write('foo')
515+ e = self.assertRaises(
516+ OSError,
517+ make_tarball, path, destination_dir, package_name)
518+ self.assertEqual(errno.EEXIST, e.errno)
519+
520+ def test_destination_exists(self):
521+ # If the destination directory already exists, we proceed normally.
522+ factory = self.useFixture(PkgmeObjectFactory())
523+ metadata = factory.make_metadata()
524+ package_name = metadata['package_name']
525+ path = self.useFixture(TempDir()).path
526+ destination_dir = self.useFixture(TempDir()).path
527+ make_tarball(path, destination_dir, package_name)
528+ self.assertThat(destination_dir, DirExists())
529+
530+ def test_creates_tarball(self):
531+ # make_tarball creates a tarball in the destination directory that's
532+ # named after the package and contains whatever was in the source
533+ # directory.
534+ factory = self.useFixture(PkgmeObjectFactory())
535+ metadata = factory.make_metadata()
536+ package_name = metadata['package_name']
537+ path = self.useFixture(TempDir()).path
538+ os.mkdir(os.path.join(path, 'a-directory'))
539+ with open(os.path.join(path, 'a-file'), 'w') as fp:
540+ fp.write('foo')
541+ destination_dir = self.useFixture(TempDir()).path
542+ destination = make_tarball(path, destination_dir, package_name)
543+ self.assertEqual(
544+ os.path.join(destination_dir, '%s.tar.gz' % (package_name,)),
545+ destination)
546+ self.assertThat(
547+ destination,
548+ TarballContains(['a-directory', 'a-file']))
549+
550+ def test_uniquifies(self):
551+ # If package-name.tar.gz exists, try package-name-1.tar.gz, etc.
552+ factory = self.useFixture(PkgmeObjectFactory())
553+ metadata = factory.make_metadata()
554+ package_name = metadata['package_name']
555+ path = self.useFixture(TempDir()).path
556+ os.mkdir(os.path.join(path, 'a-directory'))
557+ with open(os.path.join(path, 'a-file'), 'w') as fp:
558+ fp.write('foo')
559+ destination_dir = self.useFixture(TempDir()).path
560+ make_tarball(path, destination_dir, package_name)
561+ destination = make_tarball(path, destination_dir, package_name)
562+ self.assertEqual(
563+ destination,
564+ os.path.join(destination_dir, '%s-1.tar.gz' % (package_name,)))
565+ destination = make_tarball(path, destination_dir, package_name)
566+ self.assertEqual(
567+ destination,
568+ os.path.join(destination_dir, '%s-2.tar.gz' % (package_name,)))
569
570=== modified file 'src/djpkgme/tests/test_tasks.py'
571--- src/djpkgme/tests/test_tasks.py 2012-09-14 14:04:48 +0000
572+++ src/djpkgme/tests/test_tasks.py 2012-09-18 12:37:20 +0000
573@@ -1,13 +1,11 @@
574 # Run tasks eagerly for tests. Taken from
575 # http://ask.github.com/celery/cookbook/unit-testing.html
576
577-import errno
578 import json
579 import os
580 import sys
581 import tarfile
582 import traceback
583-import zipfile
584
585 from devportalbinary.metadata import MetadataBackend
586 from django.conf import settings
587@@ -21,11 +19,9 @@
588 DirExists,
589 Equals,
590 FileContains,
591- HasPermissions,
592 MatchesListwise,
593 SamePath,
594 StartsWith,
595- TarballContains,
596 )
597 from testtools import TestCase
598
599@@ -36,10 +32,14 @@
600 BuildPackageTask,
601 CallbackError,
602 get_url_for_file,
603+ prepare_icons,
604+ prepare_metadata,
605 translate_dict,
606+ write_metadata,
607 )
608 from ..client import get_response_dict
609 from djpkgme.tests.factory import (
610+ PkgmeObjectFactory,
611 TestCaseWithFactory,
612 )
613 from djpkgme.tests.helpers import FakeResponse
614@@ -120,6 +120,107 @@
615 self.assertEqual(expected, as_pkgme_dict(metadata))
616
617
618+class TestExcInfoAsDict(TestCase):
619+
620+ def test_exc_info_as_dict(self):
621+ try:
622+ raise RuntimeError('whatever')
623+ except RuntimeError:
624+ exc_info = sys.exc_info()
625+ error_dict = tasks.exc_info_as_dict(exc_info)
626+ self.assertEqual(
627+ {'type': 'RuntimeError',
628+ 'traceback': ''.join(traceback.format_exception(*exc_info)),
629+ }, error_dict)
630+
631+
632+class TestWriteMetadata(TestCase):
633+
634+ def test_write_metadata(self):
635+ factory = self.useFixture(PkgmeObjectFactory())
636+ metadata = factory.make_metadata()
637+ temp_dir = self.useFixture(TempDir()).path
638+ metadata_filename = write_metadata(metadata, temp_dir)
639+ self.assertThat(
640+ metadata_filename,
641+ SamePath(os.path.join(temp_dir, MetadataBackend.METADATA_FILE)))
642+ self.assertThat(
643+ metadata_filename,
644+ FileContains(json.dumps(as_pkgme_dict(metadata))))
645+
646+
647+class TestPrepareIcons(TestCase):
648+
649+ @patch('djpkgme.tasks.download_file')
650+ def test_downloads_icons(self, mock_download):
651+ icons = {
652+ '48x48': 'http://whatever/something.png',
653+ '64x64': 'http://whatever/foo/another.jpg',
654+ }
655+ path = self.useFixture(TempDir()).path
656+ prepare_icons(path, icons)
657+ self.assertEqual(
658+ sorted(
659+ [mock.call(
660+ 'http://whatever/something.png',
661+ name='something.png', working_dir=path),
662+ mock.call(
663+ 'http://whatever/foo/another.jpg',
664+ name='another.jpg', working_dir=path),
665+ ]),
666+ sorted(mock_download.call_args_list))
667+
668+ def test_returns_paths(self):
669+ def download_file(url, name, working_dir):
670+ return '%s/++download++/%s' % (working_dir, name)
671+ self.patch(tasks, 'download_file', download_file)
672+ icons = {
673+ '48x48': 'http://whatever/something.png',
674+ '64x64': 'http://whatever/foo/another.jpg',
675+ }
676+ path = self.useFixture(TempDir()).path
677+ new_icons = prepare_icons(path, icons)
678+ self.assertEqual(
679+ {'48x48': '%s/++download++/something.png' % (path,),
680+ '64x64': '%s/++download++/another.jpg' % (path,),
681+ }, new_icons)
682+
683+
684+class TestPrepareMetadata(TestCase):
685+
686+ def test_default(self):
687+ path = self.useFixture(TempDir()).path
688+ factory = self.useFixture(PkgmeObjectFactory())
689+ metadata = factory.make_metadata()
690+ prepared = prepare_metadata(path, metadata)
691+ self.assertEqual(prepared, metadata)
692+
693+ def test_maintainer_provided(self):
694+ path = self.useFixture(TempDir()).path
695+ factory = self.useFixture(PkgmeObjectFactory())
696+ metadata = factory.make_metadata()
697+ metadata[MetadataBackend.MAINTAINER] = (
698+ 'Upstream <upstream@example.com>')
699+ prepared = prepare_metadata(path, metadata)
700+ self.assertEqual(prepared, metadata)
701+
702+ @patch('djpkgme.tasks.prepare_icons')
703+ def test_icons(self, prepare_icons):
704+ prepare_icons.return_value = {'48x48': '/tmp/whatever.png'}
705+ path = self.useFixture(TempDir()).path
706+ factory = self.useFixture(PkgmeObjectFactory())
707+ metadata = factory.make_metadata()
708+ expected = dict(metadata)
709+ metadata['icons'] = {
710+ '48x48': 'http://example.com/whatever.png',
711+ }
712+ expected[MetadataBackend.ICONS] = {
713+ '48x48': '/tmp/whatever.png',
714+ }
715+ prepared = prepare_metadata(path, metadata)
716+ self.assertEqual(expected, prepared)
717+
718+
719 class BuildPackageTaskTestCase(TestCaseWithFactory):
720
721 @patch('httplib2.Http.request', return_value=(FakeResponse(200, 'OK'), ''))
722@@ -157,144 +258,6 @@
723 self.assertEqual(args, (metadata['callback_url'],))
724 self.assertEqual(expected, json.loads(kwargs['body']))
725
726- def test_write_metadata(self):
727- metadata = self.factory.make_metadata()
728- task = BuildPackageTask()
729- temp_dir = self.useFixture(TempDir()).path
730- metadata_filename = task.write_metadata(metadata, temp_dir)
731- self.assertThat(
732- metadata_filename,
733- SamePath(os.path.join(temp_dir, MetadataBackend.METADATA_FILE)))
734- self.assertThat(
735- metadata_filename,
736- FileContains(json.dumps(as_pkgme_dict(metadata))))
737-
738- def test_prepare_file_extracts_tarball(self):
739- # If the file downloaded is a tarball then it is extracted
740- task = BuildPackageTask()
741- path = self.factory.make_tarball(self.useFixture(TempDir()).path)
742- tarball = tarfile.open(path)
743- top_level_dir = tarball.getnames()[0]
744- tempdir = self.useFixture(TempDir()).path
745- new_path = task.prepare_file(path, tempdir)
746- self.assertEqual(
747- os.path.join(tempdir, BuildPackageTask.WORKING_DIRECTORY_NAME),
748- new_path)
749- self.assertThat(new_path, DirExists())
750- self.assertThat(os.path.join(new_path, top_level_dir), DirExists())
751-
752- def test_prepare_file_extracts_zips(self):
753- # If the file downloaded is a .zip then it is extracted
754- task = BuildPackageTask()
755- top_level_dir = 'top_level'
756- tempdir = self.useFixture(TempDir()).path
757- path = os.path.join(tempdir, 'application.zip')
758- zf = zipfile.ZipFile(path, 'w')
759- try:
760- zf.writestr(os.path.join(top_level_dir, 'afile'), 'some content\n')
761- finally:
762- zf.close()
763- new_path = task.prepare_file(path, tempdir)
764- self.assertEqual(
765- os.path.join(tempdir, BuildPackageTask.WORKING_DIRECTORY_NAME),
766- new_path)
767- self.assertThat(new_path, DirExists())
768- self.assertThat(os.path.join(new_path, top_level_dir), DirExists())
769-
770- def test_prepare_file_copies_non_tarball(self):
771- # If the file downloaded is not a tarball then it is copied
772- task = BuildPackageTask()
773- source_dir = self.useFixture(TempDir()).path
774- filename = 'afile'
775- content = 'some content\n'
776- with open(os.path.join(source_dir, filename), 'w') as f:
777- f.write(content)
778- tempdir = self.useFixture(TempDir()).path
779- new_path = task.prepare_file(
780- os.path.join(source_dir, filename), tempdir)
781- self.assertEqual(
782- os.path.join(tempdir, BuildPackageTask.WORKING_DIRECTORY_NAME),
783- new_path)
784- self.assertThat(new_path, DirExists())
785- self.assertThat(os.path.join(new_path, filename),
786- FileContains(content))
787-
788- def test_save_package_destination_multiple_parents_dont_exist(self):
789- # save_package creates the destination directory if it doesn't already
790- # exist.
791- metadata = self.factory.make_metadata()
792- package_name = metadata['package_name']
793- task = BuildPackageTask()
794- path = self.useFixture(TempDir()).path
795- destination_dir = os.path.join(path, 'does', 'not', 'exist')
796- task.save_package(path, destination_dir, package_name)
797- self.assertThat(destination_dir, DirExists())
798- self.assertThat(destination_dir, HasPermissions('0755'))
799-
800- def test_save_package_destination_is_file(self):
801- # If the destination directory is actually a file, we raise an error.
802- metadata = self.factory.make_metadata()
803- package_name = metadata['package_name']
804- task = BuildPackageTask()
805- path = self.useFixture(TempDir()).path
806- destination_dir = os.path.join(path, 'some-file')
807- with open(destination_dir, 'w') as fp:
808- fp.write('foo')
809- e = self.assertRaises(
810- OSError,
811- task.save_package, path, destination_dir, package_name)
812- self.assertEqual(errno.EEXIST, e.errno)
813-
814- def test_save_package_destination_exists(self):
815- # If the destination directory already exists, we proceed normally.
816- metadata = self.factory.make_metadata()
817- package_name = metadata['package_name']
818- task = BuildPackageTask()
819- path = self.useFixture(TempDir()).path
820- destination_dir = self.useFixture(TempDir()).path
821- task.save_package(path, destination_dir, package_name)
822- self.assertThat(destination_dir, DirExists())
823-
824- def test_save_package_creates_tarball(self):
825- # save_package creates a tarball in the destination directory that's
826- # named after the package and contains whatever was in the source
827- # directory.
828- metadata = self.factory.make_metadata()
829- package_name = metadata['package_name']
830- task = BuildPackageTask()
831- path = self.useFixture(TempDir()).path
832- os.mkdir(os.path.join(path, 'a-directory'))
833- with open(os.path.join(path, 'a-file'), 'w') as fp:
834- fp.write('foo')
835- destination_dir = self.useFixture(TempDir()).path
836- destination = task.save_package(path, destination_dir, package_name)
837- self.assertEqual(
838- os.path.join(destination_dir, '%s.tar.gz' % (package_name,)),
839- destination)
840- self.assertThat(
841- destination,
842- TarballContains(['a-directory', 'a-file']))
843-
844- def test_save_package_uniquifies(self):
845- # If package-name.tar.gz exists, try package-name-1.tar.gz, etc.
846- metadata = self.factory.make_metadata()
847- package_name = metadata['package_name']
848- task = BuildPackageTask()
849- path = self.useFixture(TempDir()).path
850- os.mkdir(os.path.join(path, 'a-directory'))
851- with open(os.path.join(path, 'a-file'), 'w') as fp:
852- fp.write('foo')
853- destination_dir = self.useFixture(TempDir()).path
854- task.save_package(path, destination_dir, package_name)
855- destination = task.save_package(path, destination_dir, package_name)
856- self.assertEqual(
857- destination,
858- os.path.join(destination_dir, '%s-1.tar.gz' % (package_name,)))
859- destination = task.save_package(path, destination_dir, package_name)
860- self.assertEqual(
861- destination,
862- os.path.join(destination_dir, '%s-2.tar.gz' % (package_name,)))
863-
864 @patch('djpkgme.tasks.submit_error')
865 @patch('djpkgme.tasks.submit_pkgme_info')
866 @patch('djpkgme.tasks.BuildPackageTask.build_package')
867@@ -418,86 +381,6 @@
868 task = BuildPackageTask()
869 self.assertRaises(KeyboardInterrupt, task.run, metadata, {})
870
871- def test_exc_info_as_dict(self):
872- try:
873- raise RuntimeError('whatever')
874- except RuntimeError:
875- exc_info = sys.exc_info()
876- error_dict = tasks.exc_info_as_dict(exc_info)
877- self.assertEqual(
878- {'type': 'RuntimeError',
879- 'traceback': ''.join(traceback.format_exception(*exc_info)),
880- }, error_dict)
881-
882- @patch('djpkgme.tasks.download_file')
883- def test_prepare_icons_downloads_icons(self, mock_download):
884- icons = {
885- '48x48': 'http://whatever/something.png',
886- '64x64': 'http://whatever/foo/another.jpg',
887- }
888- path = self.useFixture(TempDir()).path
889- task = BuildPackageTask()
890- task.prepare_icons(path, icons)
891- self.assertEqual(
892- sorted(
893- [mock.call(
894- 'http://whatever/something.png',
895- name='something.png', working_dir=path),
896- mock.call(
897- 'http://whatever/foo/another.jpg',
898- name='another.jpg', working_dir=path),
899- ]),
900- sorted(mock_download.call_args_list))
901-
902- def test_prepare_icons_returns_paths(self):
903- def download_file(url, name, working_dir):
904- return '%s/++download++/%s' % (working_dir, name)
905- self.patch(tasks, 'download_file', download_file)
906- icons = {
907- '48x48': 'http://whatever/something.png',
908- '64x64': 'http://whatever/foo/another.jpg',
909- }
910- path = self.useFixture(TempDir()).path
911- task = BuildPackageTask()
912- new_icons = task.prepare_icons(path, icons)
913- self.assertEqual(
914- {'48x48': '%s/++download++/something.png' % (path,),
915- '64x64': '%s/++download++/another.jpg' % (path,),
916- }, new_icons)
917-
918- def test_prepare_metadata_default(self):
919- path = self.useFixture(TempDir()).path
920- metadata = self.factory.make_metadata()
921- task = BuildPackageTask()
922- prepared = task.prepare_metadata(path, metadata)
923- self.assertEqual(prepared, metadata)
924-
925- def test_prepare_metadata_maintainer_provided(self):
926- path = self.useFixture(TempDir()).path
927- metadata = self.factory.make_metadata()
928- metadata[MetadataBackend.MAINTAINER] = (
929- 'Upstream <upstream@example.com>')
930- task = BuildPackageTask()
931- prepared = task.prepare_metadata(path, metadata)
932- self.assertEqual(prepared, metadata)
933-
934- @patch('djpkgme.tasks.BuildPackageTask.prepare_icons')
935- def test_prepare_metadata_icons(self, prepare_icons):
936- prepare_icons.return_value = {'48x48': '/tmp/whatever.png'}
937- path = self.useFixture(TempDir()).path
938- metadata = self.factory.make_metadata()
939- expected = dict(metadata)
940- metadata['icons'] = {
941- '48x48': 'http://example.com/whatever.png',
942- }
943- expected[MetadataBackend.ICONS] = {
944- '48x48': '/tmp/whatever.png',
945- }
946- task = BuildPackageTask()
947- prepared = task.prepare_metadata(path, metadata)
948- self.assertEqual(expected, prepared)
949-
950-
951
952 class TestBuildPackageTaskIntegration(TestCaseWithFactory):
953 """Tests that actually build the package."""

Subscribers

People subscribed via source and target branches