~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_smart.py

merge merge tweaks from aaron, which includes latest .dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007 Canonical Ltd
2
 
#
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.
7
 
#
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.
12
 
#
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
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
29
 
import tarfile
30
 
 
31
 
from bzrlib import (
32
 
    bzrdir,
33
 
    errors,
34
 
    pack,
35
 
    smart,
36
 
    tests,
37
 
    urlutils,
38
 
    )
39
 
from bzrlib.branch import BranchReferenceFormat
40
 
import bzrlib.smart.branch
41
 
import bzrlib.smart.bzrdir
42
 
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
 
from bzrlib.util import bencode
56
 
 
57
 
 
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
 
class TestCaseWithSmartMedium(tests.TestCaseWithTransport):
100
 
 
101
 
    def setUp(self):
102
 
        super(TestCaseWithSmartMedium, self).setUp()
103
 
        # We're allowed to set  the transport class here, so that we don't use
104
 
        # the default or a parameterized class, but rather use the
105
 
        # TestCaseWithTransport infrastructure to set up a smart server and
106
 
        # 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())
111
 
 
112
 
    def get_smart_medium(self):
113
 
        """Get a smart medium to use in tests."""
114
 
        return self.get_transport().get_smart_medium()
115
 
 
116
 
 
117
 
class TestSmartServerResponse(tests.TestCase):
118
 
 
119
 
    def test__eq__(self):
120
 
        self.assertEqual(SmartServerResponse(('ok', )),
121
 
            SmartServerResponse(('ok', )))
122
 
        self.assertEqual(SmartServerResponse(('ok', ), 'body'),
123
 
            SmartServerResponse(('ok', ), 'body'))
124
 
        self.assertNotEqual(SmartServerResponse(('ok', )),
125
 
            SmartServerResponse(('notok', )))
126
 
        self.assertNotEqual(SmartServerResponse(('ok', ), 'body'),
127
 
            SmartServerResponse(('ok', )))
128
 
        self.assertNotEqual(None,
129
 
            SmartServerResponse(('ok', )))
130
 
 
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):
164
 
    """Tests for BzrDir.find_repository."""
165
 
 
166
 
    def test_no_repository(self):
167
 
        """When there is no repository to be found, ('norepository', ) is returned."""
168
 
        backing = self.get_transport()
169
 
        request = self._request_class(backing)
170
 
        self.make_bzrdir('.')
171
 
        self.assertEqual(SmartServerResponse(('norepository', )),
172
 
            request.execute(''))
173
 
 
174
 
    def test_nonshared_repository(self):
175
 
        # nonshared repositorys only allow 'find' to return a handle when the 
176
 
        # path the repository is being searched on is the same as that that 
177
 
        # the repository is at.
178
 
        backing = self.get_transport()
179
 
        request = self._request_class(backing)
180
 
        result = self._make_repository_and_result()
181
 
        self.assertEqual(result, request.execute(''))
182
 
        self.make_bzrdir('subdir')
183
 
        self.assertEqual(SmartServerResponse(('norepository', )),
184
 
            request.execute('subdir'))
185
 
 
186
 
    def _make_repository_and_result(self, shared=False, format=None):
187
 
        """Convenience function to setup a repository.
188
 
 
189
 
        :result: The SmartServerResponse to expect when opening it.
190
 
        """
191
 
        repo = self.make_repository('.', shared=shared, format=format)
192
 
        if repo.supports_rich_root():
193
 
            rich_root = 'yes'
194
 
        else:
195
 
            rich_root = 'no'
196
 
        if repo._format.supports_tree_reference:
197
 
            subtrees = 'yes'
198
 
        else:
199
 
            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))
208
 
 
209
 
    def test_shared_repository(self):
210
 
        """When there is a shared repository, we get 'ok', 'relpath-to-repo'."""
211
 
        backing = self.get_transport()
212
 
        request = self._request_class(backing)
213
 
        result = self._make_repository_and_result(shared=True)
214
 
        self.assertEqual(result, request.execute(''))
215
 
        self.make_bzrdir('subdir')
216
 
        result2 = SmartServerResponse(result.args[0:1] + ('..', ) + result.args[2:])
217
 
        self.assertEqual(result2,
218
 
            request.execute('subdir'))
219
 
        self.make_bzrdir('subdir/deeper')
220
 
        result3 = SmartServerResponse(result.args[0:1] + ('../..', ) + result.args[2:])
221
 
        self.assertEqual(result3,
222
 
            request.execute('subdir/deeper'))
223
 
 
224
 
    def test_rich_root_and_subtree_encoding(self):
225
 
        """Test for the format attributes for rich root and subtree support."""
226
 
        backing = self.get_transport()
227
 
        request = self._request_class(backing)
228
 
        result = self._make_repository_and_result(format='dirstate-with-subtree')
229
 
        # check the test will be valid
230
 
        self.assertEqual('yes', result.args[2])
231
 
        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):
245
 
 
246
 
    def test_empty_dir(self):
247
 
        """Initializing an empty dir should succeed and do it."""
248
 
        backing = self.get_transport()
249
 
        request = smart.bzrdir.SmartServerRequestInitializeBzrDir(backing)
250
 
        self.assertEqual(SmartServerResponse(('ok', )),
251
 
            request.execute(''))
252
 
        made_dir = bzrdir.BzrDir.open_from_transport(backing)
253
 
        # no branch, tree or repository is expected with the current 
254
 
        # default formart.
255
 
        self.assertRaises(errors.NoWorkingTree, made_dir.open_workingtree)
256
 
        self.assertRaises(errors.NotBranchError, made_dir.open_branch)
257
 
        self.assertRaises(errors.NoRepositoryPresent, made_dir.open_repository)
258
 
 
259
 
    def test_missing_dir(self):
260
 
        """Initializing a missing directory should fail like the bzrdir api."""
261
 
        backing = self.get_transport()
262
 
        request = smart.bzrdir.SmartServerRequestInitializeBzrDir(backing)
263
 
        self.assertRaises(errors.NoSuchFile,
264
 
            request.execute, 'subdir')
265
 
 
266
 
    def test_initialized_dir(self):
