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.
24
from ...tests import features
25
from .. import launchpad
26
from . import lp_api_lite
27
from testtools.matchers import DocTestMatches
30
class _JSONParserFeature(features.Feature):
33
return lp_api_lite.json is not None
35
def feature_name(self):
36
return 'simplejson or json'
39
JSONParserFeature = _JSONParserFeature()
42
_example_response = r"""
46
"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",
49
"package_creator_link": "https://api.launchpad.net/1.0/~maxb",
50
"package_signer_link": "https://api.launchpad.net/1.0/~jelmer",
51
"source_package_name": "bzr",
52
"removal_comment": null,
53
"display_name": "bzr 2.1.4-0ubuntu1 in lucid",
54
"date_made_pending": null,
55
"source_package_version": "2.1.4-0ubuntu1",
56
"date_superseded": null,
57
"http_etag": "\"9ba966152dec474dc0fe1629d0bbce2452efaf3b-5f4c3fbb3eaf26d502db4089777a9b6a0537ffab\"",
58
"self_link": "https://api.launchpad.net/1.0/ubuntu/+archive/primary/+sourcepub/1750327",
59
"distro_series_link": "https://api.launchpad.net/1.0/ubuntu/lucid",
60
"component_name": "main",
61
"status": "Published",
64
"date_published": "2011-05-30T06:09:58.653984+00:00",
65
"removed_by_link": null,
66
"section_name": "devel",
67
"resource_type_link": "https://api.launchpad.net/1.0/#source_package_publishing_history",
68
"archive_link": "https://api.launchpad.net/1.0/ubuntu/+archive/primary",
69
"package_maintainer_link": "https://api.launchpad.net/1.0/~ubuntu-devel-discuss-lists",
70
"date_created": "2011-05-30T05:19:12.233621+00:00",
71
"scheduled_deletion_date": null
76
_no_versions_response = '{"total_size": 0, "start": 0, "entries": []}'
79
class TestLatestPublication(tests.TestCase):
81
def make_latest_publication(self, archive='ubuntu', series='natty',
83
return lp_api_lite.LatestPublication(archive, series, project)
85
def assertPlace(self, place, archive, series, project):
86
lp = lp_api_lite.LatestPublication(archive, series, project)
87
self.assertEqual(place, lp.place())
90
latest_pub = self.make_latest_publication()
91
self.assertEqual('ubuntu', latest_pub._archive)
92
self.assertEqual('natty', latest_pub._series)
93
self.assertEqual('bzr', latest_pub._project)
94
self.assertEqual('Release', latest_pub._pocket)
96
def test__archive_URL(self):
97
latest_pub = self.make_latest_publication()
99
'https://api.launchpad.net/1.0/ubuntu/+archive/primary',
100
latest_pub._archive_URL())
102
def test__publication_status_for_ubuntu(self):
103
latest_pub = self.make_latest_publication()
104
self.assertEqual('Published', latest_pub._publication_status())
106
def test__publication_status_for_debian(self):
107
latest_pub = self.make_latest_publication(archive='debian')
108
self.assertEqual('Pending', latest_pub._publication_status())
110
def test_pocket(self):
111
latest_pub = self.make_latest_publication(series='natty-proposed')
112
self.assertEqual('natty', latest_pub._series)
113
self.assertEqual('Proposed', latest_pub._pocket)
115
def test_series_None(self):
116
latest_pub = self.make_latest_publication(series=None)
117
self.assertEqual('ubuntu', latest_pub._archive)
118
self.assertEqual(None, latest_pub._series)
119
self.assertEqual('bzr', latest_pub._project)
120
self.assertEqual('Release', latest_pub._pocket)
122
def test__query_params(self):
123
latest_pub = self.make_latest_publication()
124
self.assertEqual({'ws.op': 'getPublishedSources',
125
'exact_match': 'true',
126
'source_name': '"bzr"',
127
'status': 'Published',
129
'distro_series': '/ubuntu/natty',
131
}, latest_pub._query_params())
133
def test__query_params_no_series(self):
134
latest_pub = self.make_latest_publication(series=None)
135
self.assertEqual({'ws.op': 'getPublishedSources',
136
'exact_match': 'true',
137
'source_name': '"bzr"',
138
'status': 'Published',
141
}, latest_pub._query_params())
143
def test__query_params_pocket(self):
144
latest_pub = self.make_latest_publication(series='natty-proposed')
145
self.assertEqual({'ws.op': 'getPublishedSources',
146
'exact_match': 'true',
147
'source_name': '"bzr"',
148
'status': 'Published',
150
'distro_series': '/ubuntu/natty',
151
'pocket': 'Proposed',
152
}, latest_pub._query_params())
154
def test__query_URL(self):
155
latest_pub = self.make_latest_publication()
156
# we explicitly sort params, so we can be sure this URL matches exactly
158
'https://api.launchpad.net/1.0/ubuntu/+archive/primary'
159
'?distro_series=%2Fubuntu%2Fnatty&exact_match=true'
160
'&pocket=Release&source_name=%22bzr%22&status=Published'
161
'&ws.op=getPublishedSources&ws.size=1',
162
latest_pub._query_URL())
164
def DONT_test__gracefully_handle_failed_rpc_connection(self):
165
# TODO: This test kind of sucks. We intentionally create an arbitrary
166
# port and don't listen to it, because we want the request to fail.
167
# However, it seems to take 1s for it to timeout. Is there a way
168
# to make it fail faster?
169
latest_pub = self.make_latest_publication()
171
s.bind(('127.0.0.1', 0))
172
addr, port = s.getsockname()
173
latest_pub.LP_API_ROOT = 'http://%s:%s/' % (addr, port)
175
self.assertIs(None, latest_pub._get_lp_info())
177
def DONT_test__query_launchpad(self):
178
# TODO: This is a test that we are making a valid request against
179
# launchpad. This seems important, but it is slow, requires net
180
# access, and requires launchpad to be up and running. So for
181
# now, it is commented out for production tests.
182
latest_pub = self.make_latest_publication()
183
json_txt = latest_pub._get_lp_info()
184
self.assertIsNot(None, json_txt)
185
if lp_api_lite.json is None:
186
# We don't have a way to parse the text
188
# The content should be a valid json result
189
content = lp_api_lite.json.loads(json_txt)
190
entries = content['entries'] # It should have an 'entries' field.
191
# ws.size should mean we get 0 or 1, and there should be something
192
self.assertEqual(1, len(entries))
194
self.assertEqual('bzr', entry['source_package_name'])
195
version = entry['source_package_version']
196
self.assertIsNot(None, version)
198
def test__get_lp_info_no_json(self):
199
# If we can't parse the json, we don't make the query.
200
self.overrideAttr(lp_api_lite, 'json', None)
201
latest_pub = self.make_latest_publication()
202
self.assertIs(None, latest_pub._get_lp_info())
204
def test__parse_json_info_no_module(self):
205
# If a json parsing module isn't available, we just return None here.
206
self.overrideAttr(lp_api_lite, 'json', None)
207
latest_pub = self.make_latest_publication()
208
self.assertIs(None, latest_pub._parse_json_info(_example_response))
210
def test__parse_json_example_response(self):
211
self.requireFeature(JSONParserFeature)
212
latest_pub = self.make_latest_publication()
213
content = latest_pub._parse_json_info(_example_response)
214
self.assertIsNot(None, content)
215
self.assertEqual(2, content['total_size'])
216
entries = content['entries']
217
self.assertEqual(1, len(entries))
219
self.assertEqual('bzr', entry['source_package_name'])
220
self.assertEqual("2.1.4-0ubuntu1", entry["source_package_version"])
222
def test__parse_json_not_json(self):
223
self.requireFeature(JSONParserFeature)
224
latest_pub = self.make_latest_publication()
225
self.assertIs(None, latest_pub._parse_json_info('Not_valid_json'))
227
def test_get_latest_version_no_response(self):
228
latest_pub = self.make_latest_publication()
229
latest_pub._get_lp_info = lambda: None
230
self.assertEqual(None, latest_pub.get_latest_version())
232
def test_get_latest_version_no_json(self):
233
self.overrideAttr(lp_api_lite, 'json', None)
234
latest_pub = self.make_latest_publication()
235
self.assertEqual(None, latest_pub.get_latest_version())
237
def test_get_latest_version_invalid_json(self):
238
self.requireFeature(JSONParserFeature)
239
latest_pub = self.make_latest_publication()
240
latest_pub._get_lp_info = lambda: "not json"
241
self.assertEqual(None, latest_pub.get_latest_version())
243
def test_get_latest_version_no_versions(self):
244
self.requireFeature(JSONParserFeature)
245
latest_pub = self.make_latest_publication()
246
latest_pub._get_lp_info = lambda: _no_versions_response
247
self.assertEqual(None, latest_pub.get_latest_version())
249
def test_get_latest_version_missing_entries(self):
250
# Launchpad's no-entries response does have an empty entries value.
251
# However, lets test that we handle other failures without tracebacks
252
self.requireFeature(JSONParserFeature)
253
latest_pub = self.make_latest_publication()
254
latest_pub._get_lp_info = lambda: '{}'
255
self.assertEqual(None, latest_pub.get_latest_version())
257
def test_get_latest_version_invalid_entries(self):
258
# Make sure we sanely handle a json response we don't understand
259
self.requireFeature(JSONParserFeature)
260
latest_pub = self.make_latest_publication()
261
latest_pub._get_lp_info = lambda: '{"entries": {"a": 1}}'
262
self.assertEqual(None, latest_pub.get_latest_version())
264
def test_get_latest_version_example(self):
265
self.requireFeature(JSONParserFeature)
266
latest_pub = self.make_latest_publication()
267
latest_pub._get_lp_info = lambda: _example_response
268
self.assertEqual("2.1.4-0ubuntu1", latest_pub.get_latest_version())
270
def DONT_test_get_latest_version_from_launchpad(self):
271
self.requireFeature(JSONParserFeature)
272
latest_pub = self.make_latest_publication()
273
self.assertIsNot(None, latest_pub.get_latest_version())
275
def test_place(self):
276
self.assertPlace('Ubuntu', 'ubuntu', None, 'bzr')
277
self.assertPlace('Ubuntu Natty', 'ubuntu', 'natty', 'bzr')
278
self.assertPlace('Ubuntu Natty Proposed', 'ubuntu', 'natty-proposed',
280
self.assertPlace('Debian', 'debian', None, 'bzr')
281
self.assertPlace('Debian Sid', 'debian', 'sid', 'bzr')
284
class TestIsUpToDate(tests.TestCase):
286
def assertPackageBranchRe(self, url, user, archive, series, project):
287
m = launchpad._package_branch.search(url)
289
self.fail('package_branch regex did not match url: %s' % (url,))
291
(user, archive, series, project),
292
m.group('user', 'archive', 'series', 'project'))
294
def assertNotPackageBranch(self, url):
295
self.assertIs(None, launchpad._get_package_branch_info(url))
297
def assertBranchInfo(self, url, archive, series, project):
298
self.assertEqual((archive, series, project),
299
launchpad._get_package_branch_info(url))
301
def test_package_branch_regex(self):
302
self.assertPackageBranchRe(
303
'http://bazaar.launchpad.net/+branch/ubuntu/foo',
304
None, 'ubuntu', None, 'foo')
305
self.assertPackageBranchRe(
306
'bzr+ssh://bazaar.launchpad.net/+branch/ubuntu/natty/foo',
307
None, 'ubuntu', 'natty/', 'foo')
308
self.assertPackageBranchRe(
309
'sftp://bazaar.launchpad.net/+branch/debian/foo',
310
None, 'debian', None, 'foo')
311
self.assertPackageBranchRe(
312
'http://bazaar.launchpad.net/+branch/debian/sid/foo',
313
None, 'debian', 'sid/', 'foo')
314
self.assertPackageBranchRe(
315
'http://bazaar.launchpad.net/+branch'
316
'/~ubuntu-branches/ubuntu/natty/foo/natty',
317
'~ubuntu-branches/', 'ubuntu', 'natty/', 'foo')
318
self.assertPackageBranchRe(
319
'http://bazaar.launchpad.net/+branch'
320
'/~user/ubuntu/natty/foo/test',
321
'~user/', 'ubuntu', 'natty/', 'foo')
323
def test_package_branch_doesnt_match(self):
324
self.assertNotPackageBranch('http://example.com/ubuntu/foo')
325
self.assertNotPackageBranch(
326
'http://bazaar.launchpad.net/+branch/bzr')
327
self.assertNotPackageBranch(
328
'http://bazaar.launchpad.net/+branch/~bzr-pqm/bzr/bzr.dev')
329
# Not a packaging branch because ~user isn't ~ubuntu-branches
330
self.assertNotPackageBranch(
331
'http://bazaar.launchpad.net/+branch'
332
'/~user/ubuntu/natty/foo/natty')
333
# Older versions of bzr-svn/hg/git did not set Branch.base until after
334
# they called Branch.__init__().
335
self.assertNotPackageBranch(None)
337
def test__get_package_branch_info(self):
338
self.assertBranchInfo(
339
'bzr+ssh://bazaar.launchpad.net/+branch/ubuntu/natty/foo',
340
'ubuntu', 'natty', 'foo')
341
self.assertBranchInfo(
342
'bzr+ssh://bazaar.launchpad.net/+branch'
343
'/~ubuntu-branches/ubuntu/natty/foo/natty',
344
'ubuntu', 'natty', 'foo')
345
self.assertBranchInfo(
346
'http://bazaar.launchpad.net/+branch'
347
'/~ubuntu-branches/debian/sid/foo/sid',
348
'debian', 'sid', 'foo')
351
class TestGetMostRecentTag(tests.TestCaseWithMemoryTransport):
353
def make_simple_builder(self):
354
builder = self.make_branch_builder('tip')
355
builder.build_snapshot([], [
356
('add', ('', b'root-id', 'directory', None))],
358
b = builder.get_branch()
359
b.tags.set_tag('tip-1.0', b'A')
360
return builder, b, b.tags.get_tag_dict()
362
def test_get_most_recent_tag_tip(self):
363
builder, b, tag_dict = self.make_simple_builder()
364
self.assertEqual('tip-1.0',
365
lp_api_lite.get_most_recent_tag(tag_dict, b))
367
def test_get_most_recent_tag_older(self):
368
builder, b, tag_dict = self.make_simple_builder()
369
builder.build_snapshot([b'A'], [], revision_id=b'B')
370
self.assertEqual(b'B', b.last_revision())
371
self.assertEqual('tip-1.0',
372
lp_api_lite.get_most_recent_tag(tag_dict, b))
375
class StubLatestPublication(object):
377
def __init__(self, latest):
381
def get_latest_version(self):
386
return 'Ubuntu Natty'
389
class TestReportFreshness(tests.TestCaseWithMemoryTransport):
392
super(TestReportFreshness, self).setUp()
393
builder = self.make_branch_builder('tip')
394
builder.build_snapshot([], [
395
('add', ('', b'root-id', 'directory', None))],
397
self.branch = builder.get_branch()
399
def assertFreshnessReports(self, verbosity, latest_version, content):
400
"""Assert that lp_api_lite.report_freshness reports the given content.
402
:param verbosity: The reporting level
403
:param latest_version: The version reported by StubLatestPublication
404
:param content: The expected content. This should be in DocTest form.
406
orig_log_len = len(self.get_log())
407
lp_api_lite.report_freshness(self.branch, verbosity,
408
StubLatestPublication(latest_version))
409
new_content = self.get_log()[orig_log_len:]
410
# Strip out lines that have LatestPublication.get_* because those are
411
# timing related lines. While interesting to log for now, they aren't
412
# something we want to be testing
413
new_content = new_content.split('\n')
415
if (len(new_content) > 0
416
and 'LatestPublication.get_' in new_content[0]):
417
new_content = new_content[1:]
418
new_content = '\n'.join(new_content)
419
self.assertThat(new_content,
420
DocTestMatches(content,
421
doctest.ELLIPSIS | doctest.REPORT_UDIFF))
423
def test_verbosity_off_skips_check(self):
424
# We force _get_package_branch_info so that we know it would otherwise
425
# try to connect to launcphad
426
self.overrideAttr(launchpad, '_get_package_branch_info',
427
lambda x: ('ubuntu', 'natty', 'bzr'))
428
self.overrideAttr(lp_api_lite, 'LatestPublication',
429
lambda *args: self.fail('Tried to query launchpad'))
430
c = self.branch.get_config_stack()
431
c.set('launchpad.packaging_verbosity', 'off')
432
orig_log_len = len(self.get_log())
433
launchpad._check_is_up_to_date(self.branch)
434
new_content = self.get_log()[orig_log_len:]
435
self.assertContainsRe(new_content,
436
'not checking memory.*/tip/ because verbosity is turned off')
438
def test_verbosity_off(self):
439
latest_pub = StubLatestPublication('1.0-1ubuntu2')
440
lp_api_lite.report_freshness(self.branch, 'off', latest_pub)
441
self.assertFalse(latest_pub.called)
443
def test_verbosity_all_out_of_date_smoke(self):
444
self.branch.tags.set_tag('1.0-1ubuntu1', b'A')
445
self.assertFreshnessReports('all', '1.0-1ubuntu2',
446
' INFO Most recent Ubuntu Natty version: 1.0-1ubuntu2\n'
447
'Packaging branch version: 1.0-1ubuntu1\n'
448
'Packaging branch status: OUT-OF-DATE\n')
451
class Test_GetNewestVersions(tests.TestCaseWithMemoryTransport):
454
super(Test_GetNewestVersions, self).setUp()
455
builder = self.make_branch_builder('tip')
456
builder.build_snapshot([], [
457
('add', ('', b'root-id', 'directory', None))],
459
self.branch = builder.get_branch()
461
def assertLatestVersions(self, latest_branch_version, pub_version):
462
if latest_branch_version is not None:
463
self.branch.tags.set_tag(latest_branch_version, b'A')
464
latest_pub = StubLatestPublication(pub_version)
465
self.assertEqual((pub_version, latest_branch_version),
466
lp_api_lite._get_newest_versions(self.branch, latest_pub))
468
def test_no_tags(self):
469
self.assertLatestVersions(None, '1.0-1ubuntu2')
471
def test_out_of_date(self):
472
self.assertLatestVersions('1.0-1ubuntu1', '1.0-1ubuntu2')
474
def test_up_to_date(self):
475
self.assertLatestVersions('1.0-1ubuntu2', '1.0-1ubuntu2')
477
def test_missing(self):
478
self.assertLatestVersions(None, None)
481
class Test_ReportFreshness(tests.TestCase):
483
def assertReportedFreshness(self, verbosity, latest_ver, branch_latest_ver,
484
content, place='Ubuntu Natty'):
485
"""Assert that lp_api_lite.report_freshness reports the given content.
488
def report_func(value):
489
reported.append(value)
491
lp_api_lite._report_freshness(latest_ver, branch_latest_ver, place,
492
verbosity, report_func)
493
new_content = '\n'.join(reported)
494
self.assertThat(new_content,
495
DocTestMatches(content,
496
doctest.ELLIPSIS | doctest.REPORT_UDIFF))
498
def test_verbosity_minimal_no_tags(self):
499
self.assertReportedFreshness('minimal', '1.0-1ubuntu2', None,
500
'Branch is OUT-OF-DATE, Ubuntu Natty has 1.0-1ubuntu2\n')
502
def test_verbosity_minimal_out_of_date(self):
503
self.assertReportedFreshness('minimal', '1.0-1ubuntu2', '1.0-1ubuntu1',
504
'1.0-1ubuntu1 is OUT-OF-DATE,'
505
' Ubuntu Natty has 1.0-1ubuntu2\n')
507
def test_verbosity_minimal_up_to_date(self):
508
self.assertReportedFreshness('minimal', '1.0-1ubuntu2', '1.0-1ubuntu2',
511
def test_verbosity_minimal_missing(self):
512
self.assertReportedFreshness('minimal', None, None,
515
def test_verbosity_short_out_of_date(self):
516
self.assertReportedFreshness('short', '1.0-1ubuntu2', '1.0-1ubuntu1',
517
'1.0-1ubuntu1 is OUT-OF-DATE,'
518
' Ubuntu Natty has 1.0-1ubuntu2\n')
520
def test_verbosity_short_up_to_date(self):
521
self.assertReportedFreshness('short', '1.0-1ubuntu2', '1.0-1ubuntu2',
522
'1.0-1ubuntu2 is CURRENT in Ubuntu Natty')
524
def test_verbosity_short_missing(self):
525
self.assertReportedFreshness('short', None, None,
526
'Ubuntu Natty is MISSING a version')
528
def test_verbosity_all_no_tags(self):
529
self.assertReportedFreshness('all', '1.0-1ubuntu2', None,
530
'Most recent Ubuntu Natty version: 1.0-1ubuntu2\n'
531
'Packaging branch version: None\n'
532
'Packaging branch status: OUT-OF-DATE\n')
534
def test_verbosity_all_out_of_date(self):
535
self.assertReportedFreshness('all', '1.0-1ubuntu2', '1.0-1ubuntu1',
536
'Most recent Ubuntu Natty version: 1.0-1ubuntu2\n'
537
'Packaging branch version: 1.0-1ubuntu1\n'
538
'Packaging branch status: OUT-OF-DATE\n')
540
def test_verbosity_all_up_to_date(self):
541
self.assertReportedFreshness('all', '1.0-1ubuntu2', '1.0-1ubuntu2',
542
'Most recent Ubuntu Natty version: 1.0-1ubuntu2\n'
543
'Packaging branch status: CURRENT\n')
545
def test_verbosity_all_missing(self):
546
self.assertReportedFreshness('all', None, None,
547
'Most recent Ubuntu Natty version: MISSING\n')
549
def test_verbosity_None_is_all(self):
550
self.assertReportedFreshness(None, '1.0-1ubuntu2', '1.0-1ubuntu2',
551
'Most recent Ubuntu Natty version: 1.0-1ubuntu2\n'
552
'Packaging branch status: CURRENT\n')