~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_smart.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-11-03 01:53:30 UTC
  • mfrom: (2955.1.1 trunk)
  • Revision ID: pqm@pqm.ubuntu.com-20071103015330-pt1tec7wyxwwcey8
Fix #158972 don't use timeout for HttpServer

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
 
"""Tests for the smart wire/domain protocol.
18
 
 
19
 
This module contains tests for the domain-level smart requests and responses,
20
 
such as the 'Branch.lock_write' request. Many of these use specific disk
21
 
formats to exercise calls that only make sense for formats with specific
22
 
properties.
23
 
 
24
 
Tests for low-level protocol encoding are found in test_smart_transport.
25
 
"""
26
 
 
27
 
import bz2
28
 
from cStringIO import StringIO
 
17
"""Tests for the smart wire/domain protocol."""
 
18
 
 
19
from StringIO import StringIO
 
20
import tempfile
29
21
import tarfile
30
22
 
31
 
from bzrlib import (
32
 
    bzrdir,
33
 
    errors,
34
 
    pack,
35
 
    smart,
36
 
    tests,
37
 
    urlutils,
38
 
    )
39
 
from bzrlib.branch import Branch, BranchReferenceFormat
 
23
from bzrlib import bzrdir, errors, pack, smart, tests
 
24
from bzrlib.smart.request import SmartServerResponse
 
25
import bzrlib.smart.bzrdir
40
26
import bzrlib.smart.branch
41
 
import bzrlib.smart.bzrdir
42
27
import bzrlib.smart.repository
43
 
from bzrlib.smart.request import (
44
 
    FailedSmartServerResponse,
45
 
    SmartServerRequest,
46
 
    SmartServerResponse,
47
 
    SuccessfulSmartServerResponse,
48
 
    )
49
 
from bzrlib.tests import (
50
 
    iter_suite_tests,
51
 
    split_suite_by_re,
52
 
    TestScenarioApplier,
53
 
    )
54
 
from bzrlib.transport import chroot, get_transport
55
28
from bzrlib.util import bencode
56
29
 
57
30
 
58
 
def load_tests(standard_tests, module, loader):
59
 
    """Multiply tests version and protocol consistency."""
60
 
    # FindRepository tests.
61
 
    bzrdir_mod = bzrlib.smart.bzrdir
62
 
    applier = TestScenarioApplier()
63
 
    applier.scenarios = [
64
 
        ("find_repository", {
65
 
            "_request_class":bzrdir_mod.SmartServerRequestFindRepositoryV1}),
66
 
        ("find_repositoryV2", {
67
 
            "_request_class":bzrdir_mod.SmartServerRequestFindRepositoryV2}),
68
 
        ]
69
 
    to_adapt, result = split_suite_by_re(standard_tests,
70
 
        "TestSmartServerRequestFindRepository")
71
 
    v2_only, v1_and_2 = split_suite_by_re(to_adapt,
72
 
        "_v2")
73
 
    for test in iter_suite_tests(v1_and_2):
74
 
        result.addTests(applier.adapt(test))
75
 
    del applier.scenarios[0]
76
 
    for test in iter_suite_tests(v2_only):
77
 
        result.addTests(applier.adapt(test))
78
 
    return result
79
 
 
80
 
 
81
 
class TestCaseWithChrootedTransport(tests.TestCaseWithTransport):
82
 
 
83
 
    def setUp(self):
84
 
        tests.TestCaseWithTransport.setUp(self)
85
 
        self._chroot_server = None
86
 
 
87
 
    def get_transport(self, relpath=None):
88
 
        if self._chroot_server is None:
89
 
            backing_transport = tests.TestCaseWithTransport.get_transport(self)
90
 
            self._chroot_server = chroot.ChrootServer(backing_transport)
91
 
            self._chroot_server.setUp()
92
 
            self.addCleanup(self._chroot_server.tearDown)
93
 
        t = get_transport(self._chroot_server.get_url())
94
 
        if relpath is not None:
95
 
            t = t.clone(relpath)
96
 
        return t
97
 
 
98
 
 
99
31
class TestCaseWithSmartMedium(tests.TestCaseWithTransport):
100
32
 
101
33
    def setUp(self):
104
36
        # the default or a parameterized class, but rather use the
105
37
        # TestCaseWithTransport infrastructure to set up a smart server and
106
38
        # transport.
107
 
        self.transport_server = self.make_transport_server
108
 
 
109
 
    def make_transport_server(self):
110
 
        return smart.server.SmartTCPServer_for_testing('-' + self.id())
 
39
        self.transport_server = smart.server.SmartTCPServer_for_testing
111
40
 
112
41
    def get_smart_medium(self):
113
42
        """Get a smart medium to use in tests."""
128
57
        self.assertNotEqual(None,
129
58
            SmartServerResponse(('ok', )))
130
59
 
131
 
    def test__str__(self):
132
 
        """SmartServerResponses can be stringified."""
133
 
        self.assertEqual(
134
 
            "<SmartServerResponse status=OK args=('args',) body='body'>",
135
 
            str(SuccessfulSmartServerResponse(('args',), 'body')))
136
 
        self.assertEqual(
137
 
            "<SmartServerResponse status=ERR args=('args',) body='body'>",
138
 
            str(FailedSmartServerResponse(('args',), 'body')))
139
 
 
140
 
 
141
 
class TestSmartServerRequest(tests.TestCaseWithMemoryTransport):
142
 
 
143
 
    def test_translate_client_path(self):
144
 
        transport = self.get_transport()
145
 
        request = SmartServerRequest(transport, 'foo/')
146
 
        self.assertEqual('./', request.translate_client_path('foo/'))
147
 
        self.assertRaises(
148
 
            errors.InvalidURLJoin, request.translate_client_path, 'foo/..')
149
 
        self.assertRaises(
150
 
            errors.PathNotChild, request.translate_client_path, '/')
151
 
        self.assertRaises(
152
 
            errors.PathNotChild, request.translate_client_path, 'bar/')
153
 
        self.assertEqual('./baz', request.translate_client_path('foo/baz'))
154
 
 
155
 
    def test_transport_from_client_path(self):
156
 
        transport = self.get_transport()
157
 
        request = SmartServerRequest(transport, 'foo/')
158
 
        self.assertEqual(
159
 
            transport.base,
160
 
            request.transport_from_client_path('foo/').base)
161
 
 
162
 
 
163
 
class TestSmartServerRequestFindRepository(tests.TestCaseWithMemoryTransport):
 
60
 
 
61
class TestSmartServerRequestFindRepository(tests.TestCaseWithTransport):
164
62
    """Tests for BzrDir.find_repository."""
165
63
 
166
64
    def test_no_repository(self):
