1
# Copyright (C) 2006, 2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""Tests for the smart wire/domain protocol."""
19
from StringIO import StringIO
23
from bzrlib import bzrdir, errors, pack, smart, tests
24
from bzrlib.smart.request import SmartServerResponse
25
import bzrlib.smart.bzrdir
26
import bzrlib.smart.branch
27
import bzrlib.smart.repository
28
from bzrlib.util import bencode
31
class TestCaseWithSmartMedium(tests.TestCaseWithTransport):
34
super(TestCaseWithSmartMedium, self).setUp()
35
# We're allowed to set the transport class here, so that we don't use
36
# the default or a parameterized class, but rather use the
37
# TestCaseWithTransport infrastructure to set up a smart server and
39
self.transport_server = smart.server.SmartTCPServer_for_testing
41
def get_smart_medium(self):
42
"""Get a smart medium to use in tests."""
43
return self.get_transport().get_smart_medium()
46
class TestSmartServerResponse(tests.TestCase):
49
self.assertEqual(SmartServerResponse(('ok', )),
50
SmartServerResponse(('ok', )))
51
self.assertEqual(SmartServerResponse(('ok', ), 'body'),
52
SmartServerResponse(('ok', ), 'body'))
53
self.assertNotEqual(SmartServerResponse(('ok', )),
54
SmartServerResponse(('notok', )))
55
self.assertNotEqual(SmartServerResponse(('ok', ), 'body'),
56
SmartServerResponse(('ok', )))
57
self.assertNotEqual(None,
58
SmartServerResponse(('ok', )))
61
class TestSmartServerRequestFindRepository(tests.TestCaseWithTransport):
62
"""Tests for BzrDir.find_repository."""
64
def test_no_repository(self):
65
"""When there is no repository to be found, ('norepository', ) is returned."""
66
backing = self.get_transport()
67
request = smart.bzrdir.SmartServerRequestFindRepository(backing)
69
self.assertEqual(SmartServerResponse(('norepository', )),
70
request.execute(backing.local_abspath('')))
72
def test_nonshared_repository(self):
73
# nonshared repositorys only allow 'find' to return a handle when the
74
# path the repository is being searched on is the same as that that
75
# the repository is at.
76
backing = self.get_transport()
77
request = smart.bzrdir.SmartServerRequestFindRepository(backing)
78
result = self._make_repository_and_result()
79
self.assertEqual(result, request.execute(backing.local_abspath('')))
80
self.make_bzrdir('subdir')
81
self.assertEqual(SmartServerResponse(('norepository', )),
82
request.execute(backing.local_abspath('subdir')))
84
def _make_repository_and_result(self, shared=False, format=None):
85
"""Convenience function to setup a repository.
87
:result: The SmartServerResponse to expect when opening it.
89
repo = self.make_repository('.', shared=shared, format=format)
90
if repo.supports_rich_root():
94
if repo._format.supports_tree_reference:
98
return SmartServerResponse(('ok', '', rich_root, subtrees))
100
def test_shared_repository(self):
101
"""When there is a shared repository, we get 'ok', 'relpath-to-repo'."""
102
backing = self.get_transport()
103
request = smart.bzrdir.SmartServerRequestFindRepository(backing)
104
result = self._make_repository_and_result(shared=True)
105
self.assertEqual(result, request.execute(backing.local_abspath('')))
106
self.make_bzrdir('subdir')
107
result2 = SmartServerResponse(result.args[0:1] + ('..', ) + result.args[2:])
108
self.assertEqual(result2,
109
request.execute(backing.local_abspath('subdir')))
110
self.make_bzrdir('subdir/deeper')
111
result3 = SmartServerResponse(result.args[0:1] + ('../..', ) + result.args[2:])
112
self.assertEqual(result3,
113
request.execute(backing.local_abspath('subdir/deeper')))
115
def test_rich_root_and_subtree_encoding(self):
116
"""Test for the format attributes for rich root and subtree support."""
117
backing = self.get_transport()
118
request = smart.bzrdir.SmartServerRequestFindRepository(backing)
119
result = self._make_repository_and_result(format='dirstate-with-subtree')
120
# check the test will be valid
121
self.assertEqual('yes', result.args[2])
122
self.assertEqual('yes', result.args[3])
123
self.assertEqual(result, request.execute(backing.local_abspath('')))
126
class TestSmartServerRequestInitializeBzrDir(tests.TestCaseWithTransport):
128
def test_empty_dir(self):
129
"""Initializing an empty dir should succeed and do it."""
130
backing = self.get_transport()
131
request = smart.bzrdir.SmartServerRequestInitializeBzrDir(backing)
132
self.assertEqual(SmartServerResponse(('ok', )),
133
request.execute(backing.local_abspath('.')))
134
made_dir = bzrdir.BzrDir.open_from_transport(backing)
135
# no branch, tree or repository is expected with the current
137
self.assertRaises(errors.NoWorkingTree, made_dir.open_workingtree)
138
self.assertRaises(errors.NotBranchError, made_dir.open_branch)
139
self.assertRaises(errors.NoRepositoryPresent, made_dir.open_repository)
141
def test_missing_dir(self):
142
"""Initializing a missing directory should fail like the bzrdir api."""
143
backing = self.get_transport()
144
request = smart.bzrdir.SmartServerRequestInitializeBzrDir(backing)
145
self.assertRaises(errors.NoSuchFile,
146
request.execute, backing.local_abspath('subdir'))
148
def test_initialized_dir(self):
149
"""Initializing an extant bzrdir should fail like the bzrdir api."""
150
backing = self.get_transport()
151
request = smart.bzrdir.SmartServerRequestInitializeBzrDir(backing)
152
self.make_bzrdir('subdir')
153
self.assertRaises(errors.FileExists,
154
request.execute, backing.local_abspath('subdir'))
157
class TestSmartServerRequestOpenBranch(tests.TestCaseWithTransport):
159
def test_no_branch(self):
160
"""When there is no branch, ('nobranch', ) is returned."""
161
backing = self.get_transport()
162
request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
163
self.make_bzrdir('.')
164
self.assertEqual(SmartServerResponse(('nobranch', )),
165
request.execute(backing.local_abspath('')))
167
def test_branch(self):
168
"""When there is a branch, 'ok' is returned."""
169
backing = self.get_transport()
170
request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
171
self.make_branch('.')
172
self.assertEqual(SmartServerResponse(('ok', '')),
173
request.execute(backing.local_abspath('')))
175
def test_branch_reference(self):
176
"""When there is a branch reference, the reference URL is returned."""
177
backing = self.get_transport()
178
request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
179
branch = self.make_branch('branch')
180
checkout = branch.create_checkout('reference',lightweight=True)
181
# TODO: once we have an API to probe for references of any sort, we
183
reference_url = backing.abspath('branch') + '/'
184
self.assertFileEqual(reference_url, 'reference/.bzr/branch/location')
185
self.assertEqual(SmartServerResponse(('ok', reference_url)),
186
request.execute(backing.local_abspath('reference')))
189
class TestSmartServerRequestRevisionHistory(tests.TestCaseWithTransport):
191
def test_empty(self):
192
"""For an empty branch, the body is empty."""
193
backing = self.get_transport()
194
request = smart.branch.SmartServerRequestRevisionHistory(backing)
195
self.make_branch('.')
196
self.assertEqual(SmartServerResponse(('ok', ), ''),
197
request.execute(backing.local_abspath('')))
199
def test_not_empty(self):
200
"""For a non-empty branch, the body is empty."""
201
backing = self.get_transport()
202
request = smart.branch.SmartServerRequestRevisionHistory(backing)
203
tree = self.make_branch_and_memory_tree('.')
206
r1 = tree.commit('1st commit')
207
r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
210
SmartServerResponse(('ok', ), ('\x00'.join([r1, r2]))),
211
request.execute(backing.local_abspath('')))
214
class TestSmartServerBranchRequest(tests.TestCaseWithTransport):
216
def test_no_branch(self):
217
"""When there is a bzrdir and no branch, NotBranchError is raised."""
218
backing = self.get_transport()
219
request = smart.branch.SmartServerBranchRequest(backing)
220
self.make_bzrdir('.')
221
self.assertRaises(errors.NotBranchError,
222
request.execute, backing.local_abspath(''))
224
def test_branch_reference(self):
225
"""When there is a branch reference, NotBranchError is raised."""
226
backing = self.get_transport()
227
request = smart.branch.SmartServerBranchRequest(backing)
228
branch = self.make_branch('branch')
229
checkout = branch.create_checkout('reference',lightweight=True)
230
self.assertRaises(errors.NotBranchError,
231
request.execute, backing.local_abspath('checkout'))
234
class TestSmartServerBranchRequestLastRevisionInfo(tests.TestCaseWithTransport):
236
def test_empty(self):
237
"""For an empty branch, the result is ('ok', '0', 'null:')."""
238
backing = self.get_transport()
239
request = smart.branch.SmartServerBranchRequestLastRevisionInfo(backing)
240
self.make_branch('.')
241
self.assertEqual(SmartServerResponse(('ok', '0', 'null:')),
242
request.execute(backing.local_abspath('')))
244
def test_not_empty(self):
245
"""For a non-empty branch, the result is ('ok', 'revno', 'revid')."""
246
backing = self.get_transport()
247
request = smart.branch.SmartServerBranchRequestLastRevisionInfo(backing)
248
tree = self.make_branch_and_memory_tree('.')
251
rev_id_utf8 = u'\xc8'.encode('utf-8')
252
r1 = tree.commit('1st commit')
253
r2 = tree.commit('2nd commit', rev_id=rev_id_utf8)
256
SmartServerResponse(('ok', '2', rev_id_utf8)),
257
request.execute(backing.local_abspath('')))
260
class TestSmartServerBranchRequestGetConfigFile(tests.TestCaseWithTransport):
262
def test_default(self):
263
"""With no file, we get empty content."""
264
backing = self.get_transport()
265
request = smart.branch.SmartServerBranchGetConfigFile(backing)
266
branch = self.make_branch('.')
267
# there should be no file by default
269
self.assertEqual(SmartServerResponse(('ok', ), content),
270
request.execute(backing.local_abspath('')))
272
def test_with_content(self):
273
# SmartServerBranchGetConfigFile should return the content from
274
# branch.control_files.get('branch.conf') for now - in the future it may
275
# perform more complex processing.
276
backing = self.get_transport()
277
request = smart.branch.SmartServerBranchGetConfigFile(backing)
278
branch = self.make_branch('.')
279
branch.control_files.put_utf8('branch.conf', 'foo bar baz')
280
self.assertEqual(SmartServerResponse(('ok', ), 'foo bar baz'),
281
request.execute(backing.local_abspath('')))
284
class TestSmartServerBranchRequestSetLastRevision(tests.TestCaseWithTransport):
286
def test_empty(self):
287
backing = self.get_transport()
288
request = smart.branch.SmartServerBranchRequestSetLastRevision(backing)
289
b = self.make_branch('.')
290
branch_token = b.lock_write()
291
repo_token = b.repository.lock_write()
292
b.repository.unlock()
294
self.assertEqual(SmartServerResponse(('ok',)),
296
backing.local_abspath(''), branch_token, repo_token,
301
def test_not_present_revision_id(self):
302
backing = self.get_transport()
303
request = smart.branch.SmartServerBranchRequestSetLastRevision(backing)
304
b = self.make_branch('.')
305
branch_token = b.lock_write()
306
repo_token = b.repository.lock_write()
307
b.repository.unlock()
309
revision_id = 'non-existent revision'
311
SmartServerResponse(('NoSuchRevision', revision_id)),
313
backing.local_abspath(''), branch_token, repo_token,
318
def test_revision_id_present(self):
319
backing = self.get_transport()
320
request = smart.branch.SmartServerBranchRequestSetLastRevision(backing)
321
tree = self.make_branch_and_memory_tree('.')
324
rev_id_utf8 = u'\xc8'.encode('utf-8')
325
r1 = tree.commit('1st commit', rev_id=rev_id_utf8)
326
r2 = tree.commit('2nd commit')
328
branch_token = tree.branch.lock_write()
329
repo_token = tree.branch.repository.lock_write()
330
tree.branch.repository.unlock()
333
SmartServerResponse(('ok',)),
335
backing.local_abspath(''), branch_token, repo_token,
337
self.assertEqual([rev_id_utf8], tree.branch.revision_history())
341
def test_revision_id_present2(self):
342
backing = self.get_transport()
343
request = smart.branch.SmartServerBranchRequestSetLastRevision(backing)
344
tree = self.make_branch_and_memory_tree('.')
347
rev_id_utf8 = u'\xc8'.encode('utf-8')
348
r1 = tree.commit('1st commit', rev_id=rev_id_utf8)
349
r2 = tree.commit('2nd commit')
351
tree.branch.set_revision_history([])
352
branch_token = tree.branch.lock_write()
353
repo_token = tree.branch.repository.lock_write()
354
tree.branch.repository.unlock()
357
SmartServerResponse(('ok',)),
359
backing.local_abspath(''), branch_token, repo_token,
361
self.assertEqual([rev_id_utf8], tree.branch.revision_history())
366
class TestSmartServerBranchRequestLockWrite(tests.TestCaseWithTransport):
369
tests.TestCaseWithTransport.setUp(self)
370
self.reduceLockdirTimeout()
372
def test_lock_write_on_unlocked_branch(self):
373
backing = self.get_transport()
374
request = smart.branch.SmartServerBranchRequestLockWrite(backing)
375
branch = self.make_branch('.')
376
repository = branch.repository
377
response = request.execute(backing.local_abspath(''))
378
branch_nonce = branch.control_files._lock.peek().get('nonce')
379
repository_nonce = repository.control_files._lock.peek().get('nonce')
381
SmartServerResponse(('ok', branch_nonce, repository_nonce)),
383
# The branch (and associated repository) is now locked. Verify that
384
# with a new branch object.
385
new_branch = repository.bzrdir.open_branch()
386
self.assertRaises(errors.LockContention, new_branch.lock_write)
388
def test_lock_write_on_locked_branch(self):
389
backing = self.get_transport()
390
request = smart.branch.SmartServerBranchRequestLockWrite(backing)
391
branch = self.make_branch('.')
393
branch.leave_lock_in_place()
395
response = request.execute(backing.local_abspath(''))
397
SmartServerResponse(('LockContention',)), response)
399
def test_lock_write_with_tokens_on_locked_branch(self):
400
backing = self.get_transport()
401
request = smart.branch.SmartServerBranchRequestLockWrite(backing)
402
branch = self.make_branch('.')
403
branch_token = branch.lock_write()
404
repo_token = branch.repository.lock_write()
405
branch.repository.unlock()
406
branch.leave_lock_in_place()
407
branch.repository.leave_lock_in_place()
409
response = request.execute(backing.local_abspath(''),
410
branch_token, repo_token)
412
SmartServerResponse(('ok', branch_token, repo_token)), response)
414
def test_lock_write_with_mismatched_tokens_on_locked_branch(self):
415
backing = self.get_transport()
416
request = smart.branch.SmartServerBranchRequestLockWrite(backing)
417
branch = self.make_branch('.')
418
branch_token = branch.lock_write()
419
repo_token = branch.repository.lock_write()
420
branch.repository.unlock()
421
branch.leave_lock_in_place()
422
branch.repository.leave_lock_in_place()
424
response = request.execute(backing.local_abspath(''),
425
branch_token+'xxx', repo_token)
427
SmartServerResponse(('TokenMismatch',)), response)
429
def test_lock_write_on_locked_repo(self):
430
backing = self.get_transport()
431
request = smart.branch.SmartServerBranchRequestLockWrite(backing)
432
branch = self.make_branch('.')
433
branch.repository.lock_write()
434
branch.repository.leave_lock_in_place()
435
branch.repository.unlock()
436
response = request.execute(backing.local_abspath(''))
438
SmartServerResponse(('LockContention',)), response)
440
def test_lock_write_on_readonly_transport(self):
441
backing = self.get_readonly_transport()
442
request = smart.branch.SmartServerBranchRequestLockWrite(backing)
443
branch = self.make_branch('.')
444
response = request.execute('')
445
error_name, lock_str, why_str = response.args
446
self.assertFalse(response.is_successful())
447
self.assertEqual('LockFailed', error_name)
450
class TestSmartServerBranchRequestUnlock(tests.TestCaseWithTransport):
453
tests.TestCaseWithTransport.setUp(self)
454
self.reduceLockdirTimeout()
456
def test_unlock_on_locked_branch_and_repo(self):
457
backing = self.get_transport()
458
request = smart.branch.SmartServerBranchRequestUnlock(backing)
459
branch = self.make_branch('.')
461
branch_token = branch.lock_write()
462
repo_token = branch.repository.lock_write()
463
branch.repository.unlock()
464
# Unlock the branch (and repo) object, leaving the physical locks
466
branch.leave_lock_in_place()
467
branch.repository.leave_lock_in_place()
469
response = request.execute(backing.local_abspath(''),
470
branch_token, repo_token)
472
SmartServerResponse(('ok',)), response)
473
# The branch is now unlocked. Verify that with a new branch
475
new_branch = branch.bzrdir.open_branch()
476
new_branch.lock_write()
479
def test_unlock_on_unlocked_branch_unlocked_repo(self):
480
backing = self.get_transport()
481
request = smart.branch.SmartServerBranchRequestUnlock(backing)
482
branch = self.make_branch('.')
483
response = request.execute(
484
backing.local_abspath(''), 'branch token', 'repo token')
486
SmartServerResponse(('TokenMismatch',)), response)
488
def test_unlock_on_unlocked_branch_locked_repo(self):
489
backing = self.get_transport()
490
request = smart.branch.SmartServerBranchRequestUnlock(backing)
491
branch = self.make_branch('.')
492
# Lock the repository.
493
repo_token = branch.repository.lock_write()
494
branch.repository.leave_lock_in_place()
495
branch.repository.unlock()
496
# Issue branch lock_write request on the unlocked branch (with locked
498
response = request.execute(
499
backing.local_abspath(''), 'branch token', repo_token)
501
SmartServerResponse(('TokenMismatch',)), response)
504
class TestSmartServerRepositoryRequest(tests.TestCaseWithTransport):
506
def test_no_repository(self):
507
"""Raise NoRepositoryPresent when there is a bzrdir and no repo."""
508
# we test this using a shared repository above the named path,
509
# thus checking the right search logic is used - that is, that
510
# its the exact path being looked at and the server is not
512
backing = self.get_transport()
513
request = smart.repository.SmartServerRepositoryRequest(backing)
514
self.make_repository('.', shared=True)
515
self.make_bzrdir('subdir')
516
self.assertRaises(errors.NoRepositoryPresent,
517
request.execute, backing.local_abspath('subdir'))
520
class TestSmartServerRepositoryGetRevisionGraph(tests.TestCaseWithTransport):
522
def test_none_argument(self):
523
backing = self.get_transport()
524
request = smart.repository.SmartServerRepositoryGetRevisionGraph(backing)
525
tree = self.make_branch_and_memory_tree('.')
528
r1 = tree.commit('1st commit')
529
r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
532
# the lines of revision_id->revision_parent_list has no guaranteed
533
# order coming out of a dict, so sort both our test and response
534
lines = sorted([' '.join([r2, r1]), r1])
535
response = request.execute(backing.local_abspath(''), '')
536
response.body = '\n'.join(sorted(response.body.split('\n')))
539
SmartServerResponse(('ok', ), '\n'.join(lines)), response)
541
def test_specific_revision_argument(self):
542
backing = self.get_transport()
543
request = smart.repository.SmartServerRepositoryGetRevisionGraph(backing)
544
tree = self.make_branch_and_memory_tree('.')
547
rev_id_utf8 = u'\xc9'.encode('utf-8')
548
r1 = tree.commit('1st commit', rev_id=rev_id_utf8)
549
r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
552
self.assertEqual(SmartServerResponse(('ok', ), rev_id_utf8),
553
request.execute(backing.local_abspath(''), rev_id_utf8))
555
def test_no_such_revision(self):
556
backing = self.get_transport()
557
request = smart.repository.SmartServerRepositoryGetRevisionGraph(backing)
558
tree = self.make_branch_and_memory_tree('.')
561
r1 = tree.commit('1st commit')
564
# Note that it still returns body (of zero bytes).
566
SmartServerResponse(('nosuchrevision', 'missingrevision', ), ''),
567
request.execute(backing.local_abspath(''), 'missingrevision'))
570
class TestSmartServerRequestHasRevision(tests.TestCaseWithTransport):
572
def test_missing_revision(self):
573
"""For a missing revision, ('no', ) is returned."""
574
backing = self.get_transport()
575
request = smart.repository.SmartServerRequestHasRevision(backing)
576
self.make_repository('.')
577
self.assertEqual(SmartServerResponse(('no', )),
578
request.execute(backing.local_abspath(''), 'revid'))
580
def test_present_revision(self):
581
"""For a present revision, ('yes', ) is returned."""
582
backing = self.get_transport()
583
request = smart.repository.SmartServerRequestHasRevision(backing)
584
tree = self.make_branch_and_memory_tree('.')
587
rev_id_utf8 = u'\xc8abc'.encode('utf-8')
588
r1 = tree.commit('a commit', rev_id=rev_id_utf8)
590
self.assertTrue(tree.branch.repository.has_revision(rev_id_utf8))
591
self.assertEqual(SmartServerResponse(('yes', )),
592
request.execute(backing.local_abspath(''), rev_id_utf8))
595
class TestSmartServerRepositoryGatherStats(tests.TestCaseWithTransport):
597
def test_empty_revid(self):
598
"""With an empty revid, we get only size an number and revisions"""
599
backing = self.get_transport()
600
request = smart.repository.SmartServerRepositoryGatherStats(backing)
601
repository = self.make_repository('.')
602
stats = repository.gather_stats()
604
expected_body = 'revisions: 0\nsize: %d\n' % size
605
self.assertEqual(SmartServerResponse(('ok', ), expected_body),
606
request.execute(backing.local_abspath(''), '', 'no'))
608
def test_revid_with_committers(self):
609
"""For a revid we get more infos."""
610
backing = self.get_transport()
611
rev_id_utf8 = u'\xc8abc'.encode('utf-8')
612
request = smart.repository.SmartServerRepositoryGatherStats(backing)
613
tree = self.make_branch_and_memory_tree('.')
616
# Let's build a predictable result
617
tree.commit('a commit', timestamp=123456.2, timezone=3600)
618
tree.commit('a commit', timestamp=654321.4, timezone=0,
622
stats = tree.branch.repository.gather_stats()
624
expected_body = ('firstrev: 123456.200 3600\n'
625
'latestrev: 654321.400 0\n'
628
self.assertEqual(SmartServerResponse(('ok', ), expected_body),
629
request.execute(backing.local_abspath(''),
632
def test_not_empty_repository_with_committers(self):
633
"""For a revid and requesting committers we get the whole thing."""
634
backing = self.get_transport()
635
rev_id_utf8 = u'\xc8abc'.encode('utf-8')
636
request = smart.repository.SmartServerRepositoryGatherStats(backing)
637
tree = self.make_branch_and_memory_tree('.')
640
# Let's build a predictable result
641
tree.commit('a commit', timestamp=123456.2, timezone=3600,
643
tree.commit('a commit', timestamp=654321.4, timezone=0,
644
committer='bar', rev_id=rev_id_utf8)
646
stats = tree.branch.repository.gather_stats()
649
expected_body = ('committers: 2\n'
650
'firstrev: 123456.200 3600\n'
651
'latestrev: 654321.400 0\n'
654
self.assertEqual(SmartServerResponse(('ok', ), expected_body),
655
request.execute(backing.local_abspath(''),
659
class TestSmartServerRepositoryIsShared(tests.TestCaseWithTransport):
661
def test_is_shared(self):
662
"""For a shared repository, ('yes', ) is returned."""
663
backing = self.get_transport()
664
request = smart.repository.SmartServerRepositoryIsShared(backing)
665
self.make_repository('.', shared=True)
666
self.assertEqual(SmartServerResponse(('yes', )),
667
request.execute(backing.local_abspath(''), ))
669
def test_is_not_shared(self):
670
"""For a shared repository, ('no', ) is returned."""
671
backing = self.get_transport()
672
request = smart.repository.SmartServerRepositoryIsShared(backing)
673
self.make_repository('.', shared=False)
674
self.assertEqual(SmartServerResponse(('no', )),
675
request.execute(backing.local_abspath(''), ))
678
class TestSmartServerRepositoryLockWrite(tests.TestCaseWithTransport):
681
tests.TestCaseWithTransport.setUp(self)
682
self.reduceLockdirTimeout()
684
def test_lock_write_on_unlocked_repo(self):
685
backing = self.get_transport()
686
request = smart.repository.SmartServerRepositoryLockWrite(backing)
687
repository = self.make_repository('.')
688
response = request.execute(backing.local_abspath(''))
689
nonce = repository.control_files._lock.peek().get('nonce')
690
self.assertEqual(SmartServerResponse(('ok', nonce)), response)
691
# The repository is now locked. Verify that with a new repository
693
new_repo = repository.bzrdir.open_repository()
694
self.assertRaises(errors.LockContention, new_repo.lock_write)
696
def test_lock_write_on_locked_repo(self):
697
backing = self.get_transport()
698
request = smart.repository.SmartServerRepositoryLockWrite(backing)
699
repository = self.make_repository('.')
700
repository.lock_write()
701
repository.leave_lock_in_place()
703
response = request.execute(backing.local_abspath(''))
705
SmartServerResponse(('LockContention',)), response)
707
def test_lock_write_on_readonly_transport(self):
708
backing = self.get_readonly_transport()
709
request = smart.repository.SmartServerRepositoryLockWrite(backing)
710
repository = self.make_repository('.')
711
response = request.execute('')
712
self.assertFalse(response.is_successful())
713
self.assertEqual('LockFailed', response.args[0])
716
class TestSmartServerRepositoryUnlock(tests.TestCaseWithTransport):
719
tests.TestCaseWithTransport.setUp(self)
720
self.reduceLockdirTimeout()
722
def test_unlock_on_locked_repo(self):
723
backing = self.get_transport()
724
request = smart.repository.SmartServerRepositoryUnlock(backing)
725
repository = self.make_repository('.')
726
token = repository.lock_write()
727
repository.leave_lock_in_place()
729
response = request.execute(backing.local_abspath(''), token)
731
SmartServerResponse(('ok',)), response)
732
# The repository is now unlocked. Verify that with a new repository
734
new_repo = repository.bzrdir.open_repository()
735
new_repo.lock_write()
738
def test_unlock_on_unlocked_repo(self):
739
backing = self.get_transport()
740
request = smart.repository.SmartServerRepositoryUnlock(backing)
741
repository = self.make_repository('.')
742
response = request.execute(backing.local_abspath(''), 'some token')
744
SmartServerResponse(('TokenMismatch',)), response)
747
class TestSmartServerRepositoryTarball(tests.TestCaseWithTransport):
749
def test_repository_tarball(self):
750
backing = self.get_transport()
751
request = smart.repository.SmartServerRepositoryTarball(backing)
752
repository = self.make_repository('.')
753
# make some extraneous junk in the repository directory which should
755
self.build_tree(['.bzr/repository/extra-junk'])
756
response = request.execute(backing.local_abspath(''), 'bz2')
757
self.assertEqual(('ok',), response.args)
758
# body should be a tbz2
759
body_file = StringIO(response.body)
760
body_tar = tarfile.open('body_tar.tbz2', fileobj=body_file,
762
# let's make sure there are some key repository components inside it.
763
# the tarfile returns directories with trailing slashes...
764
names = set([n.rstrip('/') for n in body_tar.getnames()])
765
self.assertTrue('.bzr/repository/lock' in names)
766
self.assertTrue('.bzr/repository/format' in names)
767
self.assertTrue('.bzr/repository/extra-junk' not in names,
768
"extraneous file present in tar file")
771
class TestSmartServerRepositoryStreamKnitData(tests.TestCaseWithTransport):
773
def test_fetch_revisions(self):
774
backing = self.get_transport()
775
request = smart.repository.SmartServerRepositoryStreamKnitDataForRevisions(backing)
776
tree = self.make_branch_and_memory_tree('.')
779
rev_id1_utf8 = u'\xc8'.encode('utf-8')
780
rev_id2_utf8 = u'\xc9'.encode('utf-8')
781
r1 = tree.commit('1st commit', rev_id=rev_id1_utf8)
782
r1 = tree.commit('2nd commit', rev_id=rev_id2_utf8)
785
response = request.execute(backing.local_abspath(''), rev_id2_utf8)
786
self.assertEqual(('ok',), response.args)
787
from cStringIO import StringIO
788
unpacker = pack.ContainerReader(StringIO(response.body))
790
for [name], read_bytes in unpacker.iter_records():
792
bytes = read_bytes(None)
793
# The bytes should be a valid bencoded string.
794
bencode.bdecode(bytes)
795
# XXX: assert that the bencoded knit records have the right
798
def test_no_such_revision_error(self):
799
backing = self.get_transport()
800
request = smart.repository.SmartServerRepositoryStreamKnitDataForRevisions(backing)
801
repo = self.make_repository('.')
802
rev_id1_utf8 = u'\xc8'.encode('utf-8')
803
response = request.execute(backing.local_abspath(''), rev_id1_utf8)
805
SmartServerResponse(('NoSuchRevision', rev_id1_utf8)),
809
class TestSmartServerIsReadonly(tests.TestCaseWithTransport):
811
def test_is_readonly_no(self):
812
backing = self.get_transport()
813
request = smart.request.SmartServerIsReadonly(backing)
814
response = request.execute()
816
SmartServerResponse(('no',)), response)
818
def test_is_readonly_yes(self):
819
backing = self.get_readonly_transport()
820
request = smart.request.SmartServerIsReadonly(backing)
821
response = request.execute()
823
SmartServerResponse(('yes',)), response)
826
class TestHandlers(tests.TestCase):
827
"""Tests for the request.request_handlers object."""
829
def test_registered_methods(self):
830
"""Test that known methods are registered to the correct object."""
832
smart.request.request_handlers.get('Branch.get_config_file'),
833
smart.branch.SmartServerBranchGetConfigFile)
835
smart.request.request_handlers.get('Branch.lock_write'),
836
smart.branch.SmartServerBranchRequestLockWrite)
838
smart.request.request_handlers.get('Branch.last_revision_info'),
839
smart.branch.SmartServerBranchRequestLastRevisionInfo)
841
smart.request.request_handlers.get('Branch.revision_history'),
842
smart.branch.SmartServerRequestRevisionHistory)
844
smart.request.request_handlers.get('Branch.set_last_revision'),
845
smart.branch.SmartServerBranchRequestSetLastRevision)
847
smart.request.request_handlers.get('Branch.unlock'),
848
smart.branch.SmartServerBranchRequestUnlock)
850
smart.request.request_handlers.get('BzrDir.find_repository'),
851
smart.bzrdir.SmartServerRequestFindRepository)
853
smart.request.request_handlers.get('BzrDirFormat.initialize'),
854
smart.bzrdir.SmartServerRequestInitializeBzrDir)
856
smart.request.request_handlers.get('BzrDir.open_branch'),
857
smart.bzrdir.SmartServerRequestOpenBranch)
859
smart.request.request_handlers.get('Repository.gather_stats'),
860
smart.repository.SmartServerRepositoryGatherStats)
862
smart.request.request_handlers.get(
863
'Repository.get_revision_graph'),
864
smart.repository.SmartServerRepositoryGetRevisionGraph)
866
smart.request.request_handlers.get('Repository.has_revision'),
867
smart.repository.SmartServerRequestHasRevision)
869
smart.request.request_handlers.get('Repository.is_shared'),
870
smart.repository.SmartServerRepositoryIsShared)
872
smart.request.request_handlers.get('Repository.lock_write'),
873
smart.repository.SmartServerRepositoryLockWrite)
875
smart.request.request_handlers.get(
876
'Repository.stream_knit_data_for_revisions'),
877
smart.repository.SmartServerRepositoryStreamKnitDataForRevisions)
879
smart.request.request_handlers.get('Repository.tarball'),
880
smart.repository.SmartServerRepositoryTarball)
882
smart.request.request_handlers.get('Repository.unlock'),
883
smart.repository.SmartServerRepositoryUnlock)
885
smart.request.request_handlers.get('Transport.is_readonly'),
886
smart.request.SmartServerIsReadonly)