267
 
        """Initializing an extant bzrdir should fail like the bzrdir api."""
268
 
        backing = self.get_transport()
269
 
        request = smart.bzrdir.SmartServerRequestInitializeBzrDir(backing)
270
 
        self.make_bzrdir('subdir')
271
 
        self.assertRaises(errors.FileExists,
272
 
            request.execute, 'subdir')
273
 
 
274
 
 
275
 
class TestSmartServerRequestOpenBranch(TestCaseWithChrootedTransport):
276
 
 
277
 
    def test_no_branch(self):
278
 
        """When there is no branch, ('nobranch', ) is returned."""
279
 
        backing = self.get_transport()
280
 
        request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
281
 
        self.make_bzrdir('.')
282
 
        self.assertEqual(SmartServerResponse(('nobranch', )),
283
 
            request.execute(''))
284
 
 
285
 
    def test_branch(self):
286
 
        """When there is a branch, 'ok' is returned."""
287
 
        backing = self.get_transport()
288
 
        request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
289
 
        self.make_branch('.')
290
 
        self.assertEqual(SmartServerResponse(('ok', '')),
291
 
            request.execute(''))
292
 
 
293
 
    def test_branch_reference(self):
294
 
        """When there is a branch reference, the reference URL is returned."""
295
 
        backing = self.get_transport()
296
 
        request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
297
 
        branch = self.make_branch('branch')
298
 
        checkout = branch.create_checkout('reference',lightweight=True)
299
 
        reference_url = BranchReferenceFormat().get_reference(checkout.bzrdir)
300
 
        self.assertFileEqual(reference_url, 'reference/.bzr/branch/location')
301
 
        self.assertEqual(SmartServerResponse(('ok', reference_url)),
302
 
            request.execute('reference'))
303
 
 
304
 
 
305
 
class TestSmartServerRequestRevisionHistory(tests.TestCaseWithMemoryTransport):
306
 
 
307
 
    def test_empty(self):
308
 
        """For an empty branch, the body is empty."""
309
 
        backing = self.get_transport()
310
 
        request = smart.branch.SmartServerRequestRevisionHistory(backing)
311
 
        self.make_branch('.')
312
 
        self.assertEqual(SmartServerResponse(('ok', ), ''),
313
 
            request.execute(''))
314
 
 
315
 
    def test_not_empty(self):
316
 
        """For a non-empty branch, the body is empty."""
317
 
        backing = self.get_transport()
318
 
        request = smart.branch.SmartServerRequestRevisionHistory(backing)
319
 
        tree = self.make_branch_and_memory_tree('.')
320
 
        tree.lock_write()
321
 
        tree.add('')
322
 
        r1 = tree.commit('1st commit')
323
 
        r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
324
 
        tree.unlock()
325
 
        self.assertEqual(
326
 
            SmartServerResponse(('ok', ), ('\x00'.join([r1, r2]))),
327
 
            request.execute(''))
328
 
 
329
 
 
330
 
class TestSmartServerBranchRequest(tests.TestCaseWithMemoryTransport):
331
 
 
332
 
    def test_no_branch(self):
333
 
        """When there is a bzrdir and no branch, NotBranchError is raised."""
334
 
        backing = self.get_transport()
335
 
        request = smart.branch.SmartServerBranchRequest(backing)
336
 
        self.make_bzrdir('.')
337
 
        self.assertRaises(errors.NotBranchError,
338
 
            request.execute, '')
339
 
 
340
 
    def test_branch_reference(self):
341
 
        """When there is a branch reference, NotBranchError is raised."""
342
 
        backing = self.get_transport()
343
 
        request = smart.branch.SmartServerBranchRequest(backing)
344
 
        branch = self.make_branch('branch')
345
 
        checkout = branch.create_checkout('reference',lightweight=True)
346
 
        self.assertRaises(errors.NotBranchError,
347
 
            request.execute, 'checkout')
348
 
 
349
 
 
350
 
class TestSmartServerBranchRequestLastRevisionInfo(tests.TestCaseWithMemoryTransport):
351
 
 
352
 
    def test_empty(self):
353
 
        """For an empty branch, the result is ('ok', '0', 'null:')."""
354
 
        backing = self.get_transport()
355
 
        request = smart.branch.SmartServerBranchRequestLastRevisionInfo(backing)
356
 
        self.make_branch('.')
357
 
        self.assertEqual(SmartServerResponse(('ok', '0', 'null:')),
358
 
            request.execute(''))
359
 
 
360
 
    def test_not_empty(self):
361
 
        """For a non-empty branch, the result is ('ok', 'revno', 'revid')."""
362
 
        backing = self.get_transport()
363
 
        request = smart.branch.SmartServerBranchRequestLastRevisionInfo(backing)
364
 
        tree = self.make_branch_and_memory_tree('.')
365
 
        tree.lock_write()
366
 
        tree.add('')
367
 
        rev_id_utf8 = u'\xc8'.encode('utf-8')
368
 
        r1 = tree.commit('1st commit')
369
 
        r2 = tree.commit('2nd commit', rev_id=rev_id_utf8)
370
 
        tree.unlock()
371
 
        self.assertEqual(
372
 
            SmartServerResponse(('ok', '2', rev_id_utf8)),
373
 
            request.execute(''))
374
 
 
375
 
 
376
 
class TestSmartServerBranchRequestGetConfigFile(tests.TestCaseWithMemoryTransport):
377
 
 
378
 
    def test_default(self):
379
 
        """With no file, we get empty content."""
380
 
        backing = self.get_transport()
381
 
        request = smart.branch.SmartServerBranchGetConfigFile(backing)
382
 
        branch = self.make_branch('.')
383
 
        # there should be no file by default
384
 
        content = ''
385
 
        self.assertEqual(SmartServerResponse(('ok', ), content),
386
 
            request.execute(''))
387
 
 
388
 
    def test_with_content(self):
389
 
        # SmartServerBranchGetConfigFile should return the content from
390
 
        # branch.control_files.get('branch.conf') for now - in the future it may
391
 
        # perform more complex processing. 
392
 
        backing = self.get_transport()
393
 
        request = smart.branch.SmartServerBranchGetConfigFile(backing)
