~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_smart.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2008-04-19 00:23:05 UTC
  • mfrom: (3373.1.1 jam-integration)
  • Revision ID: pqm@pqm.ubuntu.com-20080419002305-25ayhxp3m0b95e9c
(jam) Trivial update to warning message when a user has not run
        lp-login

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