13
13
# You should have received a copy of the GNU General Public License
14
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
"""Tests for directory lookup through Launchpad.net"""
23
21
from bzrlib import (
29
24
from bzrlib.branch import Branch
30
25
from bzrlib.directory_service import directories
31
from bzrlib.tests import (
35
TestCaseWithMemoryTransport
37
from bzrlib.plugins.launchpad import (
26
from bzrlib.tests import TestCase, TestCaseWithMemoryTransport
27
from bzrlib.transport import get_transport
28
from bzrlib.plugins.launchpad import _register_directory
41
29
from bzrlib.plugins.launchpad.lp_directory import (
42
30
LaunchpadDirectory)
43
from bzrlib.plugins.launchpad.account import get_lp_login, set_lp_login
44
from bzrlib.tests import http_server
47
def load_tests(standard_tests, module, loader):
48
result = loader.suiteClass()
49
t_tests, remaining_tests = tests.split_suite_by_condition(
50
standard_tests, tests.condition_isinstance((
53
transport_scenarios = [
54
('http', dict(server_class=PreCannedHTTPServer,)),
56
if features.HTTPSServerFeature.available():
57
transport_scenarios.append(
58
('https', dict(server_class=PreCannedHTTPSServer,)),
60
tests.multiply_tests(t_tests, transport_scenarios, result)
62
# No parametrization for the remaining tests
63
result.addTests(remaining_tests)
31
from bzrlib.plugins.launchpad.account import get_lp_login
68
34
class FakeResolveFactory(object):
70
35
def __init__(self, test, expected_path, result):
72
37
self._expected_path = expected_path
73
38
self._result = result
74
self._submitted = False
76
40
def __call__(self, path):
77
41
self._test.assertEqual(self._expected_path, path)
80
44
def submit(self, service):
81
45
self._service_url = service.service_url
82
self._submitted = True
83
46
return self._result
86
class LocalDirectoryURLTests(TestCaseInTempDir):
87
"""Tests for branch urls that we try to pass through local resolution."""
89
def assertResolve(self, expected, url, submitted=False):
90
path = url[url.index(':')+1:].lstrip('/')
91
factory = FakeResolveFactory(self, path,
92
dict(urls=['bzr+ssh://fake-resolved']))
93
directory = LaunchpadDirectory()
94
self.assertEqual(expected,
95
directory._resolve(url, factory, _lp_login='user'))
96
# We are testing local resolution, and the fallback when necessary.
97
self.assertEqual(submitted, factory._submitted)
99
def test_short_form(self):
100
self.assertResolve('bzr+ssh://bazaar.launchpad.net/+branch/apt',
103
def test_two_part_form(self):
104
self.assertResolve('bzr+ssh://bazaar.launchpad.net/+branch/apt/2.2',
107
def test_two_part_plus_subdir(self):
108
# We allow you to pass more than just what resolves. That way you can
109
# do things like "bzr log lp:apt/2.2/BUGS"
110
# Though the virtual FS implementation currently aborts when given a
111
# URL like this, rather than letting you recurse upwards to find the
112
# real branch at lp:apt/2.2
113
self.assertResolve('bzr+ssh://bazaar.launchpad.net/+branch/apt/2.2/BUGS',
116
def test_user_expansion(self):
117
self.assertResolve('bzr+ssh://bazaar.launchpad.net/~user/apt/foo',
120
def test_ubuntu(self):
121
# Confirmed against xmlrpc. If you don't have a ~user, xmlrpc doesn't
122
# care that you are asking for 'ubuntu'
123
self.assertResolve('bzr+ssh://bazaar.launchpad.net/+branch/ubuntu',
126
def test_ubuntu_invalid(self):
127
"""Invalid ubuntu urls don't crash.
129
:seealso: http://pad.lv/843900
131
# This ought to be natty-updates.
132
self.assertRaises(errors.InvalidURL,
135
'ubuntu:natty/updates/smartpm')
137
def test_ubuntu_apt(self):
138
self.assertResolve('bzr+ssh://bazaar.launchpad.net/+branch/ubuntu/apt',
141
def test_ubuntu_natty_apt(self):
143
'bzr+ssh://bazaar.launchpad.net/+branch/ubuntu/natty/apt',
144
'lp:ubuntu/natty/apt')
146
def test_ubuntu_natty_apt_filename(self):
148
'bzr+ssh://bazaar.launchpad.net/+branch/ubuntu/natty/apt/filename',
149
'lp:ubuntu/natty/apt/filename')
151
def test_user_two_part(self):
152
# We fall back to the ResolveFactory. The real Launchpad one will raise
153
# InvalidURL for this case.
154
self.assertResolve('bzr+ssh://fake-resolved', 'lp:~jameinel/apt',
157
def test_user_three_part(self):
158
self.assertResolve('bzr+ssh://bazaar.launchpad.net/~jameinel/apt/foo',
159
'lp:~jameinel/apt/foo')
161
def test_user_three_part_plus_filename(self):
163
'bzr+ssh://bazaar.launchpad.net/~jameinel/apt/foo/fname',
164
'lp:~jameinel/apt/foo/fname')
166
def test_user_ubuntu_two_part(self):
167
self.assertResolve('bzr+ssh://fake-resolved', 'lp:~jameinel/ubuntu',
169
self.assertResolve('bzr+ssh://fake-resolved', 'lp:~jameinel/debian',
172
def test_user_ubuntu_three_part(self):
173
self.assertResolve('bzr+ssh://fake-resolved',
174
'lp:~jameinel/ubuntu/natty', submitted=True)
175
self.assertResolve('bzr+ssh://fake-resolved',
176
'lp:~jameinel/debian/sid', submitted=True)
178
def test_user_ubuntu_four_part(self):
179
self.assertResolve('bzr+ssh://fake-resolved',
180
'lp:~jameinel/ubuntu/natty/project', submitted=True)
181
self.assertResolve('bzr+ssh://fake-resolved',
182
'lp:~jameinel/debian/sid/project', submitted=True)
184
def test_user_ubuntu_five_part(self):
186
'bzr+ssh://bazaar.launchpad.net/~jameinel/ubuntu/natty/apt/branch',
187
'lp:~jameinel/ubuntu/natty/apt/branch')
189
'bzr+ssh://bazaar.launchpad.net/~jameinel/debian/sid/apt/branch',
190
'lp:~jameinel/debian/sid/apt/branch')
192
def test_user_ubuntu_five_part_plus_subdir(self):
194
'bzr+ssh://bazaar.launchpad.net/~jameinel/ubuntu/natty/apt/branch/f',
195
'lp:~jameinel/ubuntu/natty/apt/branch/f')
197
'bzr+ssh://bazaar.launchpad.net/~jameinel/debian/sid/apt/branch/f',
198
'lp:~jameinel/debian/sid/apt/branch/f')
200
def test_handles_special_lp(self):
201
self.assertResolve('bzr+ssh://bazaar.launchpad.net/+branch/apt', 'lp:apt')
202
self.assertResolve('bzr+ssh://bazaar.launchpad.net/+branch/apt',
204
self.assertResolve('bzr+ssh://bazaar.launchpad.net/+branch/apt',
205
'lp://production/apt')
206
self.assertResolve('bzr+ssh://bazaar.launchpad.dev/+branch/apt',
208
self.assertResolve('bzr+ssh://bazaar.staging.launchpad.net/+branch/apt',
210
self.assertResolve('bzr+ssh://bazaar.qastaging.launchpad.net/+branch/apt',
211
'lp://qastaging/apt')
212
self.assertResolve('bzr+ssh://bazaar.demo.launchpad.net/+branch/apt',
215
def test_debug_launchpad_uses_resolver(self):
216
self.assertResolve('bzr+ssh://bazaar.launchpad.net/+branch/bzr',
217
'lp:bzr', submitted=False)
218
debug.debug_flags.add('launchpad')
219
self.addCleanup(debug.debug_flags.discard, 'launchpad')
220
self.assertResolve('bzr+ssh://fake-resolved', 'lp:bzr', submitted=True)
223
class DirectoryUrlTests(TestCaseInTempDir):
49
class DirectoryUrlTests(TestCase):
224
50
"""Tests for branch urls through Launchpad.net directory"""
226
52
def test_short_form(self):
321
134
'sftp://bazaar.launchpad.net/~apt/apt/devel',
322
135
'http://bazaar.launchpad.net/~apt/apt/devel']))
323
136
directory = LaunchpadDirectory()
324
self.assertEqual('http://bazaar.launchpad.net/~apt/apt/devel',
137
self.assertEquals('http://bazaar.launchpad.net/~apt/apt/devel',
325
138
directory._resolve('lp:///apt', factory))
327
def test_with_login_avoid_resolve_factory(self):
140
def test_rewrite_bzr_ssh_launchpad_net(self):
328
141
# Test that bzr+ssh URLs get rewritten to include the user's
329
142
# Launchpad ID (assuming we know the Launchpad ID).
330
143
factory = FakeResolveFactory(
331
144
self, 'apt', dict(urls=[
332
'bzr+ssh://my-super-custom/special/devel',
145
'bzr+ssh://bazaar.launchpad.net/~apt/apt/devel',
333
146
'http://bazaar.launchpad.net/~apt/apt/devel']))
334
147
directory = LaunchpadDirectory()
336
'bzr+ssh://bazaar.launchpad.net/+branch/apt',
149
'bzr+ssh://username@bazaar.launchpad.net/~apt/apt/devel',
337
150
directory._resolve('lp:///apt', factory, _lp_login='username'))
339
152
def test_no_rewrite_of_other_bzr_ssh(self):
340
# Test that we don't rewrite bzr+ssh URLs for other
153
# Test that we don't rewrite bzr+ssh URLs for other
341
154
self.assertEqual(None, get_lp_login())
342
155
factory = FakeResolveFactory(
343
156
self, 'apt', dict(urls=[
344
157
'bzr+ssh://example.com/~apt/apt/devel',
345
158
'http://bazaar.launchpad.net/~apt/apt/devel']))
346
159
directory = LaunchpadDirectory()
347
self.assertEqual('bzr+ssh://example.com/~apt/apt/devel',
160
self.assertEquals('bzr+ssh://example.com/~apt/apt/devel',
348
161
directory._resolve('lp:///apt', factory))
350
163
# TODO: check we get an error if the url is unreasonable
391
181
return '!unexpected look_up value!'
393
183
directories.remove('lp:')
394
directories.remove('ubuntu:')
395
directories.remove('debianlp:')
396
184
directories.register('lp:', FooService, 'Map lp URLs to local urls')
397
185
self.addCleanup(_register_directory)
398
self.addCleanup(directories.remove, 'lp:')
399
t = transport.get_transport('lp:///apt')
400
branch = Branch.open_from_transport(t)
186
self.addCleanup(lambda: directories.remove('lp:'))
187
transport = get_transport('lp:///apt')
188
branch = Branch.open_from_transport(transport)
401
189
self.assertEqual(target_branch.base, branch.base)
404
class PredefinedRequestHandler(http_server.TestingHTTPRequestHandler):
405
"""Request handler for a unique and pre-defined request.
407
The only thing we care about here is that we receive a connection. But
408
since we want to dialog with a real http client, we have to send it correct
411
We expect to receive a *single* request nothing more (and we won't even
412
check what request it is), the tests will recognize us from our response.
415
def handle_one_request(self):
416
tcs = self.server.test_case_server
417
requestline = self.rfile.readline()
418
self.MessageClass(self.rfile, 0)
419
if requestline.startswith('POST'):
420
# The body should be a single line (or we don't know where it ends
421
# and we don't want to issue a blocking read)
422
self.rfile.readline()
424
self.wfile.write(tcs.canned_response)
427
class PreCannedServerMixin(object):
430
super(PreCannedServerMixin, self).__init__(
431
request_handler=PredefinedRequestHandler)
432
# Bytes read and written by the server
434
self.bytes_written = 0
435
self.canned_response = None
438
class PreCannedHTTPServer(PreCannedServerMixin, http_server.HttpServer):
442
if features.HTTPSServerFeature.available():
443
from bzrlib.tests import https_server
444
class PreCannedHTTPSServer(PreCannedServerMixin, https_server.HTTPSServer):
448
class TestXMLRPCTransport(tests.TestCase):
454
super(TestXMLRPCTransport, self).setUp()
455
self.server = self.server_class()
456
self.server.start_server()
457
self.addCleanup(self.server.stop_server)
458
# Ensure we don't clobber env
459
self.overrideEnv('BZR_LP_XMLRPC_URL', None)
460
# Ensure we use the right certificates for https.
461
# FIXME: There should be a better way but the only alternative I can
462
# think of involves carrying the ca_certs through the lp_registration
463
# infrastructure to _urllib2_wrappers... -- vila 2012-01-20
464
bzrlib.global_state.cmdline_overrides._from_cmdline(
465
['ssl.ca_certs=%s' % ssl_certs.build_path('ca.crt')])
467
def set_canned_response(self, server, path):
468
response_format = '''HTTP/1.1 200 OK\r
469
Date: Tue, 11 Jul 2006 04:32:56 GMT\r
470
Server: Apache/2.0.54 (Fedora)\r
471
Last-Modified: Sun, 23 Apr 2006 19:35:20 GMT\r
472
ETag: "56691-23-38e9ae00"\r
473
Accept-Ranges: bytes\r
474
Content-Length: %(length)d\r
476
Content-Type: text/plain; charset=UTF-8\r
478
<?xml version='1.0'?>
486
<value><string>bzr+ssh://bazaar.launchpad.net/%(path)s</string></value>
487
<value><string>http://bazaar.launchpad.net/%(path)s</string></value>
488
</data></array></value>
495
length = 334 + 2 * len(path)
496
server.canned_response = response_format % dict(length=length,
499
def do_request(self, server_url):
500
os.environ['BZR_LP_XMLRPC_URL'] = self.server.get_url()
501
service = lp_registration.LaunchpadService()
502
resolve = lp_registration.ResolveLaunchpadPathRequest('bzr')
503
result = resolve.submit(service)
506
def test_direct_request(self):
507
self.set_canned_response(self.server, '~bzr-pqm/bzr/bzr.dev')
508
result = self.do_request(self.server.get_url())
509
urls = result.get('urls', None)
510
self.assertIsNot(None, urls)
512
['bzr+ssh://bazaar.launchpad.net/~bzr-pqm/bzr/bzr.dev',
513
'http://bazaar.launchpad.net/~bzr-pqm/bzr/bzr.dev'],
515
# FIXME: we need to test with a real proxy, I can't find a way so simulate
516
# CONNECT without leaving one server hanging the test :-/ Since that maybe
517
# related to the leaking tests problems, I'll punt for now -- vila 20091030
520
class TestDebuntuExpansions(TestCaseInTempDir):
521
"""Test expansions for ubuntu: and debianlp: schemes."""
524
super(TestDebuntuExpansions, self).setUp()
525
self.directory = LaunchpadDirectory()
527
def _make_factory(self, package='foo', distro='ubuntu', series=None):
529
path = '%s/%s' % (distro, package)
530
url_suffix = '~branch/%s/%s' % (distro, package)
532
path = '%s/%s/%s' % (distro, series, package)
533
url_suffix = '~branch/%s/%s/%s' % (distro, series, package)
534
return FakeResolveFactory(
535
self, path, dict(urls=[
536
'http://bazaar.launchpad.net/' + url_suffix]))
538
def assertURL(self, expected_url, shortcut, package='foo', distro='ubuntu',
540
factory = self._make_factory(package=package, distro=distro,
542
self.assertEqual('http://bazaar.launchpad.net/~branch/' + expected_url,
543
self.directory._resolve(shortcut, factory))
547
def test_bogus_distro(self):
548
self.assertRaises(errors.InvalidURL,
549
self.directory._resolve, 'gentoo:foo')
551
def test_trick_bogus_distro_u(self):
552
self.assertRaises(errors.InvalidURL,
553
self.directory._resolve, 'utube:foo')
555
def test_trick_bogus_distro_d(self):
556
self.assertRaises(errors.InvalidURL,
557
self.directory._resolve, 'debuntu:foo')
559
def test_missing_ubuntu_distroseries_without_project(self):
560
# Launchpad does not hold source packages for Intrepid. Missing or
561
# bogus distroseries with no project name is treated like a project.
562
self.assertURL('ubuntu/intrepid', 'ubuntu:intrepid', package='intrepid')
564
def test_missing_ubuntu_distroseries_with_project(self):
565
# Launchpad does not hold source packages for Intrepid. Missing or
566
# bogus distroseries with a project name is treated like an unknown
567
# series (i.e. we keep it verbatim).
568
self.assertURL('ubuntu/intrepid/foo',
569
'ubuntu:intrepid/foo', series='intrepid')
571
def test_missing_debian_distroseries(self):
572
# Launchpad does not hold source packages for unstable. Missing or
573
# bogus distroseries is treated like a project.
574
self.assertURL('debian/sid',
575
'debianlp:sid', package='sid', distro='debian')
577
# Ubuntu Default distro series.
579
def test_ubuntu_default_distroseries_expansion(self):
580
self.assertURL('ubuntu/foo', 'ubuntu:foo')
582
def test_ubuntu_natty_distroseries_expansion(self):
583
self.assertURL('ubuntu/natty/foo', 'ubuntu:natty/foo', series='natty')
585
def test_ubuntu_n_distroseries_expansion(self):
586
self.assertURL('ubuntu/natty/foo', 'ubuntu:n/foo', series='natty')
588
def test_ubuntu_maverick_distroseries_expansion(self):
589
self.assertURL('ubuntu/maverick/foo', 'ubuntu:maverick/foo',
592
def test_ubuntu_m_distroseries_expansion(self):
593
self.assertURL('ubuntu/maverick/foo', 'ubuntu:m/foo', series='maverick')
595
def test_ubuntu_lucid_distroseries_expansion(self):
596
self.assertURL('ubuntu/lucid/foo', 'ubuntu:lucid/foo', series='lucid')
598
def test_ubuntu_l_distroseries_expansion(self):
599
self.assertURL('ubuntu/lucid/foo', 'ubuntu:l/foo', series='lucid')
601
def test_ubuntu_karmic_distroseries_expansion(self):
602
self.assertURL('ubuntu/karmic/foo', 'ubuntu:karmic/foo',
605
def test_ubuntu_k_distroseries_expansion(self):
606
self.assertURL('ubuntu/karmic/foo', 'ubuntu:k/foo', series='karmic')
608
def test_ubuntu_jaunty_distroseries_expansion(self):
609
self.assertURL('ubuntu/jaunty/foo', 'ubuntu:jaunty/foo',
612
def test_ubuntu_j_distroseries_expansion(self):
613
self.assertURL('ubuntu/jaunty/foo', 'ubuntu:j/foo', series='jaunty')
615
def test_ubuntu_hardy_distroseries_expansion(self):
616
self.assertURL('ubuntu/hardy/foo', 'ubuntu:hardy/foo', series='hardy')
618
def test_ubuntu_h_distroseries_expansion(self):
619
self.assertURL('ubuntu/hardy/foo', 'ubuntu:h/foo', series='hardy')
621
def test_ubuntu_dapper_distroseries_expansion(self):
622
self.assertURL('ubuntu/dapper/foo', 'ubuntu:dapper/foo',
625
def test_ubuntu_d_distroseries_expansion(self):
626
self.assertURL('ubuntu/dapper/foo', 'ubuntu:d/foo', series='dapper')
628
# Debian default distro series.
630
def test_debian_default_distroseries_expansion(self):
631
self.assertURL('debian/foo', 'debianlp:foo', distro='debian')
633
def test_debian_squeeze_distroseries_expansion(self):
634
self.assertURL('debian/squeeze/foo', 'debianlp:squeeze/foo',
635
distro='debian', series='squeeze')
637
def test_debian_lenny_distroseries_expansion(self):
638
self.assertURL('debian/lenny/foo', 'debianlp:lenny/foo',
639
distro='debian', series='lenny')