394
 
        branch = self.make_branch('.')
395
 
        branch._transport.put_bytes('branch.conf', 'foo bar baz')
396
 
        self.assertEqual(SmartServerResponse(('ok', ), 'foo bar baz'),
397
 
            request.execute(''))
398
 
 
399
 
 
400
 
class TestSmartServerBranchRequestSetLastRevision(tests.TestCaseWithMemoryTransport):
401
 
 
402
 
    def test_empty(self):
403
 
        backing = self.get_transport()
404
 
        request = smart.branch.SmartServerBranchRequestSetLastRevision(backing)
405
 
        b = self.make_branch('.')
406
 
        branch_token = b.lock_write()
407
 
        repo_token = b.repository.lock_write()
408
 
        b.repository.unlock()
409
 
        try:
410
 
            self.assertEqual(SmartServerResponse(('ok',)),
411
 
                request.execute(
412
 
                    '', branch_token, repo_token,
413
 
                    'null:'))
414
 
        finally:
415
 
            b.unlock()
416
 
 
417
 
    def test_not_present_revision_id(self):
418
 
        backing = self.get_transport()
419
 
        request = smart.branch.SmartServerBranchRequestSetLastRevision(backing)
420
 
        b = self.make_branch('.')
421
 
        branch_token = b.lock_write()
422
 
        repo_token = b.repository.lock_write()
423
 
        b.repository.unlock()
424
 
        try:
425
 
            revision_id = 'non-existent revision'
426
 
            self.assertEqual(
427
 
                SmartServerResponse(('NoSuchRevision', revision_id)),
428
 
                request.execute(
429
 
                    '', branch_token, repo_token,
430
 
                    revision_id))
431
 
        finally:
432
 
            b.unlock()
433
 
 
434
 
    def test_revision_id_present(self):
435
 
        backing = self.get_transport()
436
 
        request = smart.branch.SmartServerBranchRequestSetLastRevision(backing)
437
 
        tree = self.make_branch_and_memory_tree('.')
438
 
        tree.lock_write()
439
 
        tree.add('')
440
 
        rev_id_utf8 = u'\xc8'.encode('utf-8')
441
 
        r1 = tree.commit('1st commit', rev_id=rev_id_utf8)
442
 
        r2 = tree.commit('2nd commit')
443
 
        tree.unlock()
444
 
        branch_token = tree.branch.lock_write()
445
 
        repo_token = tree.branch.repository.lock_write()
446
 
        tree.branch.repository.unlock()
447
 
        try:
448
 
            self.assertEqual(
449
 
                SmartServerResponse(('ok',)),
450
 
                request.execute(
451
 
                    '', branch_token, repo_token,
452
 
                    rev_id_utf8))
453
 
            self.assertEqual([rev_id_utf8], tree.branch.revision_history())
454
 
        finally:
455
 
            tree.branch.unlock()
456
 
 
457
 
    def test_revision_id_present2(self):
458
 
        backing = self.get_transport()
459
 
        request = smart.branch.SmartServerBranchRequestSetLastRevision(backing)
460
 
        tree = self.make_branch_and_memory_tree('.')
461
 
        tree.lock_write()
462
 
        tree.add('')
463
 
        rev_id_utf8 = u'\xc8'.encode('utf-8')
464
 
        r1 = tree.commit('1st commit', rev_id=rev_id_utf8)
465
 
        r2 = tree.commit('2nd commit')
466
 
        tree.unlock()
467
 
        tree.branch.set_revision_history([])
468
 
        branch_token = tree.branch.lock_write()
469
 
        repo_token = tree.branch.repository.lock_write()
470
 
        tree.branch.repository.unlock()
471
 
        try:
472
 
            self.assertEqual(
473
 
                SmartServerResponse(('ok',)),
474
 
                request.execute(
475
 
                    '', branch_token, repo_token,
476
 
                    rev_id_utf8))
477
 
            self.assertEqual([rev_id_utf8], tree.branch.revision_history())
478
 
        finally:
479
 
            tree.branch.unlock()
480
 
 
481
 
 
482
 
class TestSmartServerBranchRequestSetLastRevisionInfo(tests.TestCaseWithTransport):
483
 
 
484
 
    def lock_branch(self, branch):
485
 
        branch_token = branch.lock_write()
486
 
        repo_token = branch.repository.lock_write()
487
 
        branch.repository.unlock()
488
 
        self.addCleanup(branch.unlock)
489
 
        return branch_token, repo_token
490
 
 
491
 
    def make_locked_branch(self, format=None):
492
 
        branch = self.make_branch('.', format=format)
493
 
        branch_token, repo_token = self.lock_branch(branch)
494
 
        return branch, branch_token, repo_token
495
 
 
496
 
    def test_empty(self):
497
 
        """An empty branch can have its last revision set to 'null:'."""
498
 
        b, branch_token, repo_token = self.make_locked_branch()
499
 
        backing = self.get_transport()
500
 
        request = smart.branch.SmartServerBranchRequestSetLastRevisionInfo(
501
 
            backing)
502
 
        response = request.execute('', branch_token, repo_token, '0', 'null:')
503
 
        self.assertEqual(SmartServerResponse(('ok',)), response)
504
 
 
505
 
    def assertBranchLastRevisionInfo(self, expected_info, branch_relpath):
506
 
        branch = bzrdir.BzrDir.open(branch_relpath).open_branch()
507
 
        self.assertEqual(expected_info, branch.last_revision_info())
508
 
 
509
 
    def test_branch_revision_info_is_updated(self):
510
 
        """This method really does update the branch last revision info."""
511
 
        tree = self.make_branch_and_memory_tree('.')
512
 
        tree.lock_write()
513
 
        tree.add('')
514
 
        tree.commit('First commit', rev_id='revision-1')
515
 
        tree.commit('Second commit', rev_id='revision-2')
516
 
        tree.unlock()
517
 
        branch = tree.branch
518
 
 
519
 
        branch_token, repo_token = self.lock_branch(branch)
520
 
        backing = self.get_transport()
521
 
        request = smart.branch.SmartServerBranchRequestSetLastRevisionInfo(
522
 
            backing)