167
65
        """When there is no repository to be found, ('norepository', ) is returned."""
168
66
        backing = self.get_transport()
169
 
        request = self._request_class(backing)
 
67
        request = smart.bzrdir.SmartServerRequestFindRepository(backing)
170
68
        self.make_bzrdir('.')
171
69
        self.assertEqual(SmartServerResponse(('norepository', )),
172
 
            request.execute(''))
 
70
            request.execute(backing.local_abspath('')))
173
71
 
174
72
    def test_nonshared_repository(self):
175
73
        # nonshared repositorys only allow 'find' to return a handle when the 
176
74
        # path the repository is being searched on is the same as that that 
177
75
        # the repository is at.
178
76
        backing = self.get_transport()
179
 
        request = self._request_class(backing)
 
77
        request = smart.bzrdir.SmartServerRequestFindRepository(backing)
180
78
        result = self._make_repository_and_result()
181
 
        self.assertEqual(result, request.execute(''))
 
79
        self.assertEqual(result, request.execute(backing.local_abspath('')))
182
80
        self.make_bzrdir('subdir')
183
81
        self.assertEqual(SmartServerResponse(('norepository', )),
184
 
            request.execute('subdir'))
 
82
            request.execute(backing.local_abspath('subdir')))
185
83
 
186
84
    def _make_repository_and_result(self, shared=False, format=None):
