1
# Copyright (C) 2011 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""Tools for dealing with the Launchpad API without using launchpadlib.
22
from bzrlib import tests
23
from bzrlib.plugins import launchpad
24
from bzrlib.plugins.launchpad import lp_api_lite
26
class _JSONParserFeature(tests.Feature):
29
return lp_api_lite.json is not None
31
def feature_name(self):
32
return 'simplejson or json'
34
JSONParserFeature = _JSONParserFeature()
36
_example_response = r"""
40
"next_collection_link": "https://api.launchpad.net/1.0/ubuntu/+archive/primary?distro_series=%2Fubuntu%2Flucid&exact_match=true&source_name=%22bzr%22&status=Published&ws.op=getPublishedSources&ws.start=1&ws.size=1",
43
"package_creator_link": "https://api.launchpad.net/1.0/~maxb",
44
"package_signer_link": "https://api.launchpad.net/1.0/~jelmer",
45
"source_package_name": "bzr",
46
"removal_comment": null,
47
"display_name": "bzr 2.1.4-0ubuntu1 in lucid",
48
"date_made_pending": null,
49
"source_package_version": "2.1.4-0ubuntu1",
50
"date_superseded": null,
51
"http_etag": "\"9ba966152dec474dc0fe1629d0bbce2452efaf3b-5f4c3fbb3eaf26d502db4089777a9b6a0537ffab\"",
52
"self_link": "https://api.launchpad.net/1.0/ubuntu/+archive/primary/+sourcepub/1750327",
53
"distro_series_link": "https://api.launchpad.net/1.0/ubuntu/lucid",
54
"component_name": "main",
55
"status": "Published",
58
"date_published": "2011-05-30T06:09:58.653984+00:00",
59
"removed_by_link": null,
60
"section_name": "devel",
61
"resource_type_link": "https://api.launchpad.net/1.0/#source_package_publishing_history",
62
"archive_link": "https://api.launchpad.net/1.0/ubuntu/+archive/primary",
63
"package_maintainer_link": "https://api.launchpad.net/1.0/~ubuntu-devel-discuss-lists",
64
"date_created": "2011-05-30T05:19:12.233621+00:00",
65
"scheduled_deletion_date": null
70
_no_versions_response = '{"total_size": 0, "start": 0, "entries": []}'
73
class TestLatestPublication(tests.TestCase):
75
def make_latest_publication(self, archive='ubuntu', series='natty',
77
return lp_api_lite.LatestPublication(archive, series, project)
80
latest_pub = self.make_latest_publication()
81
self.assertEqual('ubuntu', latest_pub._archive)
82
self.assertEqual('natty', latest_pub._series)
83
self.assertEqual('bzr', latest_pub._project)
84
self.assertEqual('Release', latest_pub._pocket)
86
def test__archive_URL(self):
87
latest_pub = self.make_latest_publication()
89
'https://api.launchpad.net/1.0/ubuntu/+archive/primary',
90
latest_pub._archive_URL())
92
def test__publication_status_for_ubuntu(self):
93
latest_pub = self.make_latest_publication()
94
self.assertEqual('Published', latest_pub._publication_status())
96
def test__publication_status_for_debian(self):
97
latest_pub = self.make_latest_publication(archive='debian')
98
self.assertEqual('Pending', latest_pub._publication_status())
100
def test_pocket(self):
101
latest_pub = self.make_latest_publication(series='natty-proposed')
102
self.assertEqual('natty', latest_pub._series)
103
self.assertEqual('Proposed', latest_pub._pocket)
105
def test_series_None(self):
106
latest_pub = self.make_latest_publication(series=None)
107
self.assertEqual('ubuntu', latest_pub._archive)
108
self.assertEqual(None, latest_pub._series)
109
self.assertEqual('bzr', latest_pub._project)
110
self.assertEqual('Release', latest_pub._pocket)
112
def test__query_params(self):
113
latest_pub = self.make_latest_publication()
114
self.assertEqual({'ws.op': 'getPublishedSources',
115
'exact_match': 'true',
116
'source_name': '"bzr"',
117
'status': 'Published',
119
'distro_series': '/ubuntu/natty',
121
}, latest_pub._query_params())
123
def test__query_params_no_series(self):
124
latest_pub = self.make_latest_publication(series=None)
125
self.assertEqual({'ws.op': 'getPublishedSources',
126
'exact_match': 'true',
127
'source_name': '"bzr"',
128
'status': 'Published',
131
}, latest_pub._query_params())
133
def test__query_params_pocket(self):
134
latest_pub = self.make_latest_publication(series='natty-proposed')
135
self.assertEqual({'ws.op': 'getPublishedSources',
136
'exact_match': 'true',
137
'source_name': '"bzr"',
138
'status': 'Published',
140
'distro_series': '/ubuntu/natty',
141
'pocket': 'Proposed',
142
}, latest_pub._query_params())
144
def test__query_URL(self):
145
latest_pub = self.make_latest_publication()
146
# we explicitly sort params, so we can be sure this URL matches exactly
148
'https://api.launchpad.net/1.0/ubuntu/+archive/primary'
149
'?distro_series=%2Fubuntu%2Fnatty&exact_match=true'
150
'&pocket=Release&source_name=%22bzr%22&status=Published'
151
'&ws.op=getPublishedSources&ws.size=1',
152
latest_pub._query_URL())
154
def DONT_test__gracefully_handle_failed_rpc_connection(self):
155
# TODO: This test kind of sucks. We intentionally create an arbitrary
156
# port and don't listen to it, because we want the request to fail.
157
# However, it seems to take 1s for it to timeout. Is there a way
158
# to make it fail faster?
159
latest_pub = self.make_latest_publication()
161
s.bind(('127.0.0.1', 0))
162
addr, port = s.getsockname()
163
latest_pub.LP_API_ROOT = 'http://%s:%s/' % (addr, port)
165
self.assertIs(None, latest_pub._get_lp_info())
167
def DONT_test__query_launchpad(self):
168
# TODO: This is a test that we are making a valid request against
169
# launchpad. This seems important, but it is slow, requires net
170
# access, and requires launchpad to be up and running. So for
171
# now, it is commented out for production tests.
172
latest_pub = self.make_latest_publication()
173
json_txt = latest_pub._get_lp_info()
174
self.assertIsNot(None, json_txt)
175
if lp_api_lite.json is None:
176
# We don't have a way to parse the text
178
# The content should be a valid json result
179
content = lp_api_lite.json.loads(json_txt)
180
entries = content['entries'] # It should have an 'entries' field.
181
# ws.size should mean we get 0 or 1, and there should be something
182
self.assertEqual(1, len(entries))
184
self.assertEqual('bzr', entry['source_package_name'])
185
version = entry['source_package_version']
186
self.assertIsNot(None, version)
188
def test__get_lp_info_no_json(self):
189
# If we can't parse the json, we don't make the query.
190
self.overrideAttr(lp_api_lite, 'json', None)
191
latest_pub = self.make_latest_publication()
192
self.assertIs(None, latest_pub._get_lp_info())
194
def test__parse_json_info_no_module(self):
195
# If a json parsing module isn't available, we just return None here.
196
self.overrideAttr(lp_api_lite, 'json', None)
197
latest_pub = self.make_latest_publication()
198
self.assertIs(None, latest_pub._parse_json_info(_example_response))
200
def test__parse_json_example_response(self):
201
self.requireFeature(JSONParserFeature)
202
latest_pub = self.make_latest_publication()
203
content = latest_pub._parse_json_info(_example_response)
204
self.assertIsNot(None, content)
205
self.assertEqual(2, content['total_size'])
206
entries = content['entries']
207
self.assertEqual(1, len(entries))
209
self.assertEqual('bzr', entry['source_package_name'])
210
self.assertEqual("2.1.4-0ubuntu1", entry["source_package_version"])
212
def test__parse_json_not_json(self):
213
self.requireFeature(JSONParserFeature)
214
latest_pub = self.make_latest_publication()
215
self.assertIs(None, latest_pub._parse_json_info('Not_valid_json'))
217
def test_get_latest_version_no_response(self):
218
latest_pub = self.make_latest_publication()
219
latest_pub._get_lp_info = lambda: None
220
self.assertEqual(None, latest_pub.get_latest_version())
222
def test_get_latest_version_no_json(self):
223
self.overrideAttr(lp_api_lite, 'json', None)
224
latest_pub = self.make_latest_publication()
225
self.assertEqual(None, latest_pub.get_latest_version())
227
def test_get_latest_version_invalid_json(self):
228
self.requireFeature(JSONParserFeature)
229
latest_pub = self.make_latest_publication()
230
latest_pub._get_lp_info = lambda: "not json"
231
self.assertEqual(None, latest_pub.get_latest_version())
233
def test_get_latest_version_no_versions(self):
234
self.requireFeature(JSONParserFeature)
235
latest_pub = self.make_latest_publication()
236
latest_pub._get_lp_info = lambda: _no_versions_response
237
self.assertEqual(None, latest_pub.get_latest_version())
239
def test_get_latest_version_missing_entries(self):
240
# Launchpad's no-entries response does have an empty entries value.
241
# However, lets test that we handle other failures without tracebacks
242
self.requireFeature(JSONParserFeature)
243
latest_pub = self.make_latest_publication()
244
latest_pub._get_lp_info = lambda: '{}'
245
self.assertEqual(None, latest_pub.get_latest_version())
247
def test_get_latest_version_invalid_entries(self):
248
# Make sure we sanely handle a json response we don't understand
249
self.requireFeature(JSONParserFeature)
250
latest_pub = self.make_latest_publication()
251
latest_pub._get_lp_info = lambda: '{"entries": {"a": 1}}'
252
self.assertEqual(None, latest_pub.get_latest_version())
254
def test_get_latest_version_example(self):
255
self.requireFeature(JSONParserFeature)
256
latest_pub = self.make_latest_publication()
257
latest_pub._get_lp_info = lambda: _example_response
258
self.assertEqual("2.1.4-0ubuntu1", latest_pub.get_latest_version())
260
def DONT_test_get_latest_version_from_launchpad(self):
261
self.requireFeature(JSONParserFeature)
262
latest_pub = self.make_latest_publication()
263
self.assertIsNot(None, latest_pub.get_latest_version())
266
class TestIsUpToDate(tests.TestCase):
268
def assertPackageBranchRe(self, url, user, archive, series, project):
269
m = launchpad._package_branch.search(url)
271
self.fail('package_branch regex did not match url: %s' % (url,))
273
(user, archive, series, project),
274
m.group('user', 'archive', 'series', 'project'))
276
def assertNotPackageBranch(self, url):
277
self.assertIs(None, launchpad._get_package_branch_info(url))
279
def assertBranchInfo(self, url, archive, series, project):
280
self.assertEqual((archive, series, project),
281
launchpad._get_package_branch_info(url))
283
def test_package_branch_regex(self):
284
self.assertPackageBranchRe(
285
'http://bazaar.launchpad.net/+branch/ubuntu/foo',
286
None, 'ubuntu', None, 'foo')
287
self.assertPackageBranchRe(
288
'bzr+ssh://bazaar.launchpad.net/+branch/ubuntu/natty/foo',
289
None, 'ubuntu', 'natty/', 'foo')
290
self.assertPackageBranchRe(
291
'sftp://bazaar.launchpad.net/+branch/debian/foo',
292
None, 'debian', None, 'foo')
293
self.assertPackageBranchRe(
294
'http://bazaar.launchpad.net/+branch/debian/sid/foo',
295
None, 'debian', 'sid/', 'foo')
296
self.assertPackageBranchRe(
297
'http://bazaar.launchpad.net/+branch'
298
'/~ubuntu-branches/ubuntu/natty/foo/natty',
299
'~ubuntu-branches/', 'ubuntu', 'natty/', 'foo')
300
self.assertPackageBranchRe(
301
'http://bazaar.launchpad.net/+branch'
302
'/~user/ubuntu/natty/foo/test',
303
'~user/', 'ubuntu', 'natty/', 'foo')
305
def test_package_branch_doesnt_match(self):
306
self.assertNotPackageBranch('http://example.com/ubuntu/foo')
307
self.assertNotPackageBranch(
308
'http://bazaar.launchpad.net/+branch/bzr')
309
self.assertNotPackageBranch(
310
'http://bazaar.launchpad.net/+branch/~bzr-pqm/bzr/bzr.dev')
311
# Not a packaging branch because ~user isn't ~ubuntu-branches
312
self.assertNotPackageBranch(
313
'http://bazaar.launchpad.net/+branch'
314
'/~user/ubuntu/natty/foo/natty')
316
def test__get_package_branch_info(self):
317
self.assertBranchInfo(
318
'bzr+ssh://bazaar.launchpad.net/+branch/ubuntu/natty/foo',
319
'ubuntu', 'natty', 'foo')
320
self.assertBranchInfo(
321
'bzr+ssh://bazaar.launchpad.net/+branch'
322
'/~ubuntu-branches/ubuntu/natty/foo/natty',
323
'ubuntu', 'natty', 'foo')
324
self.assertBranchInfo(
325
'http://bazaar.launchpad.net/+branch'
326
'/~ubuntu-branches/debian/sid/foo/sid',
327
'debian', 'sid', 'foo')
330
class TestGetMostRecentTag(tests.TestCaseWithMemoryTransport):
332
def make_simple_builder(self):
333
builder = self.make_branch_builder('tip')
334
builder.build_snapshot('A', [], [
335
('add', ('', 'root-id', 'directory', None))])
336
b = builder.get_branch()
337
b.tags.set_tag('tip-1.0', 'A')
338
return builder, b, b.tags.get_tag_dict()
340
def test_get_most_recent_tag_tip(self):
341
builder, b, tag_dict = self.make_simple_builder()
342
self.assertEqual('tip-1.0',
343
lp_api_lite.get_most_recent_tag(tag_dict, b))
345
def test_get_most_recent_tag_older(self):
346
builder, b, tag_dict = self.make_simple_builder()
347
builder.build_snapshot('B', ['A'], [])
348
self.assertEqual('B', b.last_revision())
349
self.assertEqual('tip-1.0',
350
lp_api_lite.get_most_recent_tag(tag_dict, b))