523
 
        self.assertBranchLastRevisionInfo((2, 'revision-2'), '.')
524
 
        response = request.execute(
525
 
            '', branch_token, repo_token, '1', 'revision-1')
526
 
        self.assertEqual(SmartServerResponse(('ok',)), response)
527
 
        self.assertBranchLastRevisionInfo((1, 'revision-1'), '.')
528
 
 
529
 
    def test_not_present_revid(self):
530
 
        """Some branch formats will check that the revision is present in the
531
 
        repository.  When that check fails, a NoSuchRevision error is returned
532
 
        to the client.
533
 
        """
534
 
        # Make a knit format branch, because that format checks the values
535
 
        # given to set_last_revision_info.
536
 
        b, branch_token, repo_token = self.make_locked_branch(format='knit')
537
 
        backing = self.get_transport()
538
 
        request = smart.branch.SmartServerBranchRequestSetLastRevisionInfo(
539
 
            backing)
540
 
        response = request.execute(
541
 
            '', branch_token, repo_token, '1', 'not-present')
542
 
        self.assertEqual(
543
 
            SmartServerResponse(('NoSuchRevision', 'not-present')), response)
544
 
 
545
 
 
546
 
class TestSmartServerBranchRequestLockWrite(tests.TestCaseWithMemoryTransport):
547
 
 
548
 
    def setUp(self):
549
 
        tests.TestCaseWithMemoryTransport.setUp(self)
550
 
 
551
 
    def test_lock_write_on_unlocked_branch(self):
552
 
        backing = self.get_transport()
553
 
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
554
 
        branch = self.make_branch('.', format='knit')
555
 
        repository = branch.repository
556
 
        response = request.execute('')
557
 
        branch_nonce = branch.control_files._lock.peek().get('nonce')
558
 
        repository_nonce = repository.control_files._lock.peek().get('nonce')
559
 
        self.assertEqual(
560
 
            SmartServerResponse(('ok', branch_nonce, repository_nonce)),
561
 
            response)
562
 
        # The branch (and associated repository) is now locked.  Verify that
563
 
        # with a new branch object.
564
 
        new_branch = repository.bzrdir.open_branch()
565
 
        self.assertRaises(errors.LockContention, new_branch.lock_write)
566
 
 
567
 
    def test_lock_write_on_locked_branch(self):
568
 
        backing = self.get_transport()
569
 
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
570
 
        branch = self.make_branch('.')
571
 
        branch.lock_write()
572
 
        branch.leave_lock_in_place()
573
 
        branch.unlock()
574
 
        response = request.execute('')
575
 
        self.assertEqual(
576
 
            SmartServerResponse(('LockContention',)), response)
577
 
 
578
 
    def test_lock_write_with_tokens_on_locked_branch(self):
579
 
        backing = self.get_transport()
580
 
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
581
 
        branch = self.make_branch('.', format='knit')
582
 
        branch_token = branch.lock_write()
583
 
        repo_token = branch.repository.lock_write()
584
 
        branch.repository.unlock()
585
 
        branch.leave_lock_in_place()
586
 
        branch.repository.leave_lock_in_place()
587
 
        branch.unlock()
588
 
        response = request.execute('',
589
 
                                   branch_token, repo_token)
590
 
        self.assertEqual(
591
 
            SmartServerResponse(('ok', branch_token, repo_token)), response)
592
 
 
593
 
    def test_lock_write_with_mismatched_tokens_on_locked_branch(self):
594
 
        backing = self.get_transport()
595
 
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
596
 
        branch = self.make_branch('.', format='knit')
597
 
        branch_token = branch.lock_write()
598
 
        repo_token = branch.repository.lock_write()
599
 
        branch.repository.unlock()
600
 
        branch.leave_lock_in_place()
601
 
        branch.repository.leave_lock_in_place()
602
 
        branch.unlock()
603
 
        response = request.execute('',
604
 
                                   branch_token+'xxx', repo_token)
605
 
        self.assertEqual(
606
 
            SmartServerResponse(('TokenMismatch',)), response)
607
 
 
608
 
    def test_lock_write_on_locked_repo(self):
609
 
        backing = self.get_transport()
610
 
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
611
 
        branch = self.make_branch('.', format='knit')
612
 
        branch.repository.lock_write()
613
 
        branch.repository.leave_lock_in_place()
614
 
        branch.repository.unlock()
615
 
        response = request.execute('')
616
 
        self.assertEqual(
617
 
            SmartServerResponse(('LockContention',)), response)
618
 
 
619
 
    def test_lock_write_on_readonly_transport(self):
620
 
        backing = self.get_readonly_transport()
621
 
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
622
 
        branch = self.make_branch('.')
623
 
        root = self.get_transport().clone('/')
624
 
        path = urlutils.relative_url(root.base, self.get_transport().base)
625
 
        response = request.execute(path)
626
 
        error_name, lock_str, why_str = response.args
627
 
        self.assertFalse(response.is_successful())
628
 
        self.assertEqual('LockFailed', error_name)
629
 
 
630
 
 
631
 
class TestSmartServerBranchRequestUnlock(tests.TestCaseWithMemoryTransport):
632
 
 
633
 
    def setUp(self):
634
 
        tests.TestCaseWithMemoryTransport.setUp(self)
635
 
 
636
 
    def test_unlock_on_locked_branch_and_repo(self):
637
 
        backing = self.get_transport()
638
 
        request = smart.branch.SmartServerBranchRequestUnlock(backing)
639
 
        branch = self.make_branch('.', format='knit')
640
 
        # Lock the branch
641
 
        branch_token = branch.lock_write()
642
 
        repo_token = branch.repository.lock_write()
643
 
        branch.repository.unlock()
644
 
        # Unlock the branch (and repo) object, leaving the physical locks
645
 
        # in place.
646
 
        branch.leave_lock_in_place()
647
 
        branch.repository.leave_lock_in_place()
648
 
        branch.unlock()
649
 
        response = request.execute('',
650
 
                                   branch_token, repo_token)
651
 
        self.assertEqual(
652
 
            SmartServerResponse(('ok',)), response)
653
 
        # The branch is now unlocked.  Verify that with a new branch
654
 
        # object.