187
85
        """Convenience function to setup a repository.
197
95
            subtrees = 'yes'
198
96
        else:
199
97
            subtrees = 'no'
200
 
        if (smart.bzrdir.SmartServerRequestFindRepositoryV2 ==
201
 
            self._request_class):
202
 
            # All tests so far are on formats, and for non-external
203
 
            # repositories.
204
 
            return SuccessfulSmartServerResponse(
205
 
                ('ok', '', rich_root, subtrees, 'no'))
206
 
        else:
207
 
            return SuccessfulSmartServerResponse(('ok', '', rich_root, subtrees))
 
98
        return SmartServerResponse(('ok', '', rich_root, subtrees))
208
99
 
209
100
    def test_shared_repository(self):
210
101
        """When there is a shared repository, we get 'ok', 'relpath-to-repo'."""
211
102
        backing = self.get_transport()
212
 
        request = self._request_class(backing)
 
103
        request = smart.bzrdir.SmartServerRequestFindRepository(backing)
213
104
        result = self._make_repository_and_result(shared=True)
214
 
        self.assertEqual(result, request.execute(''))
 
105
        self.assertEqual(result, request.execute(backing.local_abspath('')))
215
106
        self.make_bzrdir('subdir')
216
107
        result2 = SmartServerResponse(result.args[0:1] + ('..', ) + result.args[2:])
217
108
        self.assertEqual(result2,
218
 
            request.execute('subdir'))
 
109
            request.execute(backing.local_abspath('subdir')))
219
110
        self.make_bzrdir('subdir/deeper')
220
111
        result3 = SmartServerResponse(result.args[0:1] + ('../..', ) + result.args[2:])
221
112
        self.assertEqual(result3,
222
 
            request.execute('subdir/deeper'))
 
113
            request.execute(backing.local_abspath('subdir/deeper')))
223
114
 
224
115
    def test_rich_root_and_subtree_encoding(self):
225
116
        """Test for the format attributes for rich root and subtree support."""
226
117
        backing = self.get_transport()
227
 
        request = self._request_class(backing)
 
118
        request = smart.bzrdir.SmartServerRequestFindRepository(backing)
228
119
        result = self._make_repository_and_result(format='dirstate-with-subtree')
229
120
        # check the test will be valid
230
121
        self.assertEqual('yes', result.args[2])
231
122
        self.assertEqual('yes', result.args[3])
232
 
        self.assertEqual(result, request.execute(''))
233
 
 
234
 
    def test_supports_external_lookups_no_v2(self):
235
 
        """Test for the supports_external_lookups attribute."""
236
 
        backing = self.get_transport()
237
 
        request = self._request_class(backing)
238
 
        result = self._make_repository_and_result(format='dirstate-with-subtree')
239
 
        # check the test will be valid
240
 
        self.assertEqual('no', result.args[4])
241
 
        self.assertEqual(result, request.execute(''))
242
 
 
243
 
 
244
 
class TestSmartServerRequestInitializeBzrDir(tests.TestCaseWithMemoryTransport):
 
123
        self.assertEqual(result, request.execute(backing.local_abspath('')))
 
124
 
 
125
 
 
126
class TestSmartServerRequestInitializeBzrDir(tests.TestCaseWithTransport):
245
127
 
246
128
    def test_empty_dir(self):
247
129
        """Initializing an empty dir should succeed and do it."""
248
130
        backing = self.get_transport()
249
131
        request = smart.bzrdir.SmartServerRequestInitializeBzrDir(backing)
250
132
        self.assertEqual(SmartServerResponse(('ok', )),
251
 
            request.execute(''))
 
133
            request.execute(backing.local_abspath('.')))
252
134
        made_dir = bzrdir.BzrDir.open_from_transport(backing)
253
135
        # no branch, tree or repository is expected with the current 
254
136
        # default formart.
261
143
        backing = self.get_transport()
262
144
        request = smart.bzrdir.SmartServerRequestInitializeBzrDir(backing)
263
145
        self.assertRaises(errors.NoSuchFile,
264
 
            request.execute, 'subdir')
 
146
            request.execute, backing.local_abspath('subdir'))
265
147
 
266
148
    def test_initialized_dir(self):
267
149
        """Initializing an extant bzrdir should fail like the bzrdir api."""
269
151
        request = smart.bzrdir.SmartServerRequestInitializeBzrDir(backing)
270
152
        self.make_bzrdir('subdir')
271
153
        self.assertRaises(errors.FileExists,
272
 
            request.execute, 'subdir')
273
 
 
274
 
 
275
 
class TestSmartServerRequestOpenBranch(TestCaseWithChrootedTransport):
 
154
            request.execute, backing.local_abspath('subdir'))
 
155
 
 
156
 
 
157
class TestSmartServerRequestOpenBranch(tests.TestCaseWithTransport):
276
158
 
277
159
    def test_no_branch(self):
278
160
        """When there is no branch, ('nobranch', ) is returned."""
280
162
        request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
281
163
        self.make_bzrdir('.')
282
164
        self.assertEqual(SmartServerResponse(('nobranch', )),
283
 
            request.execute(''))
 
165
            request.execute(backing.local_abspath('')))
284
166
 
285
167
    def test_branch(self):
286
168
        """When there is a branch, 'ok' is returned."""
288
170
        request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
289
171
        self.make_branch('.')
290
172
        self.assertEqual(SmartServerResponse(('ok', '')),
291
 
            request.execute(''))
 
173
            request.execute(backing.local_abspath('')))
292
174
 
293
175
    def test_branch_reference(self):
294
176
        """When there is a branch reference, the reference URL is returned."""
296
178
        request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
297
179
        branch = self.make_branch('branch')
298
180
        checkout = branch.create_checkout('reference',lightweight=True)
299
 
        reference_url = BranchReferenceFormat().get_reference(checkout.bzrdir)
 
181
        # TODO: once we have an API to probe for references of any sort, we
 
182
        # can use it here.
 
183
        reference_url = backing.abspath('branch') + '/'
300
184
        self.assertFileEqual(reference_url, 'reference/.bzr/branch/location')
301
185
        self.assertEqual(SmartServerResponse(('ok', reference_url)),
302
 
            request.execute('reference'))
303
 
 
304
 
 
305
 
class TestSmartServerRequestRevisionHistory(tests.TestCaseWithMemoryTransport):
 
186
            request.execute(backing.local_abspath('reference')))
 
187
 
 
188
 
 
189
class TestSmartServerRequestRevisionHistory(tests.TestCaseWithTransport):
306
190
 
307
191
    def test_empty(self):
308
192
        """For an empty branch, the body is empty."""
310
194
        request = smart.branch.SmartServerRequestRevisionHistory(backing)
311
195
        self.make_branch('.')
312
196
        self.assertEqual(SmartServerResponse(('ok', ), ''),
313
 
            request.execute(''))
 
197
            request.execute(backing.local_abspath('')))
314
198
 
315
199
    def test_not_empty(self):
316
200
        """For a non-empty branch, the body is empty."""
324
208
        tree.unlock()
325
209
        self.assertEqual(
326
210
            SmartServerResponse(('ok', ), ('\x00'.join([r1, r2]))),
327
 
            request.execute(''))
328
 
 
329
 
 
330
 
class TestSmartServerBranchRequest(tests.TestCaseWithMemoryTransport):
 
211
            request.execute(backing.local_abspath('')))
 
212
 
 
213
 
 
214
class TestSmartServerBranchRequest(tests.TestCaseWithTransport):
331
215
 
332
216
    def test_no_branch(self):
333
217
        """When there is a bzrdir and no branch, NotBranchError is raised."""
335
219
        request = smart.branch.SmartServerBranchRequest(backing)
336
220
        self.make_bzrdir('.')
337
221
        self.assertRaises(errors.NotBranchError,
338
 
            request.execute, '')
 
222
            request.execute, backing.local_abspath(''))
339
223
 
340
224
    def test_branch_reference(self):
341
225
        """When there is a branch reference, NotBranchError is raised."""
344
228
        branch = self.make_branch('branch')
345
229
        checkout = branch.create_checkout('reference',lightweight=True)
346
230
        self.assertRaises(errors.NotBranchError,
347
 
            request.execute, 'checkout')
348
 
 
349
 
 
350
 
class TestSmartServerBranchRequestLastRevisionInfo(tests.TestCaseWithMemoryTransport):
 
231
            request.execute, backing.local_abspath('checkout'))
 
232
 
 
233
 
 
234
class TestSmartServerBranchRequestLastRevisionInfo(tests.TestCaseWithTransport):
351
235
 
352
236
    def test_empty(self):
353
237
        """For an empty branch, the result is ('ok', '0', 'null:')."""
355
239
        request = smart.branch.SmartServerBranchRequestLastRevisionInfo(backing)
356
240
        self.make_branch('.')
357
241
        self.assertEqual(SmartServerResponse(('ok', '0', 'null:')),
358
 
            request.execute(''))
 
242
            request.execute(backing.local_abspath('')))
359
243
 
360
244
    def test_not_empty(self):
361
245
        """For a non-empty branch, the result is ('ok', 'revno', 'revid')."""
370
254
        tree.unlock()
371
255
        self.assertEqual(
372
256
            SmartServerResponse(('ok', '2', rev_id_utf8)),
373
 
            request.execute(''))
374
 
 
375
 
 
376
 
class TestSmartServerBranchRequestGetConfigFile(tests.TestCaseWithMemoryTransport):
 
257
            request.execute(backing.local_abspath('')))
 
258
 
 
259
 
 
260
class TestSmartServerBranchRequestGetConfigFile(tests.TestCaseWithTransport):
377
261
 
378
262
    def test_default(self):
379
263
        """With no file, we get empty content."""
383
267
        # there should be no file by default
384
268
        content = ''
385
269
        self.assertEqual(SmartServerResponse(('ok', ), content),
386
 
            request.execute(''))
 
270
            request.execute(backing.local_abspath('')))
387
271
 
388
272
    def test_with_content(self):
389
273
        # SmartServerBranchGetConfigFile should return the content from
392
276
        backing = self.get_transport()
393
277
        request = smart.branch.SmartServerBranchGetConfigFile(backing)
394
278
        branch = self.make_branch('.')
395
 
        branch._transport.put_bytes('branch.conf', 'foo bar baz')
 
279
        branch.control_files.put_utf8('branch.conf', 'foo bar baz')
396
280
        self.assertEqual(SmartServerResponse(('ok', ), 'foo bar baz'),
397
 
            request.execute(''))
398
 
 
399
 
 
400
 
class SetLastRevisionTestBase(tests.TestCaseWithMemoryTransport):
401
 
    """Base test case for verbs that implement set_last_revision."""
402
 
 
403
 
    def setUp(self):
404
 
        tests.TestCaseWithMemoryTransport.setUp(self)
405
 
        backing_transport = self.get_transport()
406
 
        self.request = self.request_class(backing_transport)
407
 
        self.tree = self.make_branch_and_memory_tree('.')
408
 
 
409
 
    def lock_branch(self):
410
 
        b = self.tree.branch
411
 
        branch_token = b.lock_write()
412
 
        repo_token = b.repository.lock_write()
413
 
        b.repository.unlock()
414
 
        return branch_token, repo_token
415
 
 
416
 
    def unlock_branch(self):
417
 
        self.tree.branch.unlock()
418
 
        
419
 
    def set_last_revision(self, revision_id, revno):
420
 
        branch_token, repo_token = self.lock_branch()
421
 
        response = self._set_last_revision(
422
 
            revision_id, revno, branch_token, repo_token)
423
 
        self.unlock_branch()
424
 
        return response
425
 
 
426
 
    def assertRequestSucceeds(self, revision_id, revno):
427
 
        response = self.set_last_revision(revision_id, revno)
428
 
        self.assertEqual(SuccessfulSmartServerResponse(('ok',)), response)
429
 
 
430
 
        
431
 
class TestSetLastRevisionVerbMixin(object):
432
 
    """Mixin test case for verbs that implement set_last_revision."""
433
 
 
434
 
    def test_set_null_to_null(self):
435
 
        """An empty branch can have its last revision set to 'null:'."""
436
 
        self.assertRequestSucceeds('null:', 0)
437
 
 
438
 
    def test_NoSuchRevision(self):
439
 
        """If the revision_id is not present, the verb returns NoSuchRevision.
