Merge lp:~jml/pkgme-service/actually-run-pkgme into lp:pkgme-service

Proposed by Jonathan Lange
Status: Merged
Approved by: James Westby
Approved revision: 35
Merged at revision: 15
Proposed branch: lp:~jml/pkgme-service/actually-run-pkgme
Merge into: lp:pkgme-service
Diff against target: 488 lines (+239/-46)
9 files modified
fabtasks/django.py (+0/-3)
setup.py (+11/-10)
src/djpkgme/models.py (+21/-0)
src/djpkgme/tasks.py (+44/-3)
src/djpkgme/tests/factory.py (+58/-5)
src/djpkgme/tests/test_handlers.py (+6/-2)
src/djpkgme/tests/test_models.py (+18/-2)
src/djpkgme/tests/test_tasks.py (+74/-19)
test-dependencies.txt (+7/-2)
To merge this branch: bzr merge lp:~jml/pkgme-service/actually-run-pkgme
Reviewer Review Type Date Requested Status
James Westby (community) Approve
Review via email: mp+83024@code.launchpad.net

Description of the change

At long last, actually running pkgme-binary from pkgme-service.

I'm a little too zonked to provide much more details right now, but happy to answer questions.

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

Hi,

This broadly looks good, and is certainly an improvement.

As it stands this test will only work when run from source:

437 + import acceptance

as pkgme-binary doesn't include this when installed. We could move
it in to the devportalbinary namespace?

Thanks,

James

review: Approve
Revision history for this message
Jonathan Lange (jml) wrote :