655
 
        new_branch = branch.bzrdir.open_branch()
656
 
        new_branch.lock_write()
657
 
        new_branch.unlock()
658
 
 
659
 
    def test_unlock_on_unlocked_branch_unlocked_repo(self):
660
 
        backing = self.get_transport()
661
 
        request = smart.branch.SmartServerBranchRequestUnlock(backing)
662
 
        branch = self.make_branch('.', format='knit')
663
 
        response = request.execute(
664
 
            '', 'branch token', 'repo token')
665
 
        self.assertEqual(
666
 
            SmartServerResponse(('TokenMismatch',)), response)
667
 
 
668
 
    def test_unlock_on_unlocked_branch_locked_repo(self):
669
 
        backing = self.get_transport()
670
 
        request = smart.branch.SmartServerBranchRequestUnlock(backing)
671
 
        branch = self.make_branch('.', format='knit')
672
 
        # Lock the repository.
673
 
        repo_token = branch.repository.lock_write()
674
 
        branch.repository.leave_lock_in_place()
675
 
        branch.repository.unlock()
676
 
        # Issue branch lock_write request on the unlocked branch (with locked
677
 
        # repo).
678
 
        response = request.execute(
679
 
            '', 'branch token', repo_token)
680
 
        self.assertEqual(
681
 
            SmartServerResponse(('TokenMismatch',)), response)
682
 
 
683
 
 
684
 
class TestSmartServerRepositoryRequest(tests.TestCaseWithMemoryTransport):
685
 
 
686
 
    def test_no_repository(self):
687
 
        """Raise NoRepositoryPresent when there is a bzrdir and no repo."""
688
 
        # we test this using a shared repository above the named path,
689
 
        # thus checking the right search logic is used - that is, that
690
 
        # its the exact path being looked at and the server is not
691
 
        # searching.
692
 
        backing = self.get_transport()
693
 
        request = smart.repository.SmartServerRepositoryRequest(backing)
694
 
        self.make_repository('.', shared=True)
695
 
        self.make_bzrdir('subdir')
696
 
        self.assertRaises(errors.NoRepositoryPresent,
697
 
            request.execute, 'subdir')
698
 
 
699
 
 
700
 
class TestSmartServerRepositoryGetParentMap(tests.TestCaseWithTransport):
701
 
 
702
 
    def test_trivial_bzipped(self):
703
 
        # This tests that the wire encoding is actually bzipped
704
 
        backing = self.get_transport()
705
 
        request = smart.repository.SmartServerRepositoryGetParentMap(backing)
706
 
        tree = self.make_branch_and_memory_tree('.')
707
 
 
708
 
        self.assertEqual(None,
709
 
            request.execute('', 'missing-id'))
710
 
        # Note that it returns a body (of '' bzipped).
711
 
        self.assertEqual(
712
 
            SuccessfulSmartServerResponse(('ok', ), bz2.compress('')),
713
 
            request.do_body('\n\n0\n'))
714
 
 
715
 
 
716
 
class TestSmartServerRepositoryGetRevisionGraph(tests.TestCaseWithMemoryTransport):
717
 
 
718
 
    def test_none_argument(self):
719
 
        backing = self.get_transport()
720
 
        request = smart.repository.SmartServerRepositoryGetRevisionGraph(backing)
721
 
        tree = self.make_branch_and_memory_tree('.')
722
 
        tree.lock_write()
723
 
        tree.add('')
724
 
        r1 = tree.commit('1st commit')
725
 
        r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
726
 
        tree.unlock()
727
 
 
728
 
        # the lines of revision_id->revision_parent_list has no guaranteed
729
 
        # order coming out of a dict, so sort both our test and response
730
 
        lines = sorted([' '.join([r2, r1]), r1])
731
 
        response = request.execute('', '')
732
 
        response.body = '\n'.join(sorted(response.body.split('\n')))
733
 
 
734
 
        self.assertEqual(
735
 
            SmartServerResponse(('ok', ), '\n'.join(lines)), response)
736
 
 
737
 
    def test_specific_revision_argument(self):
738
 
        backing = self.get_transport()
739
 
        request = smart.repository.SmartServerRepositoryGetRevisionGraph(backing)
740
 
        tree = self.make_branch_and_memory_tree('.')
741
 
        tree.lock_write()
742
 
        tree.add('')
743
 
        rev_id_utf8 = u'\xc9'.encode('utf-8')
744
 
        r1 = tree.commit('1st commit', rev_id=rev_id_utf8)
745
 
        r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
746
 
        tree.unlock()
747
 
 
748
 
        self.assertEqual(SmartServerResponse(('ok', ), rev_id_utf8),
749
 
            request.execute('', rev_id_utf8))
750
 
    
751
 
    def test_no_such_revision(self):
752
 
        backing = self.get_transport()
753
 
        request = smart.repository.SmartServerRepositoryGetRevisionGraph(backing)
754
 
        tree = self.make_branch_and_memory_tree('.')
755
 
        tree.lock_write()
756
 
        tree.add('')
757
 
        r1 = tree.commit('1st commit')
758
 
        tree.unlock()
759
 
 
760
 
        # Note that it still returns body (of zero bytes).
761
 
        self.assertEqual(
762
 
            SmartServerResponse(('nosuchrevision', 'missingrevision', ), ''),
763
 
            request.execute('', 'missingrevision'))
764
 
 
765
 
 
766
 
class TestSmartServerRequestHasRevision(tests.TestCaseWithMemoryTransport):
767
 
 
768
 
    def test_missing_revision(self):
769
 
        """For a missing revision, ('no', ) is returned."""
770
 
        backing = self.get_transport()
771
 
        request = smart.repository.SmartServerRequestHasRevision(backing)
772
 
        self.make_repository('.')
773
 
        self.assertEqual(SmartServerResponse(('no', )),
774
 
            request.execute('', 'revid'))
775
 
 
776
 
    def test_present_revision(self):
777
 
        """For a present revision, ('yes', ) is returned."""
778
 
        backing = self.get_transport()
779
 
        request = smart.repository.SmartServerRequestHasRevision(backing)
780
 
        tree = self.make_branch_and_memory_tree('.')