440
 
        """
441
 
        revision_id = 'non-existent revision'
442
 
        self.assertEqual(
443
 
            FailedSmartServerResponse(('NoSuchRevision', revision_id)),
444
 
            self.set_last_revision(revision_id, 1))
445
 
 
446
 
    def make_tree_with_two_commits(self):
447
 
        self.tree.lock_write()
448
 
        self.tree.add('')
449
 
        rev_id_utf8 = u'\xc8'.encode('utf-8')
450
 
        r1 = self.tree.commit('1st commit', rev_id=rev_id_utf8)
451
 
        r2 = self.tree.commit('2nd commit', rev_id='rev-2')
452
 
        self.tree.unlock()
453
 
 
454
 
    def test_branch_last_revision_info_is_updated(self):
455
 
        """A branch's tip can be set to a revision that is present in its
456
 
        repository.
457
 
        """
458
 
        # Make a branch with an empty revision history, but two revisions in
459
 
        # its repository.
460
 
        self.make_tree_with_two_commits()
461
 
        rev_id_utf8 = u'\xc8'.encode('utf-8')
462
 
        self.tree.branch.set_revision_history([])
463
 
        self.assertEqual(
464
 
            (0, 'null:'), self.tree.branch.last_revision_info())
465
 
        # We can update the branch to a revision that is present in the
466
 
        # repository.
467
 
        self.assertRequestSucceeds(rev_id_utf8, 1)
468
 
        self.assertEqual(
469
 
            (1, rev_id_utf8), self.tree.branch.last_revision_info())
470
 
 
471
 
    def test_branch_last_revision_info_rewind(self):
472
 
        """A branch's tip can be set to a revision that is an ancestor of the
473
 
        current tip.
474
 
        """
475
 
        self.make_tree_with_two_commits()
476
 
        rev_id_utf8 = u'\xc8'.encode('utf-8')
477
 
        self.assertEqual(
478
 
            (2, 'rev-2'), self.tree.branch.last_revision_info())
479
 
        self.assertRequestSucceeds(rev_id_utf8, 1)
480
 
        self.assertEqual(
481
 
            (1, rev_id_utf8), self.tree.branch.last_revision_info())
482
 
 
483
 
    def test_TipChangeRejected(self):
484
 
        """If a pre_change_branch_tip hook raises TipChangeRejected, the verb
485
 
        returns TipChangeRejected.
486
 
        """
487
 
        rejection_message = u'rejection message\N{INTERROBANG}'
488
 
        def hook_that_rejects(params):
489
 
            raise errors.TipChangeRejected(rejection_message)
490
 
        Branch.hooks.install_named_hook(
491
 
            'pre_change_branch_tip', hook_that_rejects, None)
492
 
        self.assertEqual(
493
 
            FailedSmartServerResponse(
494
 
                ('TipChangeRejected', rejection_message.encode('utf-8'))),
495
 
            self.set_last_revision('null:', 0))
496
 
 
497
 
 
498
 
class TestSmartServerBranchRequestSetLastRevision(
499
 
        SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
500
 
    """Tests for Branch.set_last_revision verb."""
501
 
 
502
 
    request_class = smart.branch.SmartServerBranchRequestSetLastRevision
503
 
 
504
 
    def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
505
 
        return self.request.execute(
506
 
            '', branch_token, repo_token, revision_id)
507
 
 
508
 
 
509
 
class TestSmartServerBranchRequestSetLastRevisionInfo(
510
 
        SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
511
 
    """Tests for Branch.set_last_revision_info verb."""
512
 
 
513
 
    request_class = smart.branch.SmartServerBranchRequestSetLastRevisionInfo
514
 
 
515
 
    def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
516
 
        return self.request.execute(
517
 
            '', branch_token, repo_token, revno, revision_id)
518
 
 
519
 
    def test_NoSuchRevision(self):
520
 
        """Branch.set_last_revision_info does not have to return
521
 
        NoSuchRevision if the revision_id is absent.
522
 
        """
523
 
        raise tests.TestNotApplicable()
524
 
 
525
 
 
526
 
class TestSmartServerBranchRequestSetLastRevisionEx(
527
 
        SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
528
 
    """Tests for Branch.set_last_revision_ex verb."""
529
 
 
530
 
    request_class = smart.branch.SmartServerBranchRequestSetLastRevisionEx
531
 
 
532
 
    def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
533
 
        return self.request.execute(
534
 
            '', branch_token, repo_token, revision_id, 0, 0)
535
 
 
536
 
    def assertRequestSucceeds(self, revision_id, revno):
537
 
        response = self.set_last_revision(revision_id, revno)
538
 
        self.assertEqual(
539
 
            SuccessfulSmartServerResponse(('ok', revno, revision_id)),
540
 
            response)
541
 
        
542
 
    def test_branch_last_revision_info_rewind(self):
543
 
        """A branch's tip can be set to a revision that is an ancestor of the
544
 
        current tip, but only if allow_overwrite_descendant is passed.
545
 
        """
546
 
        self.make_tree_with_two_commits()
547
 
        rev_id_utf8 = u'\xc8'.encode('utf-8')
548
 
        self.assertEqual(
549
 
            (2, 'rev-2'), self.tree.branch.last_revision_info())
550
 
        # If allow_overwrite_descendant flag is 0, then trying to set the tip
551
 
        # to an older revision ID has no effect.
552
 
        branch_token, repo_token = self.lock_branch()
553
 
        response = self.request.execute(
554
 
            '', branch_token, repo_token, rev_id_utf8, 0, 0)
555
 
        self.assertEqual(
556
 
            SuccessfulSmartServerResponse(('ok', 2, 'rev-2')),
557
 
            response)
558
 
        self.assertEqual(
559
 
            (2, 'rev-2'), self.tree.branch.last_revision_info())
560
 
 
561
 
        # If allow_overwrite_descendant flag is 1, then setting the tip to an
562
 
        # ancestor works.
563
 
        response = self.request.execute(
564
 
            '', branch_token, repo_token, rev_id_utf8, 0, 1)
565
 
        self.assertEqual(
566
 
            SuccessfulSmartServerResponse(('ok', 1, rev_id_utf8)),
567
 
            response)
568
 
        self.unlock_branch()
569
 
        self.assertEqual(
570
 
            (1, rev_id_utf8), self.tree.branch.last_revision_info())
571
 
 
572
 
    def make_branch_with_divergent_history(self):
573
 
        """Make a branch with divergent history in its repo.