Merged. Filed bug 893675, bug 893674 and bug 893563.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'fabtasks/django.py'
--- fabtasks/django.py 2011-08-30 17:41:11 +0000
+++ fabtasks/django.py 2011-11-22 14:09:24 +0000
@@ -1,6 +1,3 @@
1import os
2import os.path
3
4from fabric.api import local1from fabric.api import local
52
63
74
=== modified file 'setup.py'
--- setup.py 2011-11-09 11:14:13 +0000
+++ setup.py 2011-11-22 14:09:24 +0000
@@ -1,22 +1,23 @@
1from setuptools import setup, find_packages1from setuptools import setup, find_packages
22
3setup(3setup(
4 name = "django-pkgme",4 name="django-pkgme",
5 version = "0.1",5 version="0.1",
6 author = "Canonical Consumer Applications",6 author="Canonical Consumer Applications",
7 author_email = "canonical-consumer-applications@lists.launchpad.net",7 author_email="canonical-consumer-applications@lists.launchpad.net",
8 license = "AGPL3",8 license="AGPL3",
9 install_requires = [9 install_requires=[
10 'django==1.3',10 'django==1.3',
11 'django-piston',11 'django-piston',
12 # XXX: Until https://github.com/ask/django-celery/pull/88 is fixed, we12 # XXX: Until https://github.com/ask/django-celery/pull/88 is fixed, we
13 # have to use the older release. Review this when django-celery13 # have to use the older release. Review this when django-celery
14 # releases the version after 2.4.1. -- jml14 # releases the version after 2.4.1. -- jml
15 'django-celery==2.4.0',15 'django-celery==2.4.0',
16 'fixtures',
16 'south==0.7.3',17 'south==0.7.3',
17 'oauth',18 'oauth',
18 ],19 ],
19 zip_safe = False,20 zip_safe=False,
20 packages = find_packages('src'),21 packages=find_packages('src'),
21 package_dir = {'': 'src'},22 package_dir={'': 'src'},
22)23)
2324
=== modified file 'src/djpkgme/models.py'
--- src/djpkgme/models.py 2011-09-01 21:46:47 +0000
+++ src/djpkgme/models.py 2011-11-22 14:09:24 +0000
@@ -2,6 +2,11 @@
2from django.db import models2from django.db import models
3from datetime import datetime3from datetime import datetime
44
5from devportalbinary.binary import (
6 CATEGORIES,
7 PACKAGE_NAME,
8 TAGLINE,
9 )
510
6class AppMetadata(models.Model):11class AppMetadata(models.Model):
7 # ID of the app, according to myapps12 # ID of the app, according to myapps
@@ -16,8 +21,12 @@
16 categories = models.CharField(max_length=128, blank=True)21 categories = models.CharField(max_length=128, blank=True)
17 department = models.CharField(max_length=64, blank=True)22 department = models.CharField(max_length=64, blank=True)
18 keywords = models.TextField(blank=True, default='')23 keywords = models.TextField(blank=True, default='')
24 # XXX: We *think* this is the URL to the tarball that contains the app
25 # that we are packaging.
19 package_url = models.CharField(max_length=256)26 package_url = models.CharField(max_length=256)
20 icon_url = models.CharField(max_length=256)27 icon_url = models.CharField(max_length=256)
28 # XXX: This should not be required, or even sent. It's not part of the
29 # package.
21 screenshot_url = models.CharField(max_length=256)30 screenshot_url = models.CharField(max_length=256)
22 price = models.DecimalField(max_digits=7, decimal_places=2, null=True)31 price = models.DecimalField(max_digits=7, decimal_places=2, null=True)
23 callback_url = models.CharField(max_length=256)32 callback_url = models.CharField(max_length=256)
@@ -35,6 +44,18 @@
35 # Location of the built package44 # Location of the built package
36 packaged_app_url = models.CharField(max_length=256)45 packaged_app_url = models.CharField(max_length=256)
3746
47 def as_dict(self):
48 """Return this metadata as a dict.
49
50 The contents of that dict should be suitable for dumping as JSON into
51 devportal-metadata.json (see lp:pkgme-binary).
52 """
53 return {
54 CATEGORIES: self.categories,
55 PACKAGE_NAME: self.package_name,
56 TAGLINE: self.tagline,
57 }
58
38 def update_guesses(self, guesses):59 def update_guesses(self, guesses):
39 """Updates the guess fields based on a result from pkgme."""60 """Updates the guess fields based on a result from pkgme."""
40 other = {}61 other = {}
4162
=== modified file 'src/djpkgme/tasks.py'
--- src/djpkgme/tasks.py 2011-09-01 21:46:47 +0000
+++ src/djpkgme/tasks.py 2011-11-22 14:09:24 +0000
@@ -1,4 +1,8 @@
1import json1import json
2import os
3import shutil
4import tarfile
5
2from celery.registry import tasks6from celery.registry import tasks
3from celery.task import Task7from celery.task import Task
4from django.conf import settings8from django.conf import settings
@@ -10,7 +14,13 @@
10 OAuthSignatureMethod_PLAINTEXT,14 OAuthSignatureMethod_PLAINTEXT,
11 )15 )
1216
13import djpkgme.mock_pkgme as pkgme17from devportalbinary.binary import METADATA_FILE
18from devportalbinary.database import download_file
19from fixtures import TempDir
20from pkgme import write_packaging
21from pkgme.debuild import build_source_package
22
23from djpkgme import mock_pkgme
1424
1525
16def oauth_sign_request(url):26def oauth_sign_request(url):
@@ -34,7 +44,7 @@
3444
35class GuessPackagingDetailsTask(Task):45class GuessPackagingDetailsTask(Task):
36 def run(self, metadata):46 def run(self, metadata):
37 result = pkgme.guess(metadata)47 result = mock_pkgme.guess(metadata)
38 metadata.update_guesses(result)48 metadata.update_guesses(result)
39 submit_pkgme_info(metadata)49 submit_pkgme_info(metadata)
40 return result50 return result
@@ -42,8 +52,39 @@
4252
4353
44class BuildPackageTask(Task):54class BuildPackageTask(Task):
55
56 def build_package(self, metadata, overrides, output_tar_path):
57 """Build packaging for 'metadata' and store it all in 'output_tar_path'.
58 """
59 with TempDir() as temp_dir:
60 # Download, extract and remove the tarball.
61 output_dir = temp_dir.path
62 path = download_file(metadata.package_url, output_dir)
63 # XXX: 'package' is an arbitrary name. Maybe we should pick a
64 # better arbitrary name?
65 extraction_path = os.path.join(output_dir, 'package')
66 with tarfile.open(path) as t:
67 t.extractall(extraction_path)
68 os.unlink(path)
69 # Write the metadata out to a special file, where the binary
70 # backend will find it.
71 metadata_filename = os.path.join(extraction_path, METADATA_FILE)
72 with open(metadata_filename, 'w') as metadata_file:
73 json.dump(metadata.as_dict(), metadata_file)
74 # Run pkgme: generate the packaging and build a source package.
75 write_packaging(extraction_path, allowed_backend_names=['binary'])
76 build_source_package(extraction_path, False)
77 # Create a new tarball containing the source package and the
78 # metadata file.
79 os.rename(
80 metadata_filename, os.path.join(output_dir, METADATA_FILE))
81 shutil.rmtree(extraction_path)
82 with tarfile.open(output_tar_path, 'w') as t:
83 for name in os.listdir(output_dir):
84 t.add(os.path.join(output_dir, name), name)
85
45 def run(self, metadata, overrides):86 def run(self, metadata, overrides):
46 result = pkgme.build_package(metadata, overrides)87 result = self.build_package(metadata, overrides)
47 metadata.packaged_app_url = result88 metadata.packaged_app_url = result
48 metadata.save()89 metadata.save()
49 submit_pkgme_info(metadata)90 submit_pkgme_info(metadata)
5091
=== modified file 'src/djpkgme/tests/factory.py'
--- src/djpkgme/tests/factory.py 2011-09-01 20:35:22 +0000
+++ src/djpkgme/tests/factory.py 2011-11-22 14:09:24 +0000
@@ -1,9 +1,24 @@
1from django.test import TestCase
2from itertools import count1from itertools import count
32import os
3
4from django.test import TestCase as DjangoTestCase
5from fixtures import (
6 EnvironmentVariableFixture,
7 Fixture,
8 TempDir,
9 )
10from testtools import (
11 RunTest,
12 TestCase,
13 )
14
15from devportalbinary.database import PackageDatabase
4from djpkgme.models import AppMetadata16from djpkgme.models import AppMetadata
517
6__all__ = ['TestCaseWithFactory']18__all__ = [
19 'EmptyBinaryPackageDatabase',
20 'TestCaseWithFactory',
21 ]
722
823
9class PkgmeObjectFactory(object):24class PkgmeObjectFactory(object):
@@ -20,7 +35,9 @@
20 return prefix + str(self.get_unique_integer())35 return prefix + str(self.get_unique_integer())
2136
22 def make_metadata(self, myapps_id=None, callback_url=None,37 def make_metadata(self, myapps_id=None, callback_url=None,
23 with_guesses=False):38 with_guesses=False, package_name=None):
39 if package_name is None:
40 package_name = self.get_unique_string('package-name')
24 if myapps_id is None:41 if myapps_id is None:
25 myapps_id = self.get_unique_integer()42 myapps_id = self.get_unique_integer()
26 if callback_url is None:43 if callback_url is None:
@@ -39,7 +56,43 @@
39 return AppMetadata.objects.create(myapps_id=myapps_id,56 return AppMetadata.objects.create(myapps_id=myapps_id,
40 executable=executable, distroarchseries=distroarchseries,57 executable=executable, distroarchseries=distroarchseries,
41 dependencies=dependencies, other_guesses=other_guesses,58 dependencies=dependencies, other_guesses=other_guesses,
42 callback_url=callback_url)59 callback_url=callback_url, package_name=package_name)
60
61
62class DjangoRunner(RunTest):
63 """Use with run_tests_with to run Django tests.
64
65 Django's test case has a couple of hooks that it runs before and after
66 tests to isolate transactions and do other fun things
67 <https://docs.djangoproject.com/en/dev/topics/testing/#django.test.TestCase>.
68
69 Use this runner to get that same effect with testtools.
70 """
71
72 class MyDjangoTestCase(DjangoTestCase):
73 def test_foo(self):
74 pass
75
76 def __init__(self, case, handlers=None):
77 super(DjangoRunner, self).__init__(case, handlers=handlers)
78 self._django_case = self.MyDjangoTestCase('test_foo')
79
80 def _run_core(self):
81 self._run_user(self._django_case._pre_setup)
82 super(DjangoRunner, self)._run_core()
83 self._run_user(self._django_case._post_teardown)
84
85
86class EmptyBinaryPackageDatabase(Fixture):
87
88 def setUp(self):
89 super(EmptyBinaryPackageDatabase, self).setUp()
90 db_directory = self.useFixture(TempDir()).path
91 db_path = os.path.join(db_directory, 'test.db')
92 self.useFixture(
93 EnvironmentVariableFixture(PackageDatabase.ENV_VAR, db_path))
94
4395
44class TestCaseWithFactory(TestCase):96class TestCaseWithFactory(TestCase):
45 factory = PkgmeObjectFactory()97 factory = PkgmeObjectFactory()
98 run_tests_with = DjangoRunner
4699
=== modified file 'src/djpkgme/tests/test_handlers.py'
--- src/djpkgme/tests/test_handlers.py 2011-09-01 20:35:22 +0000
+++ src/djpkgme/tests/test_handlers.py 2011-11-22 14:09:24 +0000
@@ -78,16 +78,20 @@
7878
7979
80 @patch('httplib2.Http.request')80 @patch('httplib2.Http.request')
81 def test_no_overrides_success(self, mock_request):81 @patch('djpkgme.tasks.BuildPackageTask.build_package')
82 def test_no_overrides_success(self, mock_build_package, mock_request):
82 request = Mock()83 request = Mock()
84 mock_build_package.return_value = 'foo'
83 request.data = {'metadata': test_metadata, 'overrides': {}}85 request.data = {'metadata': test_metadata, 'overrides': {}}
84 handler = PackageHandler()86 handler = PackageHandler()
85 response = handler.create(request)87 response = handler.create(request)
86 self.assertEqual({'status': 'success'}, response)88 self.assertEqual({'status': 'success'}, response)
8789
88 @patch('httplib2.Http.request')90 @patch('httplib2.Http.request')
89 def test_overrides_success(self, mock_request):91 @patch('djpkgme.tasks.BuildPackageTask.build_package')
92 def test_overrides_success(self, mock_build_package, mock_request):
90 request = Mock()93 request = Mock()
94 mock_build_package.return_value = 'foo'
91 request.data = {'metadata': test_metadata, 'overrides': {95 request.data = {'metadata': test_metadata, 'overrides': {
92 'executable': 'bin/foo',96 'executable': 'bin/foo',
93 'deps': ['libfoo', 'libbar'],97 'deps': ['libfoo', 'libbar'],
9498
=== modified file 'src/djpkgme/tests/test_models.py'
--- src/djpkgme/tests/test_models.py 2011-09-01 21:46:47 +0000
+++ src/djpkgme/tests/test_models.py 2011-11-22 14:09:24 +0000
@@ -1,6 +1,13 @@
1from factory import TestCaseWithFactory1from djpkgme.tests.factory import TestCaseWithFactory
2
3from devportalbinary.binary import (
4 CATEGORIES,
5 PACKAGE_NAME,
6 TAGLINE,
7 )
28
3class AppMetadataTestCase(TestCaseWithFactory):9class AppMetadataTestCase(TestCaseWithFactory):
10
4 def test_update_guesses_doesnt_mutate_argument(self):11 def test_update_guesses_doesnt_mutate_argument(self):
5 """Calling update_guesses doesn't modify the dict we pass in."""12 """Calling update_guesses doesn't modify the dict we pass in."""
6 metadata = self.factory.make_metadata(with_guesses=True)13 metadata = self.factory.make_metadata(with_guesses=True)
@@ -50,4 +57,13 @@
5057
51 expected = ['dependencies', 'pkgme_distro_arch_series',58 expected = ['dependencies', 'pkgme_distro_arch_series',
52 'pkgme_extra_data', 'packaged_app_url']59 'pkgme_extra_data', 'packaged_app_url']
53 self.assertEquals(set(expected), set(info))
54\ No newline at end of file60\ No newline at end of file
61 self.assertEquals(set(expected), set(info))
62
63 def test_as_dict(self):
64 metadata = self.factory.make_metadata()
65 expected = {
66 CATEGORIES: metadata.categories,
67 PACKAGE_NAME: metadata.package_name,
68 TAGLINE: metadata.tagline,
69 }
70 self.assertEqual(expected, metadata.as_dict())
5571
=== modified file 'src/djpkgme/tests/test_tasks.py'
--- src/djpkgme/tests/test_tasks.py 2011-09-01 21:46:47 +0000
+++ src/djpkgme/tests/test_tasks.py 2011-11-22 14:09:24 +0000
@@ -1,15 +1,29 @@
1# Run tasks eagerly for tests. Taken from 1# Run tasks eagerly for tests. Taken from
2# http://ask.github.com/celery/cookbook/unit-testing.html2# http://ask.github.com/celery/cookbook/unit-testing.html
33
4import json4import json
5import os
6import shutil
7import tarfile
8
9from fixtures import TempDir
5from mock import patch10from mock import patch
611
7from djpkgme.tasks import GuessPackagingDetailsTask, BuildPackageTask12from devportalbinary.binary import METADATA_FILE
8from djpkgme.tests.factory import TestCaseWithFactory13from djpkgme import tasks
14from djpkgme.tasks import (
15 BuildPackageTask,
16 GuessPackagingDetailsTask,
17 )
18from djpkgme.tests.factory import (
19 EmptyBinaryPackageDatabase,
20 TestCaseWithFactory,
21 )
22
923
10class GuessPackagingDetailsTaskTestCase(TestCaseWithFactory):24class GuessPackagingDetailsTaskTestCase(TestCaseWithFactory):
11 @patch('httplib2.Http.request')25 @patch('httplib2.Http.request')
12 @patch('djpkgme.tasks.pkgme')26 @patch('djpkgme.tasks.mock_pkgme')
13 def test_guess_details_is_called(self, mock_pkgme, mock_request):27 def test_guess_details_is_called(self, mock_pkgme, mock_request):
14 mock_request.return_value = ({}, '')28 mock_request.return_value = ({}, '')
15 mock_pkgme.guess.return_value = {}29 mock_pkgme.guess.return_value = {}
@@ -23,7 +37,7 @@
23 self.assertEqual(1, mock_pkgme.guess.call_count)37 self.assertEqual(1, mock_pkgme.guess.call_count)
2438
25 @patch('httplib2.Http.request')39 @patch('httplib2.Http.request')
26 @patch('djpkgme.tasks.pkgme')40 @patch('djpkgme.tasks.mock_pkgme')
27 def test_result_is_submitted(self, mock_pkgme, mock_request):41 def test_result_is_submitted(self, mock_pkgme, mock_request):
28 mock_request.return_value = ({}, '')42 mock_request.return_value = ({}, '')
29 mock_pkgme.guess.return_value = {}43 mock_pkgme.guess.return_value = {}
@@ -35,20 +49,21 @@
35 "pkgme_extra_data": {"pkgme_id": 1, "executable": ""}49 "pkgme_extra_data": {"pkgme_id": 1, "executable": ""}
36 }50 }
3751
38 result = GuessPackagingDetailsTask.delay(metadata)52 GuessPackagingDetailsTask.delay(metadata)
3953
40 self.assertEqual(1, mock_request.call_count)54 self.assertEqual(1, mock_request.call_count)
41 args, kwargs = mock_request.call_args55 args, kwargs = mock_request.call_args
42 self.assertEqual((metadata.callback_url,), args)56 self.assertEqual(args, (metadata.callback_url,))
43 self.assertEqual(json.loads(kwargs['body']), expected)57 self.assertEqual(expected, json.loads(kwargs['body']))
44 58
4559
46class BuildPackageTaskTestCase(TestCaseWithFactory):60class BuildPackageTaskTestCase(TestCaseWithFactory):
61
47 @patch('httplib2.Http.request')62 @patch('httplib2.Http.request')
48 @patch('djpkgme.tasks.pkgme')63 @patch('djpkgme.tasks.BuildPackageTask.build_package')
49 def test_guess_details_is_called(self, mock_pkgme, mock_request):64 def test_build_package_is_called(self, mock_build_package, mock_request):
50 mock_request.return_value = ({}, '')65 mock_request.return_value = ({}, '')
51 mock_pkgme.build_package.return_value = "foo"66 mock_build_package.return_value = "foo"
5267
53 metadata = self.factory.make_metadata()68 metadata = self.factory.make_metadata()
54 overrides = {}69 overrides = {}
@@ -57,18 +72,18 @@
5772
58 self.assertEqual('foo', result.get())73 self.assertEqual('foo', result.get())
59 self.assertTrue(result.successful())74 self.assertTrue(result.successful())
60 self.assertEqual(1, mock_pkgme.build_package.call_count)75 self.assertEqual(1, mock_build_package.call_count)
6176
62 @patch('httplib2.Http.request')77 @patch('httplib2.Http.request')
63 @patch('djpkgme.tasks.pkgme')78 @patch('djpkgme.tasks.BuildPackageTask.build_package')
64 def test_result_is_submitted(self, mock_pkgme, mock_request):79 def test_result_is_submitted(self, mock_build_package, mock_request):
65 mock_request.return_value = ({}, '')80 mock_request.return_value = ({}, '')
66 mock_pkgme.build_package.return_value = "foo"81 mock_build_package.return_value = "foo"
6782
68 metadata = self.factory.make_metadata()83 metadata = self.factory.make_metadata()
69 overrides = {}84 overrides = {}
7085
71 result = BuildPackageTask.delay(metadata, overrides)86 BuildPackageTask.delay(metadata, overrides)
7287
73 self.assertEqual(1, mock_request.call_count)88 self.assertEqual(1, mock_request.call_count)
74 args, kwargs = mock_request.call_args89 args, kwargs = mock_request.call_args
@@ -78,5 +93,45 @@
78 "pkgme_distro_arch_series": [],93 "pkgme_distro_arch_series": [],
79 "pkgme_extra_data": {"pkgme_id": 1, "executable": ""}94 "pkgme_extra_data": {"pkgme_id": 1, "executable": ""}
80 }95 }
81 self.assertEqual((metadata.callback_url,), args)96 self.assertEqual(args, (metadata.callback_url,))
82 self.assertEqual(json.loads(kwargs['body']), expected)97 self.assertEqual(expected, json.loads(kwargs['body']))
98
99 def download_file(self, url, directory):
100 # For the moment, the test we just want to create a valid tarball in
101 # the directory.
102 import acceptance
103 binary_path = os.path.join(
104 os.path.dirname(acceptance.__file__), 'data', 'gtk', 'test_gtk')
105 temp_dir = self.useFixture(TempDir()).path
106 nothing_file_path = os.path.join(temp_dir, 'binary-file')
107 shutil.copyfile(binary_path, nothing_file_path)
108 tarball_path = os.path.join(directory, 'tarball.tar.gz')
109 tarball = tarfile.open(tarball_path, 'w')
110 tarball.add(nothing_file_path, os.path.basename(nothing_file_path))
111 tarball.close()
112 return tarball_path
113
114 def test_writes_packaging_for_tarball(self):
115 # BuildPackageTask.build_package runs pkgme to build the package and
116 # outputs a tarball to the given path. Here we're testing the happy
117 # case.
118 metadata = self.factory.make_metadata()
119 task = BuildPackageTask()
120 self.patch(tasks, 'download_file', self.download_file)
121 self.useFixture(EmptyBinaryPackageDatabase())
122 temp_dir = self.useFixture(TempDir()).path
123 output_tar_path = os.path.join(temp_dir, 'output.tar.gz')
124 task.build_package(metadata, {}, output_tar_path)
125 with tarfile.open(output_tar_path) as output_tar:
126 self.assertEqual(
127 sorted(
128 ['%s_0.dsc' % (metadata.package_name,),
129 '%s_0.tar.gz' % (metadata.package_name,),
130 '%s_0_source.build' % (metadata.package_name,),
131 '%s_0_source.changes' % (metadata.package_name,),
132 METADATA_FILE,
133 ]),
134 sorted(output_tar.getnames()))
135
136 # XXX: Write a test directly for the contents of METADATA_FILE?
137 # XXX: Check the contents of the tarball in the source package?
83138
=== modified file 'test-dependencies.txt'
--- test-dependencies.txt 2011-11-09 11:14:13 +0000
+++ test-dependencies.txt 2011-11-22 14:09:24 +0000
@@ -1,4 +1,9 @@
1coverage
1django-kombu2django-kombu
2coverage3mock
3piston-mini-client4piston-mini-client
4mock5testtools
6# These aren't test requirements, but we want to fetch these dependencies from
7# bzr and this is the only way I can figure out how to do this -- jml
8-e bzr+ssh://bazaar.launchpad.net/+branch/pkgme#egg=pkgme
9-e bzr+ssh://bazaar.launchpad.net/+branch/pkgme-binary#egg=pkgme-binary

Subscribers

People subscribed via source and target branches