781
 
        tree.lock_write()
782
 
        tree.add('')
783
 
        rev_id_utf8 = u'\xc8abc'.encode('utf-8')
784
 
        r1 = tree.commit('a commit', rev_id=rev_id_utf8)
785
 
        tree.unlock()
786
 
        self.assertTrue(tree.branch.repository.has_revision(rev_id_utf8))
787
 
        self.assertEqual(SmartServerResponse(('yes', )),
788
 
            request.execute('', rev_id_utf8))
789
 
 
790
 
 
791
 
class TestSmartServerRepositoryGatherStats(tests.TestCaseWithMemoryTransport):
792
 
 
793
 
    def test_empty_revid(self):
794
 
        """With an empty revid, we get only size an number and revisions"""
795
 
        backing = self.get_transport()
796
 
        request = smart.repository.SmartServerRepositoryGatherStats(backing)
797
 
        repository = self.make_repository('.')
798
 
        stats = repository.gather_stats()
799
 
        size = stats['size']
800
 
        expected_body = 'revisions: 0\nsize: %d\n' % size
801
 
        self.assertEqual(SmartServerResponse(('ok', ), expected_body),
802
 
                         request.execute('', '', 'no'))
803
 
 
804
 
    def test_revid_with_committers(self):
805
 
        """For a revid we get more infos."""
806
 
        backing = self.get_transport()
807
 
        rev_id_utf8 = u'\xc8abc'.encode('utf-8')
808
 
        request = smart.repository.SmartServerRepositoryGatherStats(backing)
809
 
        tree = self.make_branch_and_memory_tree('.')
810
 
        tree.lock_write()
811
 
        tree.add('')
812
 
        # Let's build a predictable result
813
 
        tree.commit('a commit', timestamp=123456.2, timezone=3600)
814
 
        tree.commit('a commit', timestamp=654321.4, timezone=0,
815
 
                    rev_id=rev_id_utf8)
816
 
        tree.unlock()
817
 
 
818
 
        stats = tree.branch.repository.gather_stats()
819
 
        size = stats['size']
820
 
        expected_body = ('firstrev: 123456.200 3600\n'
821
 
                         'latestrev: 654321.400 0\n'
822
 
                         'revisions: 2\n'
823
 
                         'size: %d\n' % size)
824
 
        self.assertEqual(SmartServerResponse(('ok', ), expected_body),
825
 
                         request.execute('',
826
 
                                         rev_id_utf8, 'no'))
827
 
 
828
 
    def test_not_empty_repository_with_committers(self):
829
 
        """For a revid and requesting committers we get the whole thing."""
830
 
        backing = self.get_transport()
831
 
        rev_id_utf8 = u'\xc8abc'.encode('utf-8')
832
 
        request = smart.repository.SmartServerRepositoryGatherStats(backing)
833
 
        tree = self.make_branch_and_memory_tree('.')
834
 
        tree.lock_write()
835
 
        tree.add('')
836
 
        # Let's build a predictable result
837
 
        tree.commit('a commit', timestamp=123456.2, timezone=3600,
838
 
                    committer='foo')
839
 
        tree.commit('a commit', timestamp=654321.4, timezone=0,
840
 
                    committer='bar', rev_id=rev_id_utf8)
841
 
        tree.unlock()
842
 
        stats = tree.branch.repository.gather_stats()
843
 
 
844
 
        size = stats['size']
845
 
        expected_body = ('committers: 2\n'
846
 
                         'firstrev: 123456.200 3600\n'
847
 
                         'latestrev: 654321.400 0\n'
848
 
                         'revisions: 2\n'
849
 
                         'size: %d\n' % size)
850
 
        self.assertEqual(SmartServerResponse(('ok', ), expected_body),
851
 
                         request.execute('',
852
 
                                         rev_id_utf8, 'yes'))
853
 
 
854
 
 
855
 
class TestSmartServerRepositoryIsShared(tests.TestCaseWithMemoryTransport):
856
 
 
857
 
    def test_is_shared(self):
858
 
        """For a shared repository, ('yes', ) is returned."""
859
 
        backing = self.get_transport()
860
 
        request = smart.repository.SmartServerRepositoryIsShared(backing)
861
 
        self.make_repository('.', shared=True)
862
 
        self.assertEqual(SmartServerResponse(('yes', )),
863
 
            request.execute('', ))
864
 
 
865
 
    def test_is_not_shared(self):
866
 
        """For a shared repository, ('no', ) is returned."""
867
 
        backing = self.get_transport()
868
 
        request = smart.repository.SmartServerRepositoryIsShared(backing)
869
 
        self.make_repository('.', shared=False)
870
 
        self.assertEqual(SmartServerResponse(('no', )),
871
 
            request.execute('', ))
872
 
 
873
 
 
874
 
class TestSmartServerRepositoryLockWrite(tests.TestCaseWithMemoryTransport):
875
 
 
876
 
    def setUp(self):
877
 
        tests.TestCaseWithMemoryTransport.setUp(self)
878
 
 
879
 
    def test_lock_write_on_unlocked_repo(self):
880
 
        backing = self.get_transport()
881
 
        request = smart.repository.SmartServerRepositoryLockWrite(backing)
882
 
        repository = self.make_repository('.', format='knit')
883
 
        response = request.execute('')
884
 
        nonce = repository.control_files._lock.peek().get('nonce')
885
 
        self.assertEqual(SmartServerResponse(('ok', nonce)), response)
886
 
        # The repository is now locked.  Verify that with a new repository
887
 
        # object.
888
 
        new_repo = repository.bzrdir.open_repository()
889
 
        self.assertRaises(errors.LockContention, new_repo.lock_write)
890
 
 
891
 
    def test_lock_write_on_locked_repo(self):
892
 
        backing = self.get_transport()
893
 
        request = smart.repository.SmartServerRepositoryLockWrite(backing)
894
 
        repository = self.make_repository('.', format='knit')
895
 
        repository.lock_write()
896
 
        repository.leave_lock_in_place()
897
 
        repository.unlock()
898
 
        response = request.execute('')
899
 
        self.assertEqual(
900
 
            SmartServerResponse(('LockContention',)), response)