574
 
 
575
 
        The branch's tip will be 'child-2', and the repo will also contain
576
 
        'child-1', which diverges from a common base revision.
577
 
        """
578
 
        self.tree.lock_write()
579
 
        self.tree.add('')
580
 
        r1 = self.tree.commit('1st commit')
581
 
        revno_1, revid_1 = self.tree.branch.last_revision_info()
582
 
        r2 = self.tree.commit('2nd commit', rev_id='child-1')
583
 
        # Undo the second commit
584
 
        self.tree.branch.set_last_revision_info(revno_1, revid_1)
585
 
        self.tree.set_parent_ids([revid_1])
586
 
        # Make a new second commit, child-2.  child-2 has diverged from
587
 
        # child-1.
588
 
        new_r2 = self.tree.commit('2nd commit', rev_id='child-2')
589
 
        self.tree.unlock()
590
 
        
591
 
    def test_not_allow_diverged(self):
592
 
        """If allow_diverged is not passed, then setting a divergent history
593
 
        returns a Diverged error.
594
 
        """
595
 
        self.make_branch_with_divergent_history()
596
 
        self.assertEqual(
597
 
            FailedSmartServerResponse(('Diverged',)),
598
 
            self.set_last_revision('child-1', 2))
599
 
        # The branch tip was not changed.
600
 
        self.assertEqual('child-2', self.tree.branch.last_revision())
601
 
 
602
 
    def test_allow_diverged(self):
603
 
        """If allow_diverged is passed, then setting a divergent history
604
 
        succeeds.
