~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_smart.py

Fixed as per John's review.

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)