901
 
 
902
 
    def test_lock_write_on_readonly_transport(self):
903
 
        backing = self.get_readonly_transport()
904
 
        request = smart.repository.SmartServerRepositoryLockWrite(backing)
905
 
        repository = self.make_repository('.', format='knit')
906
 
        response = request.execute('')
907
 
        self.assertFalse(response.is_successful())
908
 
        self.assertEqual('LockFailed', response.args[0])
909
 
 
910
 
 
911
 
class TestSmartServerRepositoryUnlock(tests.TestCaseWithMemoryTransport):
912
 
 
913
 
    def setUp(self):
914
 
        tests.TestCaseWithMemoryTransport.setUp(self)
915
 
 
916
 
    def test_unlock_on_locked_repo(self):
917
 
        backing = self.get_transport()
918
 
        request = smart.repository.SmartServerRepositoryUnlock(backing)
919
 
        repository = self.make_repository('.', format='knit')
920
 
        token = repository.lock_write()
921
 
        repository.leave_lock_in_place()
922
 
        repository.unlock()
923
 
        response = request.execute('', token)
924
 
        self.assertEqual(
925
 
            SmartServerResponse(('ok',)), response)
926
 
        # The repository is now unlocked.  Verify that with a new repository
927
 
        # object.
928
 
        new_repo = repository.bzrdir.open_repository()
929
 
        new_repo.lock_write()
930
 
        new_repo.unlock()
931
 
 
932
 
    def test_unlock_on_unlocked_repo(self):
933
 
        backing = self.get_transport()
934
 
        request = smart.repository.SmartServerRepositoryUnlock(backing)
935
 
        repository = self.make_repository('.', format='knit')
936
 
        response = request.execute('', 'some token')
937
 
        self.assertEqual(
938
 
            SmartServerResponse(('TokenMismatch',)), response)
939
 
 
940
 
 
941
 
class TestSmartServerRepositoryTarball(tests.TestCaseWithTransport):
942
 
 
943
 
    def test_repository_tarball(self):
944
 
        backing = self.get_transport()
945
 
        request = smart.repository.SmartServerRepositoryTarball(backing)
946
 
        repository = self.make_repository('.')
947
 
        # make some extraneous junk in the repository directory which should
948
 
        # not be copied
949
 
        self.build_tree(['.bzr/repository/extra-junk'])
950
 
        response = request.execute('', 'bz2')
951
 
        self.assertEqual(('ok',), response.args)
952
 
        # body should be a tbz2
953
 
        body_file = StringIO(response.body)
954
 
        body_tar = tarfile.open('body_tar.tbz2', fileobj=body_file,
955
 
            mode='r|bz2')
956
 
        # let's make sure there are some key repository components inside it.
957
 
        # the tarfile returns directories with trailing slashes...
958
 
        names = set([n.rstrip('/') for n in body_tar.getnames()])
959
 
        self.assertTrue('.bzr/repository/lock' in names)
960
 
        self.assertTrue('.bzr/repository/format' in names)
961
 
        self.assertTrue('.bzr/repository/extra-junk' not in names,
962
 
            "extraneous file present in tar file")
963
 
 
964
 
 
965
 
class TestSmartServerRepositoryStreamKnitData(tests.TestCaseWithMemoryTransport):
966
 
 
967
 
    def test_fetch_revisions(self):
968
 
        backing = self.get_transport()
969
 
        request = smart.repository.SmartServerRepositoryStreamKnitDataForRevisions(backing)
970
 
        tree = self.make_branch_and_memory_tree('.')
971
 
        tree.lock_write()
972
 
        tree.add('')
973
 
        rev_id1_utf8 = u'\xc8'.encode('utf-8')
974
 
        rev_id2_utf8 = u'\xc9'.encode('utf-8')
975
 
        r1 = tree.commit('1st commit', rev_id=rev_id1_utf8)
976
 
        r1 = tree.commit('2nd commit', rev_id=rev_id2_utf8)
977
 
        tree.unlock()
978
 
 
979
 
        response = request.execute('', rev_id2_utf8)
980
 
        self.assertEqual(('ok',), response.args)
981
 
        unpacker = pack.ContainerReader(StringIO(response.body))
982
 
        names = []
983
 
        for [name], read_bytes in unpacker.iter_records():
984
 
            names.append(name)
985
 
            bytes = read_bytes(None)
986
 
            # The bytes should be a valid bencoded string.
987
 
            bencode.bdecode(bytes)
988
 
            # XXX: assert that the bencoded knit records have the right
989
 
            # contents?
990
 
        
991
 
    def test_no_such_revision_error(self):
992
 
        backing = self.get_transport()
993
 
        request = smart.repository.SmartServerRepositoryStreamKnitDataForRevisions(backing)
994
 
        repo = self.make_repository('.')
995
 
        rev_id1_utf8 = u'\xc8'.encode('utf-8')
996
 
        response = request.execute('', rev_id1_utf8)
997
 
        self.assertEqual(
998
 
            SmartServerResponse(('NoSuchRevision', rev_id1_utf8)),
999
 
            response)
1000
 
 
1001
 
 
1002
 
class TestSmartServerRepositoryStreamRevisionsChunked(tests.TestCaseWithMemoryTransport):
1003
 
 
1004
 
    def test_fetch_revisions(self):
1005
 
        backing = self.get_transport()
1006
 
        request = smart.repository.SmartServerRepositoryStreamRevisionsChunked(
1007
 
            backing)
1008
 
        tree = self.make_branch_and_memory_tree('.')
1009
 
        tree.lock_write()
1010
 
        tree.add('')
1011
 
        rev_id1_utf8 = u'\xc8'.encode('utf-8')
1012
 
        rev_id2_utf8 = u'\xc9'.encode('utf-8')
1013
 
        tree.commit('1st commit', rev_id=rev_id1_utf8)
1014
 
        tree.commit('2nd commit', rev_id=rev_id2_utf8)
1015
 
        tree.unlock()
1016
 
 
1017
 
        response = request.execute('')
1018
 
        self.assertEqual(None, response)
1019
 
        response = request.do_body("%s\n%s\n1" % (rev_id2_utf8, rev_id1_utf8))