605
 
        """
606
 
        self.make_branch_with_divergent_history()
607
 
        branch_token, repo_token = self.lock_branch()
608
 
        response = self.request.execute(
609
 
            '', branch_token, repo_token, 'child-1', 1, 0)
610
 
        self.assertEqual(
611
 
            SuccessfulSmartServerResponse(('ok', 2, 'child-1')),
612
 
            response)
613
 
        self.unlock_branch()
614
 
        # The branch tip was changed.
615
 
        self.assertEqual('child-1', self.tree.branch.last_revision())
616
 
 
617
 
 
618
 
class TestSmartServerBranchRequestLockWrite(tests.TestCaseWithMemoryTransport):
619
 
 
620
 
    def setUp(self):
621
 
        tests.TestCaseWithMemoryTransport.setUp(self)
 
281
            request.execute(backing.local_abspath('')))
 
282
 
 
283
 
 
284
class TestSmartServerBranchRequestSetLastRevision(tests.TestCaseWithTransport):
 
285
 
 
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()
 
293
        try:
 
294
            self.assertEqual(SmartServerResponse(('ok',)),
 
295
                request.execute(
 
296
                    backing.local_abspath(''), branch_token, repo_token,
 
297
                    'null:'))
 
298
        finally:
 
299
            b.unlock()
 
300
 
 
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()
 
308
        try:
 
309
            revision_id = 'non-existent revision'
 
310
            self.assertEqual(
 
311
                SmartServerResponse(('NoSuchRevision', revision_id)),
 
312
                request.execute(
 
313
                    backing.local_abspath(''), branch_token, repo_token,
 
314
                    revision_id))
 
315
        finally:
 
316
            b.unlock()
 
317
 
 
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('.')
 
322
        tree.lock_write()
 
323
        tree.add('')
 
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')
 
327
        tree.unlock()
 
328
        branch_token = tree.branch.lock_write()
 
329
        repo_token = tree.branch.repository.lock_write()
 
330
        tree.branch.repository.unlock()
 
331
        try:
 
332
            self.assertEqual(
 
333
                SmartServerResponse(('ok',)),
 
334
                request.execute(
 
335
                    backing.local_abspath(''), branch_token, repo_token,
 
336
                    rev_id_utf8))
 
337
            self.assertEqual([rev_id_utf8], tree.branch.revision_history())
 
338
        finally:
 
339
            tree.branch.unlock()
 
340
 
 
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('.')
 
345
        tree.lock_write()
 
346
        tree.add('')
 
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')
 
350
        tree.unlock()
 
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()
 
355
        try:
 
356
            self.assertEqual(
 
357
                SmartServerResponse(('ok',)),
 
358
                request.execute(
 
359
                    backing.local_abspath(''), branch_token, repo_token,
 
360
                    rev_id_utf8))
 
361
            self.assertEqual([rev_id_utf8], tree.branch.revision_history())
 
362
        finally:
 
363
            tree.branch.unlock()
 
364
 
 
365
 
 
366
class TestSmartServerBranchRequestLockWrite(tests.TestCaseWithTransport):
 
367
 
 
368
    def setUp(self):
 
369
        tests.TestCaseWithTransport.setUp(self)
 
370
        self.reduceLockdirTimeout()
622
371
 
623
372
    def test_lock_write_on_unlocked_branch(self):
624
373
        backing = self.get_transport()
625
374
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
626
 
        branch = self.make_branch('.', format='knit')
 
375
        branch = self.make_branch('.')
627
376
        repository = branch.repository
628
 
        response = request.execute('')
 
377
        response = request.execute(backing.local_abspath(''))
629
378
        branch_nonce = branch.control_files._lock.peek().get('nonce')
630
379
        repository_nonce = repository.control_files._lock.peek().get('nonce')
631
380
        self.assertEqual(
643
392
        branch.lock_write()
644
393
        branch.leave_lock_in_place()
645
394
        branch.unlock()
646
 
        response = request.execute('')
 
395
        response = request.execute(backing.local_abspath(''))
647
396
        self.assertEqual(
648
397
            SmartServerResponse(('LockContention',)), response)
649
398
 
650
399
    def test_lock_write_with_tokens_on_locked_branch(self):
651
400
        backing = self.get_transport()
652
401
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
653
 
        branch = self.make_branch('.', format='knit')
 
402
        branch = self.make_branch('.')
654
403
        branch_token = branch.lock_write()
655
404
        repo_token = branch.repository.lock_write()
656
405
        branch.repository.unlock()
657
406
        branch.leave_lock_in_place()
658
407
        branch.repository.leave_lock_in_place()
659
408
        branch.unlock()
660
 
        response = request.execute('',
 
409
        response = request.execute(backing.local_abspath(''),
661
410
                                   branch_token, repo_token)
662
411
        self.assertEqual(
663
412
            SmartServerResponse(('ok', branch_token, repo_token)), response)
665
414
    def test_lock_write_with_mismatched_tokens_on_locked_branch(self):
666
415
        backing = self.get_transport()
667
416
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
668
 
        branch = self.make_branch('.', format='knit')
 
417
        branch = self.make_branch('.')
669
418
        branch_token = branch.lock_write()
670
419
        repo_token = branch.repository.lock_write()
671
420
        branch.repository.unlock()
672
421
        branch.leave_lock_in_place()
673
422
        branch.repository.leave_lock_in_place()
674
423
        branch.unlock()
675
 
        response = request.execute('',
 
424
        response = request.execute(backing.local_abspath(''),
676
425
                                   branch_token+'xxx', repo_token)
677
426
        self.assertEqual(
678
427
            SmartServerResponse(('TokenMismatch',)), response)
680
429
    def test_lock_write_on_locked_repo(self):
681
430
        backing = self.get_transport()
682
431
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
683
 
        branch = self.make_branch('.', format='knit')
 
432
        branch = self.make_branch('.')
684
433
        branch.repository.lock_write()
685
434
        branch.repository.leave_lock_in_place()
686
435
        branch.repository.unlock()
687
 
        response = request.execute('')
 
436
        response = request.execute(backing.local_abspath(''))
688
437
        self.assertEqual(
689
438
            SmartServerResponse(('LockContention',)), response)
690
439
 
692
441
        backing = self.get_readonly_transport()
693
442
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
694
443
        branch = self.make_branch('.')
695
 
        root = self.get_transport().clone('/')
696
 
        path = urlutils.relative_url(root.base, self.get_transport().base)
697
 
        response = request.execute(path)
 
444
        response = request.execute('')
698
445
        error_name, lock_str, why_str = response.args
699
446
        self.assertFalse(response.is_successful())
700
447
        self.assertEqual('LockFailed', error_name)
701
448
 
702
449
 
703
 
class TestSmartServerBranchRequestUnlock(tests.TestCaseWithMemoryTransport):
 
450
class TestSmartServerBranchRequestUnlock(tests.TestCaseWithTransport):
704
451
 
705
452
    def setUp(self):
706
 
        tests.TestCaseWithMemoryTransport.setUp(self)
 
453
        tests.TestCaseWithTransport.setUp(self)
 
454
        self.reduceLockdirTimeout()
707
455
 
708
456
    def test_unlock_on_locked_branch_and_repo(self):
709
457
        backing = self.get_transport()
710
458
        request = smart.branch.SmartServerBranchRequestUnlock(backing)
711
 
        branch = self.make_branch('.', format='knit')
 
459
        branch = self.make_branch('.')
712
460
        # Lock the branch
713
461
        branch_token = branch.lock_write()
714
462
        repo_token = branch.repository.lock_write()
718
466
        branch.leave_lock_in_place()
719
467
        branch.repository.leave_lock_in_place()
720
468
        branch.unlock()
721
 
        response = request.execute('',
 
469
        response = request.execute(backing.local_abspath(''),
722
470
                                   branch_token, repo_token)
723
471
        self.assertEqual(
724
472
            SmartServerResponse(('ok',)), response)
731
479
    def test_unlock_on_unlocked_branch_unlocked_repo(self):
732
480
        backing = self.get_transport()
733
481
        request = smart.branch.SmartServerBranchRequestUnlock(backing)
734
 
        branch = self.make_branch('.', format='knit')
 
482
        branch = self.make_branch('.')
735
483
        response = request.execute(
736
 
            '', 'branch token', 'repo token')
 
484
            backing.local_abspath(''), 'branch token', 'repo token')
737
485
        self.assertEqual(
738
486
            SmartServerResponse(('TokenMismatch',)), response)
739
487
 
740
488
    def test_unlock_on_unlocked_branch_locked_repo(self):
741
489
        backing = self.get_transport()
742
490
        request = smart.branch.SmartServerBranchRequestUnlock(backing)
743
 
        branch = self.make_branch('.', format='knit')
 
491
        branch = self.make_branch('.')
744
492
        # Lock the repository.
745
493
        repo_token = branch.repository.lock_write()
746
494
        branch.repository.leave_lock_in_place()
748
496
        # Issue branch lock_write request on the unlocked branch (with locked
749
497
        # repo).
750
498
        response = request.execute(
751
 
            '', 'branch token', repo_token)
 
499
            backing.local_abspath(''), 'branch token', repo_token)
752
500
        self.assertEqual(
753
501
            SmartServerResponse(('TokenMismatch',)), response)
754
502
 
755
503
 
756
 
class TestSmartServerRepositoryRequest(tests.TestCaseWithMemoryTransport):
 
504
class TestSmartServerRepositoryRequest(tests.TestCaseWithTransport):
757
505
 
758
506
    def test_no_repository(self):
759
507
        """Raise NoRepositoryPresent when there is a bzrdir and no repo."""
766
514
        self.make_repository('.', shared=True)
767
515
        self.make_bzrdir('subdir')
768
516
        self.assertRaises(errors.NoRepositoryPresent,
769
 
            request.execute, 'subdir')
770
 
 
771
 
 
772
 
class TestSmartServerRepositoryGetParentMap(tests.TestCaseWithMemoryTransport):
773
 
 
774
 
    def test_trivial_bzipped(self):
775
 
        # This tests that the wire encoding is actually bzipped
776
 
        backing = self.get_transport()
777
 
        request = smart.repository.SmartServerRepositoryGetParentMap(backing)
778
 
        tree = self.make_branch_and_memory_tree('.')
779
 
 
780
 
        self.assertEqual(None,
781
 
            request.execute('', 'missing-id'))
782
 
        # Note that it returns a body (of '' bzipped).
783
 
        self.assertEqual(
784
 
            SuccessfulSmartServerResponse(('ok', ), bz2.compress('')),
785
 
            request.do_body('\n\n0\n'))
786
 
 
787
 
 
788
 
class TestSmartServerRepositoryGetRevisionGraph(tests.TestCaseWithMemoryTransport):
 
517
            request.execute, backing.local_abspath('subdir'))
 
518
 
 
519
 
 
520
class TestSmartServerRepositoryGetRevisionGraph(tests.TestCaseWithTransport):
789
521
 
790
522
    def test_none_argument(self):
791
523
        backing = self.get_transport()
800
532
        # the lines of revision_id->revision_parent_list has no guaranteed
801
533
        # order coming out of a dict, so sort both our test and response
802
534
        lines = sorted([' '.join([r2, r1]), r1])
803
 
        response = request.execute('', '')
 
535
        response = request.execute(backing.local_abspath(''), '')
804
536
        response.body = '\n'.join(sorted(response.body.split('\n')))
805
537
 
806
538
        self.assertEqual(
818
550
        tree.unlock()
819
551
 
820
552
        self.assertEqual(SmartServerResponse(('ok', ), rev_id_utf8),
821
 
            request.execute('', rev_id_utf8))
 
553
            request.execute(backing.local_abspath(''), rev_id_utf8))
822
554
    
823
555
    def test_no_such_revision(self):
824
556
        backing = self.get_transport()
832
564
        # Note that it still returns body (of zero bytes).
833
565
        self.assertEqual(
834
566
            SmartServerResponse(('nosuchrevision', 'missingrevision', ), ''),
835
 
            request.execute('', 'missingrevision'))
836
 
 
837
 
 
838
 
class TestSmartServerRequestHasRevision(tests.TestCaseWithMemoryTransport):
 
567
            request.execute(backing.local_abspath(''), 'missingrevision'))
 
568
 
 
569
 
 
570
class TestSmartServerRequestHasRevision(tests.TestCaseWithTransport):
839
571
 
840
572
    def test_missing_revision(self):
841
573
        """For a missing revision, ('no', ) is returned."""
843
575
        request = smart.repository.SmartServerRequestHasRevision(backing)
844
576
        self.make_repository('.')
845
577
        self.assertEqual(SmartServerResponse(('no', )),
846
 
            request.execute('', 'revid'))
 
578
            request.execute(backing.local_abspath(''), 'revid'))
847
579
 
848
580
    def test_present_revision(self):
849
581
        """For a present revision, ('yes', ) is returned."""
857
589
        tree.unlock()
858
590
        self.assertTrue(tree.branch.repository.has_revision(rev_id_utf8))
859
591
        self.assertEqual(SmartServerResponse(('yes', )),
860
 
            request.execute('', rev_id_utf8))
861
 
 
862
 
 
863
 
class TestSmartServerRepositoryGatherStats(tests.TestCaseWithMemoryTransport):
 
592
            request.execute(backing.local_abspath(''), rev_id_utf8))
 
593
 
 
594
 
 
595
class TestSmartServerRepositoryGatherStats(tests.TestCaseWithTransport):
864
596
 
865
597
    def test_empty_revid(self):
866
598
        """With an empty revid, we get only size an number and revisions"""
868
600
        request = smart.repository.SmartServerRepositoryGatherStats(backing)
869
601
        repository = self.make_repository('.')
870
602
        stats = repository.gather_stats()
871
 
        expected_body = 'revisions: 0\n'
 
603
        size = stats['size']
 
604
        expected_body = 'revisions: 0\nsize: %d\n' % size
872
605
        self.assertEqual(SmartServerResponse(('ok', ), expected_body),
873
 
                         request.execute('', '', 'no'))
 
606
                         request.execute(backing.local_abspath(''), '', 'no'))
874
607
 
875
608
    def test_revid_with_committers(self):
876
609
        """For a revid we get more infos."""
887
620
        tree.unlock()
888
621
 
889
622
        stats = tree.branch.repository.gather_stats()
 
623
        size = stats['size']
890
624
        expected_body = ('firstrev: 123456.200 3600\n'
891
625
                         'latestrev: 654321.400 0\n'
892
 
                         'revisions: 2\n')
 
626
                         'revisions: 2\n'
 
627
                         'size: %d\n' % size)
893
628
        self.assertEqual(SmartServerResponse(('ok', ), expected_body),
894
 
                         request.execute('',
 
629
                         request.execute(backing.local_abspath(''),
895
630
                                         rev_id_utf8, 'no'))
896
631
 
897
632
    def test_not_empty_repository_with_committers(self):
910
645
        tree.unlock()
911
646
        stats = tree.branch.repository.gather_stats()
912
647
 
 
648
        size = stats['size']
913
649
        expected_body = ('committers: 2\n'
914
650
                         'firstrev: 123456.200 3600\n'
915
651
                         'latestrev: 654321.400 0\n'
916
 
                         'revisions: 2\n')
 
652
                         'revisions: 2\n'
 
653
                         'size: %d\n' % size)
917
654
        self.assertEqual(SmartServerResponse(('ok', ), expected_body),
918
 
                         request.execute('',
 
655
                         request.execute(backing.local_abspath(''),
919
656
                                         rev_id_utf8, 'yes'))
920
657
 
921
658
 
922
 
class TestSmartServerRepositoryIsShared(tests.TestCaseWithMemoryTransport):
 
659
class TestSmartServerRepositoryIsShared(tests.TestCaseWithTransport):
923
660
 
924
661
    def test_is_shared(self):
925
662
        """For a shared repository, ('yes', ) is returned."""
927
664
        request = smart.repository.SmartServerRepositoryIsShared(backing)
928
665
        self.make_repository('.', shared=True)
929
666
        self.assertEqual(SmartServerResponse(('yes', )),
930
 
            request.execute('', ))
 
667
            request.execute(backing.local_abspath(''), ))
931
668
 
932
669
    def test_is_not_shared(self):
933
670
        """For a shared repository, ('no', ) is returned."""
935
672
        request = smart.repository.SmartServerRepositoryIsShared(backing)
936
673
        self.make_repository('.', shared=False)
937
674
        self.assertEqual(SmartServerResponse(('no', )),
938
 
            request.execute('', ))
939
 
 
940
 
 
941
 
class TestSmartServerRepositoryLockWrite(tests.TestCaseWithMemoryTransport):
 
675
            request.execute(backing.local_abspath(''), ))
 
676
 
 
677
 
 
678
class TestSmartServerRepositoryLockWrite(tests.TestCaseWithTransport):
942
679
 
943
680
    def setUp(self):
944
 
        tests.TestCaseWithMemoryTransport.setUp(self)
 
681
        tests.TestCaseWithTransport.setUp(self)
 
682
        self.reduceLockdirTimeout()
945
683
 
946
684
    def test_lock_write_on_unlocked_repo(self):
947
685
        backing = self.get_transport()
948
686
        request = smart.repository.SmartServerRepositoryLockWrite(backing)
949
 
        repository = self.make_repository('.', format='knit')
950
 
        response = request.execute('')
 
687
        repository = self.make_repository('.')
 
688
        response = request.execute(backing.local_abspath(''))
951
689
        nonce = repository.control_files._lock.peek().get('nonce')
952
690
        self.assertEqual(SmartServerResponse(('ok', nonce)), response)
953
691
        # The repository is now locked.  Verify that with a new repository
958
696
    def test_lock_write_on_locked_repo(self):
959
697
        backing = self.get_transport()
960
698
        request = smart.repository.SmartServerRepositoryLockWrite(backing)
961
 
        repository = self.make_repository('.', format='knit')
 
699
        repository = self.make_repository('.')
962
700
        repository.lock_write()
963
701
        repository.leave_lock_in_place()
964
702
        repository.unlock()
965
 
        response = request.execute('')
 
703
        response = request.execute(backing.local_abspath(''))
966
704
        self.assertEqual(
967
705
            SmartServerResponse(('LockContention',)), response)
968
706
 
969
707
    def test_lock_write_on_readonly_transport(self):
970
708
        backing = self.get_readonly_transport()
971
709
        request = smart.repository.SmartServerRepositoryLockWrite(backing)
972
 
        repository = self.make_repository('.', format='knit')
 
710
        repository = self.make_repository('.')
973
711
        response = request.execute('')
974
712
        self.assertFalse(response.is_successful())
975
713
        self.assertEqual('LockFailed', response.args[0])
976
714
 
977
715
 
978
 
class TestSmartServerRepositoryUnlock(tests.TestCaseWithMemoryTransport):
 
716
class TestSmartServerRepositoryUnlock(tests.TestCaseWithTransport):
979
717
 
980
718
    def setUp(self):
981
 
        tests.TestCaseWithMemoryTransport.setUp(self)
 
719
        tests.TestCaseWithTransport.setUp(self)
 
720
        self.reduceLockdirTimeout()
982
721
 
983
722
    def test_unlock_on_locked_repo(self):
984
723
        backing = self.get_transport()
985
724
        request = smart.repository.SmartServerRepositoryUnlock(backing)
986
 
        repository = self.make_repository('.', format='knit')
 
725
        repository = self.make_repository('.')
987
726
        token = repository.lock_write()
988
727
        repository.leave_lock_in_place()
989
728
        repository.unlock()
990
 
        response = request.execute('', token)
 
729
        response = request.execute(backing.local_abspath(''), token)
991
730
        self.assertEqual(
992
731
            SmartServerResponse(('ok',)), response)
993
732
        # The repository is now unlocked.  Verify that with a new repository
999
738
    def test_unlock_on_unlocked_repo(self):
1000
739
        backing = self.get_transport()
1001
740
        request = smart.repository.SmartServerRepositoryUnlock(backing)
1002
 
        repository = self.make_repository('.', format='knit')
1003
 
        response = request.execute('', 'some token')
 
741
        repository = self.make_repository('.')
 
742
        response = request.execute(backing.local_abspath(''), 'some token')
1004
743
        self.assertEqual(
1005
744
            SmartServerResponse(('TokenMismatch',)), response)
1006
745
 
1007
746
 
1008
 
class TestSmartServerIsReadonly(tests.TestCaseWithMemoryTransport):
 
747
class TestSmartServerRepositoryTarball(tests.TestCaseWithTransport):
 
748
 
 
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
 
754
        # not be copied
 
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,
 
761
            mode='r|bz2')
 
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")
 
769
 
 
770
 
 
771
class TestSmartServerRepositoryStreamKnitData(tests.TestCaseWithTransport):
 
772
 
 
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('.')
 
777
        tree.lock_write()
 
778
        tree.add('')
 
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)
 
783
        tree.unlock()
 
784
 
 
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))
 
789
        names = []
 
790
        for [name], read_bytes in unpacker.iter_records():
 
791
            names.append(name)
 
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
 
796
            # contents?
 
797
        
 
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)
 
804
        self.assertEqual(
 
805
            SmartServerResponse(('NoSuchRevision', rev_id1_utf8)),
 
806
            response)
 
807
 
 
808
 
 
809
class TestSmartServerIsReadonly(tests.TestCaseWithTransport):
1009
810
 
1010
811
    def test_is_readonly_no(self):
1011
812
        backing = self.get_transport()
1025
826
class TestHandlers(tests.TestCase):
1026
827
    """Tests for the request.request_handlers object."""
1027
828
 
1028
 
    def test_all_registrations_exist(self):
1029
 
        """All registered request_handlers can be found."""
1030
 
        # If there's a typo in a register_lazy call, this loop will fail with
1031
 
        # an AttributeError.
1032
 
        for key, item in smart.request.request_handlers.iteritems():
1033
 
            pass
1034
 
 
1035
829
    def test_registered_methods(self):
1036
830
        """Test that known methods are registered to the correct object."""
1037
831
        self.assertEqual(
1050
844
            smart.request.request_handlers.get('Branch.set_last_revision'),
1051
845
            smart.branch.SmartServerBranchRequestSetLastRevision)
1052
846
        self.assertEqual(
1053
 
            smart.request.request_handlers.get('Branch.set_last_revision_info'),
1054
 
            smart.branch.SmartServerBranchRequestSetLastRevisionInfo)
1055
 
        self.assertEqual(
1056
847
            smart.request.request_handlers.get('Branch.unlock'),
1057
848
            smart.branch.SmartServerBranchRequestUnlock)
1058
849
        self.assertEqual(
1059
850
            smart.request.request_handlers.get('BzrDir.find_repository'),
1060
 
            smart.bzrdir.SmartServerRequestFindRepositoryV1)
1061
 
        self.assertEqual(
1062
 
            smart.request.request_handlers.get('BzrDir.find_repositoryV2'),
1063
 
            smart.bzrdir.SmartServerRequestFindRepositoryV2)
 
851
            smart.bzrdir.SmartServerRequestFindRepository)
1064
852
        self.assertEqual(
1065
853
            smart.request.request_handlers.get('BzrDirFormat.initialize'),
1066
854
            smart.bzrdir.SmartServerRequestInitializeBzrDir)
1071
859
            smart.request.request_handlers.get('Repository.gather_stats'),
1072
860
            smart.repository.SmartServerRepositoryGatherStats)
1073
861
        self.assertEqual(
1074
 
            smart.request.request_handlers.get('Repository.get_parent_map'),
1075
 
            smart.repository.SmartServerRepositoryGetParentMap)
1076
 
        self.assertEqual(
1077
862
            smart.request.request_handlers.get(
1078
863
                'Repository.get_revision_graph'),
1079
864
            smart.repository.SmartServerRepositoryGetRevisionGraph)
1087
872
            smart.request.request_handlers.get('Repository.lock_write'),
1088
873
            smart.repository.SmartServerRepositoryLockWrite)
1089
874
        self.assertEqual(
 
875
            smart.request.request_handlers.get(
 
876
                'Repository.stream_knit_data_for_revisions'),
 
877
            smart.repository.SmartServerRepositoryStreamKnitDataForRevisions)
 
878
        self.assertEqual(
1090
879
            smart.request.request_handlers.get('Repository.tarball'),
1091
880
            smart.repository.SmartServerRepositoryTarball)
1092
881
        self.assertEqual(