1020
 
        self.assertEqual(('ok',), response.args)
1021
 
        parser = pack.ContainerPushParser()
1022
 
        names = []
1023
 
        for stream_bytes in response.body_stream:
1024
 
            parser.accept_bytes(stream_bytes)
1025
 
            for [name], record_bytes in parser.read_pending_records():
1026
 
                names.append(name)
1027
 
                # The bytes should be a valid bencoded string.
1028
 
                bencode.bdecode(record_bytes)
1029
 
                # XXX: assert that the bencoded knit records have the right
1030
 
                # contents?
1031
 
        
1032
 
    def test_no_such_revision_error(self):
1033
 
        backing = self.get_transport()
1034
 
        request = smart.repository.SmartServerRepositoryStreamRevisionsChunked(
1035
 
            backing)
1036
 
        repo = self.make_repository('.')
1037
 
        rev_id1_utf8 = u'\xc8'.encode('utf-8')
1038
 
        response = request.execute('')
1039
 
        self.assertEqual(None, response)
1040
 
        response = request.do_body("%s\n\n1" % (rev_id1_utf8,))
1041
 
        self.assertEqual(
1042
 
            FailedSmartServerResponse(('NoSuchRevision', )),
1043
 
            response)
1044
 
 
1045
 
 
1046
 
class TestSmartServerIsReadonly(tests.TestCaseWithMemoryTransport):
1047
 
 
1048
 
    def test_is_readonly_no(self):
1049
 
        backing = self.get_transport()
1050
 
        request = smart.request.SmartServerIsReadonly(backing)
1051
 
        response = request.execute()
1052
 
        self.assertEqual(
1053
 
            SmartServerResponse(('no',)), response)
1054
 
 
1055
 
    def test_is_readonly_yes(self):
1056
 
        backing = self.get_readonly_transport()
1057
 
        request = smart.request.SmartServerIsReadonly(backing)
1058
 
        response = request.execute()
1059
 
        self.assertEqual(
1060
 
            SmartServerResponse(('yes',)), response)
1061
 
 
1062
 
 
1063
 
class TestHandlers(tests.TestCase):
1064
 
    """Tests for the request.request_handlers object."""
1065
 
 
1066
 
    def test_registered_methods(self):
1067
 
        """Test that known methods are registered to the correct object."""
1068
 
        self.assertEqual(
1069
 
            smart.request.request_handlers.get('Branch.get_config_file'),
1070
 
            smart.branch.SmartServerBranchGetConfigFile)
1071
 
        self.assertEqual(
1072
 
            smart.request.request_handlers.get('Branch.lock_write'),
1073
 
            smart.branch.SmartServerBranchRequestLockWrite)
1074
 
        self.assertEqual(
1075
 
            smart.request.request_handlers.get('Branch.last_revision_info'),
1076
 
            smart.branch.SmartServerBranchRequestLastRevisionInfo)
1077
 
        self.assertEqual(
1078
 
            smart.request.request_handlers.get('Branch.revision_history'),
1079
 
            smart.branch.SmartServerRequestRevisionHistory)
1080
 
        self.assertEqual(
1081
 
            smart.request.request_handlers.get('Branch.set_last_revision'),
1082
 
            smart.branch.SmartServerBranchRequestSetLastRevision)
1083
 
        self.assertEqual(
1084
 
            smart.request.request_handlers.get('Branch.set_last_revision_info'),
1085
 
            smart.branch.SmartServerBranchRequestSetLastRevisionInfo)
1086
 
        self.assertEqual(
1087
 
            smart.request.request_handlers.get('Branch.unlock'),
1088
 
            smart.branch.SmartServerBranchRequestUnlock)
1089
 
        self.assertEqual(
1090
 
            smart.request.request_handlers.get('BzrDir.find_repository'),
1091
 
            smart.bzrdir.SmartServerRequestFindRepositoryV1)
1092
 
        self.assertEqual(
1093
 
            smart.request.request_handlers.get('BzrDir.find_repositoryV2'),
1094
 
            smart.bzrdir.SmartServerRequestFindRepositoryV2)
1095
 
        self.assertEqual(
1096
 
            smart.request.request_handlers.get('BzrDirFormat.initialize'),
1097
 
            smart.bzrdir.SmartServerRequestInitializeBzrDir)
1098
 
        self.assertEqual(
1099
 
            smart.request.request_handlers.get('BzrDir.open_branch'),
1100
 
            smart.bzrdir.SmartServerRequestOpenBranch)
1101
 
        self.assertEqual(
1102
 
            smart.request.request_handlers.get('Repository.gather_stats'),
1103
 
            smart.repository.SmartServerRepositoryGatherStats)
1104
 
        self.assertEqual(
1105
 
            smart.request.request_handlers.get('Repository.get_parent_map'),
1106
 
            smart.repository.SmartServerRepositoryGetParentMap)
1107
 
        self.assertEqual(
1108
 
            smart.request.request_handlers.get(
1109
 
                'Repository.get_revision_graph'),
1110
 
            smart.repository.SmartServerRepositoryGetRevisionGraph)
1111
 
        self.assertEqual(
1112
 
            smart.request.request_handlers.get('Repository.has_revision'),
1113
 
            smart.repository.SmartServerRequestHasRevision)
1114
 
        self.assertEqual(
1115
 
            smart.request.request_handlers.get('Repository.is_shared'),
1116
 
            smart.repository.SmartServerRepositoryIsShared)
1117
 
        self.assertEqual(
1118
 
            smart.request.request_handlers.get('Repository.lock_write'),
1119
 
            smart.repository.SmartServerRepositoryLockWrite)
1120
 
        self.assertEqual(
1121
 
            smart.request.request_handlers.get('Repository.tarball'),
1122
 
            smart.repository.SmartServerRepositoryTarball)
1123
 
        self.assertEqual(
1124
 
            smart.request.request_handlers.get('Repository.unlock'),
1125
 
            smart.repository.SmartServerRepositoryUnlock)
1126
 
        self.assertEqual(
1127
 
            smart.request.request_handlers.get('Transport.is_readonly'),
1128
 
            smart.request.SmartServerIsReadonly)