/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_smart.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-09-03 01:25:33 UTC
  • mfrom: (4665.2.2 trivial)
  • Revision ID: pqm@pqm.ubuntu.com-20090903012533-qc6kvh5ujgk8042p
(mbp) trivial corrections to progress bar style and developer docs

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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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
    bencode,
 
33
    bzrdir,
 
34
    errors,
 
35
    pack,
 
36
    smart,
 
37
    tests,
 
38
    urlutils,
 
39
    )
 
40
from bzrlib.branch import Branch, BranchReferenceFormat
 
41
import bzrlib.smart.branch
 
42
import bzrlib.smart.bzrdir, bzrlib.smart.bzrdir as smart_dir
 
43
import bzrlib.smart.packrepository
 
44
import bzrlib.smart.repository
 
45
from bzrlib.smart.request import (
 
46
    FailedSmartServerResponse,
 
47
    SmartServerRequest,
 
48
    SmartServerResponse,
 
49
    SuccessfulSmartServerResponse,
 
50
    )
 
51
from bzrlib.tests import (
 
52
    split_suite_by_re,
 
53
    )
 
54
from bzrlib.transport import chroot, get_transport
 
55
 
 
56
 
 
57
def load_tests(standard_tests, module, loader):
 
58
    """Multiply tests version and protocol consistency."""
 
59
    # FindRepository tests.
 
60
    bzrdir_mod = bzrlib.smart.bzrdir
 
61
    scenarios = [
 
62
        ("find_repository", {
 
63
            "_request_class":bzrdir_mod.SmartServerRequestFindRepositoryV1}),
 
64
        ("find_repositoryV2", {
 
65
            "_request_class":bzrdir_mod.SmartServerRequestFindRepositoryV2}),
 
66
        ("find_repositoryV3", {
 
67
            "_request_class":bzrdir_mod.SmartServerRequestFindRepositoryV3}),
 
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
    tests.multiply_tests(v1_and_2, scenarios, result)
 
74
    # The first scenario is only applicable to v1 protocols, it is deleted
 
75
    # since.
 
76
    tests.multiply_tests(v2_only, scenarios[1:], result)
 
77
    return result
 
78
 
 
79
 
 
80
class TestCaseWithChrootedTransport(tests.TestCaseWithTransport):
 
81
 
 
82
    def setUp(self):
 
83
        tests.TestCaseWithTransport.setUp(self)
 
84
        self._chroot_server = None
 
85
 
 
86
    def get_transport(self, relpath=None):
 
87
        if self._chroot_server is None:
 
88
            backing_transport = tests.TestCaseWithTransport.get_transport(self)
 
89
            self._chroot_server = chroot.ChrootServer(backing_transport)
 
90
            self.start_server(self._chroot_server)
 
91
        t = get_transport(self._chroot_server.get_url())
 
92
        if relpath is not None:
 
93
            t = t.clone(relpath)
 
94
        return t
 
95
 
 
96
 
 
97
class TestCaseWithSmartMedium(tests.TestCaseWithTransport):
 
98
 
 
99
    def setUp(self):
 
100
        super(TestCaseWithSmartMedium, self).setUp()
 
101
        # We're allowed to set  the transport class here, so that we don't use
 
102
        # the default or a parameterized class, but rather use the
 
103
        # TestCaseWithTransport infrastructure to set up a smart server and
 
104
        # transport.
 
105
        self.transport_server = self.make_transport_server
 
106
 
 
107
    def make_transport_server(self):
 
108
        return smart.server.SmartTCPServer_for_testing('-' + self.id())
 
109
 
 
110
    def get_smart_medium(self):
 
111
        """Get a smart medium to use in tests."""
 
112
        return self.get_transport().get_smart_medium()
 
113
 
 
114
 
 
115
class TestSmartServerResponse(tests.TestCase):
 
116
 
 
117
    def test__eq__(self):
 
118
        self.assertEqual(SmartServerResponse(('ok', )),
 
119
            SmartServerResponse(('ok', )))
 
120
        self.assertEqual(SmartServerResponse(('ok', ), 'body'),
 
121
            SmartServerResponse(('ok', ), 'body'))
 
122
        self.assertNotEqual(SmartServerResponse(('ok', )),
 
123
            SmartServerResponse(('notok', )))
 
124
        self.assertNotEqual(SmartServerResponse(('ok', ), 'body'),
 
125
            SmartServerResponse(('ok', )))
 
126
        self.assertNotEqual(None,
 
127
            SmartServerResponse(('ok', )))
 
128
 
 
129
    def test__str__(self):
 
130
        """SmartServerResponses can be stringified."""
 
131
        self.assertEqual(
 
132
            "<SuccessfulSmartServerResponse args=('args',) body='body'>",
 
133
            str(SuccessfulSmartServerResponse(('args',), 'body')))
 
134
        self.assertEqual(
 
135
            "<FailedSmartServerResponse args=('args',) body='body'>",
 
136
            str(FailedSmartServerResponse(('args',), 'body')))
 
137
 
 
138
 
 
139
class TestSmartServerRequest(tests.TestCaseWithMemoryTransport):
 
140
 
 
141
    def test_translate_client_path(self):
 
142
        transport = self.get_transport()
 
143
        request = SmartServerRequest(transport, 'foo/')
 
144
        self.assertEqual('./', request.translate_client_path('foo/'))
 
145
        self.assertRaises(
 
146
            errors.InvalidURLJoin, request.translate_client_path, 'foo/..')
 
147
        self.assertRaises(
 
148
            errors.PathNotChild, request.translate_client_path, '/')
 
149
        self.assertRaises(
 
150
            errors.PathNotChild, request.translate_client_path, 'bar/')
 
151
        self.assertEqual('./baz', request.translate_client_path('foo/baz'))
 
152
 
 
153
    def test_transport_from_client_path(self):
 
154
        transport = self.get_transport()
 
155
        request = SmartServerRequest(transport, 'foo/')
 
156
        self.assertEqual(
 
157
            transport.base,
 
158
            request.transport_from_client_path('foo/').base)
 
159
 
 
160
 
 
161
class TestSmartServerBzrDirRequestCloningMetaDir(
 
162
    tests.TestCaseWithMemoryTransport):
 
163
    """Tests for BzrDir.cloning_metadir."""
 
164
 
 
165
    def test_cloning_metadir(self):
 
166
        """When there is a bzrdir present, the call succeeds."""
 
167
        backing = self.get_transport()
 
168
        dir = self.make_bzrdir('.')
 
169
        local_result = dir.cloning_metadir()
 
170
        request_class = smart_dir.SmartServerBzrDirRequestCloningMetaDir
 
171
        request = request_class(backing)
 
172
        expected = SuccessfulSmartServerResponse(
 
173
            (local_result.network_name(),
 
174
            local_result.repository_format.network_name(),
 
175
            ('branch', local_result.get_branch_format().network_name())))
 
176
        self.assertEqual(expected, request.execute('', 'False'))
 
177
 
 
178
    def test_cloning_metadir_reference(self):
 
179
        """The request fails when bzrdir contains a branch reference."""
 
180
        backing = self.get_transport()
 
181
        referenced_branch = self.make_branch('referenced')
 
182
        dir = self.make_bzrdir('.')
 
183
        local_result = dir.cloning_metadir()
 
184
        reference = BranchReferenceFormat().initialize(dir, referenced_branch)
 
185
        reference_url = BranchReferenceFormat().get_reference(dir)
 
186
        # The server shouldn't try to follow the branch reference, so it's fine
 
187
        # if the referenced branch isn't reachable.
 
188
        backing.rename('referenced', 'moved')
 
189
        request_class = smart_dir.SmartServerBzrDirRequestCloningMetaDir
 
190
        request = request_class(backing)
 
191
        expected = FailedSmartServerResponse(('BranchReference',))
 
192
        self.assertEqual(expected, request.execute('', 'False'))
 
193
 
 
194
 
 
195
class TestSmartServerRequestCreateRepository(tests.TestCaseWithMemoryTransport):
 
196
    """Tests for BzrDir.create_repository."""
 
197
 
 
198
    def test_makes_repository(self):
 
199
        """When there is a bzrdir present, the call succeeds."""
 
200
        backing = self.get_transport()
 
201
        self.make_bzrdir('.')
 
202
        request_class = bzrlib.smart.bzrdir.SmartServerRequestCreateRepository
 
203
        request = request_class(backing)
 
204
        reference_bzrdir_format = bzrdir.format_registry.get('pack-0.92')()
 
205
        reference_format = reference_bzrdir_format.repository_format
 
206
        network_name = reference_format.network_name()
 
207
        expected = SuccessfulSmartServerResponse(
 
208
            ('ok', 'no', 'no', 'no', network_name))
 
209
        self.assertEqual(expected, request.execute('', network_name, 'True'))
 
210
 
 
211
 
 
212
class TestSmartServerRequestFindRepository(tests.TestCaseWithMemoryTransport):
 
213
    """Tests for BzrDir.find_repository."""
 
214
 
 
215
    def test_no_repository(self):
 
216
        """When there is no repository to be found, ('norepository', ) is returned."""
 
217
        backing = self.get_transport()
 
218
        request = self._request_class(backing)
 
219
        self.make_bzrdir('.')
 
220
        self.assertEqual(SmartServerResponse(('norepository', )),
 
221
            request.execute(''))
 
222
 
 
223
    def test_nonshared_repository(self):
 
224
        # nonshared repositorys only allow 'find' to return a handle when the
 
225
        # path the repository is being searched on is the same as that that
 
226
        # the repository is at.
 
227
        backing = self.get_transport()
 
228
        request = self._request_class(backing)
 
229
        result = self._make_repository_and_result()
 
230
        self.assertEqual(result, request.execute(''))
 
231
        self.make_bzrdir('subdir')
 
232
        self.assertEqual(SmartServerResponse(('norepository', )),
 
233
            request.execute('subdir'))
 
234
 
 
235
    def _make_repository_and_result(self, shared=False, format=None):
 
236
        """Convenience function to setup a repository.
 
237
 
 
238
        :result: The SmartServerResponse to expect when opening it.
 
239
        """
 
240
        repo = self.make_repository('.', shared=shared, format=format)
 
241
        if repo.supports_rich_root():
 
242
            rich_root = 'yes'
 
243
        else:
 
244
            rich_root = 'no'
 
245
        if repo._format.supports_tree_reference:
 
246
            subtrees = 'yes'
 
247
        else:
 
248
            subtrees = 'no'
 
249
        if repo._format.supports_external_lookups:
 
250
            external = 'yes'
 
251
        else:
 
252
            external = 'no'
 
253
        if (smart.bzrdir.SmartServerRequestFindRepositoryV3 ==
 
254
            self._request_class):
 
255
            return SuccessfulSmartServerResponse(
 
256
                ('ok', '', rich_root, subtrees, external,
 
257
                 repo._format.network_name()))
 
258
        elif (smart.bzrdir.SmartServerRequestFindRepositoryV2 ==
 
259
            self._request_class):
 
260
            # All tests so far are on formats, and for non-external
 
261
            # repositories.
 
262
            return SuccessfulSmartServerResponse(
 
263
                ('ok', '', rich_root, subtrees, external))
 
264
        else:
 
265
            return SuccessfulSmartServerResponse(('ok', '', rich_root, subtrees))
 
266
 
 
267
    def test_shared_repository(self):
 
268
        """When there is a shared repository, we get 'ok', 'relpath-to-repo'."""
 
269
        backing = self.get_transport()
 
270
        request = self._request_class(backing)
 
271
        result = self._make_repository_and_result(shared=True)
 
272
        self.assertEqual(result, request.execute(''))
 
273
        self.make_bzrdir('subdir')
 
274
        result2 = SmartServerResponse(result.args[0:1] + ('..', ) + result.args[2:])
 
275
        self.assertEqual(result2,
 
276
            request.execute('subdir'))
 
277
        self.make_bzrdir('subdir/deeper')
 
278
        result3 = SmartServerResponse(result.args[0:1] + ('../..', ) + result.args[2:])
 
279
        self.assertEqual(result3,
 
280
            request.execute('subdir/deeper'))
 
281
 
 
282
    def test_rich_root_and_subtree_encoding(self):
 
283
        """Test for the format attributes for rich root and subtree support."""
 
284
        backing = self.get_transport()
 
285
        request = self._request_class(backing)
 
286
        result = self._make_repository_and_result(format='dirstate-with-subtree')
 
287
        # check the test will be valid
 
288
        self.assertEqual('yes', result.args[2])
 
289
        self.assertEqual('yes', result.args[3])
 
290
        self.assertEqual(result, request.execute(''))
 
291
 
 
292
    def test_supports_external_lookups_no_v2(self):
 
293
        """Test for the supports_external_lookups attribute."""
 
294
        backing = self.get_transport()
 
295
        request = self._request_class(backing)
 
296
        result = self._make_repository_and_result(format='dirstate-with-subtree')
 
297
        # check the test will be valid
 
298
        self.assertEqual('no', result.args[4])
 
299
        self.assertEqual(result, request.execute(''))
 
300
 
 
301
 
 
302
class TestSmartServerBzrDirRequestGetConfigFile(
 
303
    tests.TestCaseWithMemoryTransport):
 
304
    """Tests for BzrDir.get_config_file."""
 
305
 
 
306
    def test_present(self):
 
307
        backing = self.get_transport()
 
308
        dir = self.make_bzrdir('.')
 
309
        dir.get_config().set_default_stack_on("/")
 
310
        local_result = dir._get_config()._get_config_file().read()
 
311
        request_class = smart_dir.SmartServerBzrDirRequestConfigFile
 
312
        request = request_class(backing)
 
313
        expected = SuccessfulSmartServerResponse((), local_result)
 
314
        self.assertEqual(expected, request.execute(''))
 
315
 
 
316
    def test_missing(self):
 
317
        backing = self.get_transport()
 
318
        dir = self.make_bzrdir('.')
 
319
        request_class = smart_dir.SmartServerBzrDirRequestConfigFile
 
320
        request = request_class(backing)
 
321
        expected = SuccessfulSmartServerResponse((), '')
 
322
        self.assertEqual(expected, request.execute(''))
 
323
 
 
324
 
 
325
class TestSmartServerRequestInitializeBzrDir(tests.TestCaseWithMemoryTransport):
 
326
 
 
327
    def test_empty_dir(self):
 
328
        """Initializing an empty dir should succeed and do it."""
 
329
        backing = self.get_transport()
 
330
        request = smart.bzrdir.SmartServerRequestInitializeBzrDir(backing)
 
331
        self.assertEqual(SmartServerResponse(('ok', )),
 
332
            request.execute(''))
 
333
        made_dir = bzrdir.BzrDir.open_from_transport(backing)
 
334
        # no branch, tree or repository is expected with the current
 
335
        # default formart.
 
336
        self.assertRaises(errors.NoWorkingTree, made_dir.open_workingtree)
 
337
        self.assertRaises(errors.NotBranchError, made_dir.open_branch)
 
338
        self.assertRaises(errors.NoRepositoryPresent, made_dir.open_repository)
 
339
 
 
340
    def test_missing_dir(self):
 
341
        """Initializing a missing directory should fail like the bzrdir api."""
 
342
        backing = self.get_transport()
 
343
        request = smart.bzrdir.SmartServerRequestInitializeBzrDir(backing)
 
344
        self.assertRaises(errors.NoSuchFile,
 
345
            request.execute, 'subdir')
 
346
 
 
347
    def test_initialized_dir(self):
 
348
        """Initializing an extant bzrdir should fail like the bzrdir api."""
 
349
        backing = self.get_transport()
 
350
        request = smart.bzrdir.SmartServerRequestInitializeBzrDir(backing)
 
351
        self.make_bzrdir('subdir')
 
352
        self.assertRaises(errors.FileExists,
 
353
            request.execute, 'subdir')
 
354
 
 
355
 
 
356
class TestSmartServerRequestBzrDirInitializeEx(tests.TestCaseWithMemoryTransport):
 
357
    """Basic tests for BzrDir.initialize_ex_1.16 in the smart server.
 
358
 
 
359
    The main unit tests in test_bzrdir exercise the API comprehensively.
 
360
    """
 
361
 
 
362
    def test_empty_dir(self):
 
363
        """Initializing an empty dir should succeed and do it."""
 
364
        backing = self.get_transport()
 
365
        name = self.make_bzrdir('reference')._format.network_name()
 
366
        request = smart.bzrdir.SmartServerRequestBzrDirInitializeEx(backing)
 
367
        self.assertEqual(SmartServerResponse(('', '', '', '', '', '', name,
 
368
            'False', '', '', '')),
 
369
            request.execute(name, '', 'True', 'False', 'False', '', '', '', '',
 
370
            'False'))
 
371
        made_dir = bzrdir.BzrDir.open_from_transport(backing)
 
372
        # no branch, tree or repository is expected with the current
 
373
        # default format.
 
374
        self.assertRaises(errors.NoWorkingTree, made_dir.open_workingtree)
 
375
        self.assertRaises(errors.NotBranchError, made_dir.open_branch)
 
376
        self.assertRaises(errors.NoRepositoryPresent, made_dir.open_repository)
 
377
 
 
378
    def test_missing_dir(self):
 
379
        """Initializing a missing directory should fail like the bzrdir api."""
 
380
        backing = self.get_transport()
 
381
        name = self.make_bzrdir('reference')._format.network_name()
 
382
        request = smart.bzrdir.SmartServerRequestBzrDirInitializeEx(backing)
 
383
        self.assertRaises(errors.NoSuchFile, request.execute, name,
 
384
            'subdir/dir', 'False', 'False', 'False', '', '', '', '', 'False')
 
385
 
 
386
    def test_initialized_dir(self):
 
387
        """Initializing an extant directory should fail like the bzrdir api."""
 
388
        backing = self.get_transport()
 
389
        name = self.make_bzrdir('reference')._format.network_name()
 
390
        request = smart.bzrdir.SmartServerRequestBzrDirInitializeEx(backing)
 
391
        self.make_bzrdir('subdir')
 
392
        self.assertRaises(errors.FileExists, request.execute, name, 'subdir',
 
393
            'False', 'False', 'False', '', '', '', '', 'False')
 
394
 
 
395
 
 
396
class TestSmartServerRequestOpenBranch(TestCaseWithChrootedTransport):
 
397
 
 
398
    def test_no_branch(self):
 
399
        """When there is no branch, ('nobranch', ) is returned."""
 
400
        backing = self.get_transport()
 
401
        request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
 
402
        self.make_bzrdir('.')
 
403
        self.assertEqual(SmartServerResponse(('nobranch', )),
 
404
            request.execute(''))
 
405
 
 
406
    def test_branch(self):
 
407
        """When there is a branch, 'ok' is returned."""
 
408
        backing = self.get_transport()
 
409
        request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
 
410
        self.make_branch('.')
 
411
        self.assertEqual(SmartServerResponse(('ok', '')),
 
412
            request.execute(''))
 
413
 
 
414
    def test_branch_reference(self):
 
415
        """When there is a branch reference, the reference URL is returned."""
 
416
        backing = self.get_transport()
 
417
        request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
 
418
        branch = self.make_branch('branch')
 
419
        checkout = branch.create_checkout('reference',lightweight=True)
 
420
        reference_url = BranchReferenceFormat().get_reference(checkout.bzrdir)
 
421
        self.assertFileEqual(reference_url, 'reference/.bzr/branch/location')
 
422
        self.assertEqual(SmartServerResponse(('ok', reference_url)),
 
423
            request.execute('reference'))
 
424
 
 
425
 
 
426
class TestSmartServerRequestOpenBranchV2(TestCaseWithChrootedTransport):
 
427
 
 
428
    def test_no_branch(self):
 
429
        """When there is no branch, ('nobranch', ) is returned."""
 
430
        backing = self.get_transport()
 
431
        self.make_bzrdir('.')
 
432
        request = smart.bzrdir.SmartServerRequestOpenBranchV2(backing)
 
433
        self.assertEqual(SmartServerResponse(('nobranch', )),
 
434
            request.execute(''))
 
435
 
 
436
    def test_branch(self):
 
437
        """When there is a branch, 'ok' is returned."""
 
438
        backing = self.get_transport()
 
439
        expected = self.make_branch('.')._format.network_name()
 
440
        request = smart.bzrdir.SmartServerRequestOpenBranchV2(backing)
 
441
        self.assertEqual(SuccessfulSmartServerResponse(('branch', expected)),
 
442
            request.execute(''))
 
443
 
 
444
    def test_branch_reference(self):
 
445
        """When there is a branch reference, the reference URL is returned."""
 
446
        backing = self.get_transport()
 
447
        request = smart.bzrdir.SmartServerRequestOpenBranchV2(backing)
 
448
        branch = self.make_branch('branch')
 
449
        checkout = branch.create_checkout('reference',lightweight=True)
 
450
        reference_url = BranchReferenceFormat().get_reference(checkout.bzrdir)
 
451
        self.assertFileEqual(reference_url, 'reference/.bzr/branch/location')
 
452
        self.assertEqual(SuccessfulSmartServerResponse(('ref', reference_url)),
 
453
            request.execute('reference'))
 
454
 
 
455
    def test_stacked_branch(self):
 
456
        """Opening a stacked branch does not open the stacked-on branch."""
 
457
        trunk = self.make_branch('trunk')
 
458
        feature = self.make_branch('feature')
 
459
        feature.set_stacked_on_url(trunk.base)
 
460
        opened_branches = []
 
461
        Branch.hooks.install_named_hook('open', opened_branches.append, None)
 
462
        backing = self.get_transport()
 
463
        request = smart.bzrdir.SmartServerRequestOpenBranchV2(backing)
 
464
        request.setup_jail()
 
465
        try:
 
466
            response = request.execute('feature')
 
467
        finally:
 
468
            request.teardown_jail()
 
469
        expected_format = feature._format.network_name()
 
470
        self.assertEqual(
 
471
            SuccessfulSmartServerResponse(('branch', expected_format)),
 
472
            response)
 
473
        self.assertLength(1, opened_branches)
 
474
 
 
475
 
 
476
class TestSmartServerRequestRevisionHistory(tests.TestCaseWithMemoryTransport):
 
477
 
 
478
    def test_empty(self):
 
479
        """For an empty branch, the body is empty."""
 
480
        backing = self.get_transport()
 
481
        request = smart.branch.SmartServerRequestRevisionHistory(backing)
 
482
        self.make_branch('.')
 
483
        self.assertEqual(SmartServerResponse(('ok', ), ''),
 
484
            request.execute(''))
 
485
 
 
486
    def test_not_empty(self):
 
487
        """For a non-empty branch, the body is empty."""
 
488
        backing = self.get_transport()
 
489
        request = smart.branch.SmartServerRequestRevisionHistory(backing)
 
490
        tree = self.make_branch_and_memory_tree('.')
 
491
        tree.lock_write()
 
492
        tree.add('')
 
493
        r1 = tree.commit('1st commit')
 
494
        r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
 
495
        tree.unlock()
 
496
        self.assertEqual(
 
497
            SmartServerResponse(('ok', ), ('\x00'.join([r1, r2]))),
 
498
            request.execute(''))
 
499
 
 
500
 
 
501
class TestSmartServerBranchRequest(tests.TestCaseWithMemoryTransport):
 
502
 
 
503
    def test_no_branch(self):
 
504
        """When there is a bzrdir and no branch, NotBranchError is raised."""
 
505
        backing = self.get_transport()
 
506
        request = smart.branch.SmartServerBranchRequest(backing)
 
507
        self.make_bzrdir('.')
 
508
        self.assertRaises(errors.NotBranchError,
 
509
            request.execute, '')
 
510
 
 
511
    def test_branch_reference(self):
 
512
        """When there is a branch reference, NotBranchError is raised."""
 
513
        backing = self.get_transport()
 
514
        request = smart.branch.SmartServerBranchRequest(backing)
 
515
        branch = self.make_branch('branch')
 
516
        checkout = branch.create_checkout('reference',lightweight=True)
 
517
        self.assertRaises(errors.NotBranchError,
 
518
            request.execute, 'checkout')
 
519
 
 
520
 
 
521
class TestSmartServerBranchRequestLastRevisionInfo(tests.TestCaseWithMemoryTransport):
 
522
 
 
523
    def test_empty(self):
 
524
        """For an empty branch, the result is ('ok', '0', 'null:')."""
 
525
        backing = self.get_transport()
 
526
        request = smart.branch.SmartServerBranchRequestLastRevisionInfo(backing)
 
527
        self.make_branch('.')
 
528
        self.assertEqual(SmartServerResponse(('ok', '0', 'null:')),
 
529
            request.execute(''))
 
530
 
 
531
    def test_not_empty(self):
 
532
        """For a non-empty branch, the result is ('ok', 'revno', 'revid')."""
 
533
        backing = self.get_transport()
 
534
        request = smart.branch.SmartServerBranchRequestLastRevisionInfo(backing)
 
535
        tree = self.make_branch_and_memory_tree('.')
 
536
        tree.lock_write()
 
537
        tree.add('')
 
538
        rev_id_utf8 = u'\xc8'.encode('utf-8')
 
539
        r1 = tree.commit('1st commit')
 
540
        r2 = tree.commit('2nd commit', rev_id=rev_id_utf8)
 
541
        tree.unlock()
 
542
        self.assertEqual(
 
543
            SmartServerResponse(('ok', '2', rev_id_utf8)),
 
544
            request.execute(''))
 
545
 
 
546
 
 
547
class TestSmartServerBranchRequestGetConfigFile(tests.TestCaseWithMemoryTransport):
 
548
 
 
549
    def test_default(self):
 
550
        """With no file, we get empty content."""
 
551
        backing = self.get_transport()
 
552
        request = smart.branch.SmartServerBranchGetConfigFile(backing)
 
553
        branch = self.make_branch('.')
 
554
        # there should be no file by default
 
555
        content = ''
 
556
        self.assertEqual(SmartServerResponse(('ok', ), content),
 
557
            request.execute(''))
 
558
 
 
559
    def test_with_content(self):
 
560
        # SmartServerBranchGetConfigFile should return the content from
 
561
        # branch.control_files.get('branch.conf') for now - in the future it may
 
562
        # perform more complex processing.
 
563
        backing = self.get_transport()
 
564
        request = smart.branch.SmartServerBranchGetConfigFile(backing)
 
565
        branch = self.make_branch('.')
 
566
        branch._transport.put_bytes('branch.conf', 'foo bar baz')
 
567
        self.assertEqual(SmartServerResponse(('ok', ), 'foo bar baz'),
 
568
            request.execute(''))
 
569
 
 
570
 
 
571
class TestLockedBranch(tests.TestCaseWithMemoryTransport):
 
572
 
 
573
    def get_lock_tokens(self, branch):
 
574
        branch_token = branch.lock_write()
 
575
        repo_token = branch.repository.lock_write()
 
576
        branch.repository.unlock()
 
577
        return branch_token, repo_token
 
578
 
 
579
 
 
580
class TestSmartServerBranchRequestSetConfigOption(TestLockedBranch):
 
581
 
 
582
    def test_value_name(self):
 
583
        branch = self.make_branch('.')
 
584
        request = smart.branch.SmartServerBranchRequestSetConfigOption(
 
585
            branch.bzrdir.root_transport)
 
586
        branch_token, repo_token = self.get_lock_tokens(branch)
 
587
        config = branch._get_config()
 
588
        result = request.execute('', branch_token, repo_token, 'bar', 'foo',
 
589
            '')
 
590
        self.assertEqual(SuccessfulSmartServerResponse(()), result)
 
591
        self.assertEqual('bar', config.get_option('foo'))
 
592
        # Cleanup
 
593
        branch.unlock()
 
594
 
 
595
    def test_value_name_section(self):
 
596
        branch = self.make_branch('.')
 
597
        request = smart.branch.SmartServerBranchRequestSetConfigOption(
 
598
            branch.bzrdir.root_transport)
 
599
        branch_token, repo_token = self.get_lock_tokens(branch)
 
600
        config = branch._get_config()
 
601
        result = request.execute('', branch_token, repo_token, 'bar', 'foo',
 
602
            'gam')
 
603
        self.assertEqual(SuccessfulSmartServerResponse(()), result)
 
604
        self.assertEqual('bar', config.get_option('foo', 'gam'))
 
605
        # Cleanup
 
606
        branch.unlock()
 
607
 
 
608
 
 
609
class TestSmartServerBranchRequestSetTagsBytes(TestLockedBranch):
 
610
    # Only called when the branch format and tags match [yay factory
 
611
    # methods] so only need to test straight forward cases.
 
612
 
 
613
    def test_set_bytes(self):
 
614
        base_branch = self.make_branch('base')
 
615
        tag_bytes = base_branch._get_tags_bytes()
 
616
        # get_lock_tokens takes out a lock.
 
617
        branch_token, repo_token = self.get_lock_tokens(base_branch)
 
618
        request = smart.branch.SmartServerBranchSetTagsBytes(
 
619
            self.get_transport())
 
620
        response = request.execute('base', branch_token, repo_token)
 
621
        self.assertEqual(None, response)
 
622
        response = request.do_chunk(tag_bytes)
 
623
        self.assertEqual(None, response)
 
624
        response = request.do_end()
 
625
        self.assertEquals(
 
626
            SuccessfulSmartServerResponse(()), response)
 
627
        base_branch.unlock()
 
628
 
 
629
    def test_lock_failed(self):
 
630
        base_branch = self.make_branch('base')
 
631
        base_branch.lock_write()
 
632
        tag_bytes = base_branch._get_tags_bytes()
 
633
        request = smart.branch.SmartServerBranchSetTagsBytes(
 
634
            self.get_transport())
 
635
        self.assertRaises(errors.TokenMismatch, request.execute,
 
636
            'base', 'wrong token', 'wrong token')
 
637
        # The request handler will keep processing the message parts, so even
 
638
        # if the request fails immediately do_chunk and do_end are still
 
639
        # called.
 
640
        request.do_chunk(tag_bytes)
 
641
        request.do_end()
 
642
        base_branch.unlock()
 
643
 
 
644
 
 
645
 
 
646
class SetLastRevisionTestBase(TestLockedBranch):
 
647
    """Base test case for verbs that implement set_last_revision."""
 
648
 
 
649
    def setUp(self):
 
650
        tests.TestCaseWithMemoryTransport.setUp(self)
 
651
        backing_transport = self.get_transport()
 
652
        self.request = self.request_class(backing_transport)
 
653
        self.tree = self.make_branch_and_memory_tree('.')
 
654
 
 
655
    def lock_branch(self):
 
656
        return self.get_lock_tokens(self.tree.branch)
 
657
 
 
658
    def unlock_branch(self):
 
659
        self.tree.branch.unlock()
 
660
 
 
661
    def set_last_revision(self, revision_id, revno):
 
662
        branch_token, repo_token = self.lock_branch()
 
663
        response = self._set_last_revision(
 
664
            revision_id, revno, branch_token, repo_token)
 
665
        self.unlock_branch()
 
666
        return response
 
667
 
 
668
    def assertRequestSucceeds(self, revision_id, revno):
 
669
        response = self.set_last_revision(revision_id, revno)
 
670
        self.assertEqual(SuccessfulSmartServerResponse(('ok',)), response)
 
671
 
 
672
 
 
673
class TestSetLastRevisionVerbMixin(object):
 
674
    """Mixin test case for verbs that implement set_last_revision."""
 
675
 
 
676
    def test_set_null_to_null(self):
 
677
        """An empty branch can have its last revision set to 'null:'."""
 
678
        self.assertRequestSucceeds('null:', 0)
 
679
 
 
680
    def test_NoSuchRevision(self):
 
681
        """If the revision_id is not present, the verb returns NoSuchRevision.
 
682
        """
 
683
        revision_id = 'non-existent revision'
 
684
        self.assertEqual(
 
685
            FailedSmartServerResponse(('NoSuchRevision', revision_id)),
 
686
            self.set_last_revision(revision_id, 1))
 
687
 
 
688
    def make_tree_with_two_commits(self):
 
689
        self.tree.lock_write()
 
690
        self.tree.add('')
 
691
        rev_id_utf8 = u'\xc8'.encode('utf-8')
 
692
        r1 = self.tree.commit('1st commit', rev_id=rev_id_utf8)
 
693
        r2 = self.tree.commit('2nd commit', rev_id='rev-2')
 
694
        self.tree.unlock()
 
695
 
 
696
    def test_branch_last_revision_info_is_updated(self):
 
697
        """A branch's tip can be set to a revision that is present in its
 
698
        repository.
 
699
        """
 
700
        # Make a branch with an empty revision history, but two revisions in
 
701
        # its repository.
 
702
        self.make_tree_with_two_commits()
 
703
        rev_id_utf8 = u'\xc8'.encode('utf-8')
 
704
        self.tree.branch.set_revision_history([])
 
705
        self.assertEqual(
 
706
            (0, 'null:'), self.tree.branch.last_revision_info())
 
707
        # We can update the branch to a revision that is present in the
 
708
        # repository.
 
709
        self.assertRequestSucceeds(rev_id_utf8, 1)
 
710
        self.assertEqual(
 
711
            (1, rev_id_utf8), self.tree.branch.last_revision_info())
 
712
 
 
713
    def test_branch_last_revision_info_rewind(self):
 
714
        """A branch's tip can be set to a revision that is an ancestor of the
 
715
        current tip.
 
716
        """
 
717
        self.make_tree_with_two_commits()
 
718
        rev_id_utf8 = u'\xc8'.encode('utf-8')
 
719
        self.assertEqual(
 
720
            (2, 'rev-2'), self.tree.branch.last_revision_info())
 
721
        self.assertRequestSucceeds(rev_id_utf8, 1)
 
722
        self.assertEqual(
 
723
            (1, rev_id_utf8), self.tree.branch.last_revision_info())
 
724
 
 
725
    def test_TipChangeRejected(self):
 
726
        """If a pre_change_branch_tip hook raises TipChangeRejected, the verb
 
727
        returns TipChangeRejected.
 
728
        """
 
729
        rejection_message = u'rejection message\N{INTERROBANG}'
 
730
        def hook_that_rejects(params):
 
731
            raise errors.TipChangeRejected(rejection_message)
 
732
        Branch.hooks.install_named_hook(
 
733
            'pre_change_branch_tip', hook_that_rejects, None)
 
734
        self.assertEqual(
 
735
            FailedSmartServerResponse(
 
736
                ('TipChangeRejected', rejection_message.encode('utf-8'))),
 
737
            self.set_last_revision('null:', 0))
 
738
 
 
739
 
 
740
class TestSmartServerBranchRequestSetLastRevision(
 
741
        SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
 
742
    """Tests for Branch.set_last_revision verb."""
 
743
 
 
744
    request_class = smart.branch.SmartServerBranchRequestSetLastRevision
 
745
 
 
746
    def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
 
747
        return self.request.execute(
 
748
            '', branch_token, repo_token, revision_id)
 
749
 
 
750
 
 
751
class TestSmartServerBranchRequestSetLastRevisionInfo(
 
752
        SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
 
753
    """Tests for Branch.set_last_revision_info verb."""
 
754
 
 
755
    request_class = smart.branch.SmartServerBranchRequestSetLastRevisionInfo
 
756
 
 
757
    def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
 
758
        return self.request.execute(
 
759
            '', branch_token, repo_token, revno, revision_id)
 
760
 
 
761
    def test_NoSuchRevision(self):
 
762
        """Branch.set_last_revision_info does not have to return
 
763
        NoSuchRevision if the revision_id is absent.
 
764
        """
 
765
        raise tests.TestNotApplicable()
 
766
 
 
767
 
 
768
class TestSmartServerBranchRequestSetLastRevisionEx(
 
769
        SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
 
770
    """Tests for Branch.set_last_revision_ex verb."""
 
771
 
 
772
    request_class = smart.branch.SmartServerBranchRequestSetLastRevisionEx
 
773
 
 
774
    def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
 
775
        return self.request.execute(
 
776
            '', branch_token, repo_token, revision_id, 0, 0)
 
777
 
 
778
    def assertRequestSucceeds(self, revision_id, revno):
 
779
        response = self.set_last_revision(revision_id, revno)
 
780
        self.assertEqual(
 
781
            SuccessfulSmartServerResponse(('ok', revno, revision_id)),
 
782
            response)
 
783
 
 
784
    def test_branch_last_revision_info_rewind(self):
 
785
        """A branch's tip can be set to a revision that is an ancestor of the
 
786
        current tip, but only if allow_overwrite_descendant is passed.
 
787
        """
 
788
        self.make_tree_with_two_commits()
 
789
        rev_id_utf8 = u'\xc8'.encode('utf-8')
 
790
        self.assertEqual(
 
791
            (2, 'rev-2'), self.tree.branch.last_revision_info())
 
792
        # If allow_overwrite_descendant flag is 0, then trying to set the tip
 
793
        # to an older revision ID has no effect.
 
794
        branch_token, repo_token = self.lock_branch()
 
795
        response = self.request.execute(
 
796
            '', branch_token, repo_token, rev_id_utf8, 0, 0)
 
797
        self.assertEqual(
 
798
            SuccessfulSmartServerResponse(('ok', 2, 'rev-2')),
 
799
            response)
 
800
        self.assertEqual(
 
801
            (2, 'rev-2'), self.tree.branch.last_revision_info())
 
802
 
 
803
        # If allow_overwrite_descendant flag is 1, then setting the tip to an
 
804
        # ancestor works.
 
805
        response = self.request.execute(
 
806
            '', branch_token, repo_token, rev_id_utf8, 0, 1)
 
807
        self.assertEqual(
 
808
            SuccessfulSmartServerResponse(('ok', 1, rev_id_utf8)),
 
809
            response)
 
810
        self.unlock_branch()
 
811
        self.assertEqual(
 
812
            (1, rev_id_utf8), self.tree.branch.last_revision_info())
 
813
 
 
814
    def make_branch_with_divergent_history(self):
 
815
        """Make a branch with divergent history in its repo.
 
816
 
 
817
        The branch's tip will be 'child-2', and the repo will also contain
 
818
        'child-1', which diverges from a common base revision.
 
819
        """
 
820
        self.tree.lock_write()
 
821
        self.tree.add('')
 
822
        r1 = self.tree.commit('1st commit')
 
823
        revno_1, revid_1 = self.tree.branch.last_revision_info()
 
824
        r2 = self.tree.commit('2nd commit', rev_id='child-1')
 
825
        # Undo the second commit
 
826
        self.tree.branch.set_last_revision_info(revno_1, revid_1)
 
827
        self.tree.set_parent_ids([revid_1])
 
828
        # Make a new second commit, child-2.  child-2 has diverged from
 
829
        # child-1.
 
830
        new_r2 = self.tree.commit('2nd commit', rev_id='child-2')
 
831
        self.tree.unlock()
 
832
 
 
833
    def test_not_allow_diverged(self):
 
834
        """If allow_diverged is not passed, then setting a divergent history
 
835
        returns a Diverged error.
 
836
        """
 
837
        self.make_branch_with_divergent_history()
 
838
        self.assertEqual(
 
839
            FailedSmartServerResponse(('Diverged',)),
 
840
            self.set_last_revision('child-1', 2))
 
841
        # The branch tip was not changed.
 
842
        self.assertEqual('child-2', self.tree.branch.last_revision())
 
843
 
 
844
    def test_allow_diverged(self):
 
845
        """If allow_diverged is passed, then setting a divergent history
 
846
        succeeds.
 
847
        """
 
848
        self.make_branch_with_divergent_history()
 
849
        branch_token, repo_token = self.lock_branch()
 
850
        response = self.request.execute(
 
851
            '', branch_token, repo_token, 'child-1', 1, 0)
 
852
        self.assertEqual(
 
853
            SuccessfulSmartServerResponse(('ok', 2, 'child-1')),
 
854
            response)
 
855
        self.unlock_branch()
 
856
        # The branch tip was changed.
 
857
        self.assertEqual('child-1', self.tree.branch.last_revision())
 
858
 
 
859
 
 
860
class TestSmartServerBranchRequestGetParent(tests.TestCaseWithMemoryTransport):
 
861
 
 
862
    def test_get_parent_none(self):
 
863
        base_branch = self.make_branch('base')
 
864
        request = smart.branch.SmartServerBranchGetParent(self.get_transport())
 
865
        response = request.execute('base')
 
866
        self.assertEquals(
 
867
            SuccessfulSmartServerResponse(('',)), response)
 
868
 
 
869
    def test_get_parent_something(self):
 
870
        base_branch = self.make_branch('base')
 
871
        base_branch.set_parent(self.get_url('foo'))
 
872
        request = smart.branch.SmartServerBranchGetParent(self.get_transport())
 
873
        response = request.execute('base')
 
874
        self.assertEquals(
 
875
            SuccessfulSmartServerResponse(("../foo",)),
 
876
            response)
 
877
 
 
878
 
 
879
class TestSmartServerBranchRequestSetParent(tests.TestCaseWithMemoryTransport):
 
880
 
 
881
    def test_set_parent_none(self):
 
882
        branch = self.make_branch('base', format="1.9")
 
883
        branch.lock_write()
 
884
        branch._set_parent_location('foo')
 
885
        branch.unlock()
 
886
        request = smart.branch.SmartServerBranchRequestSetParentLocation(
 
887
            self.get_transport())
 
888
        branch_token = branch.lock_write()
 
889
        repo_token = branch.repository.lock_write()
 
890
        try:
 
891
            response = request.execute('base', branch_token, repo_token, '')
 
892
        finally:
 
893
            branch.repository.unlock()
 
894
            branch.unlock()
 
895
        self.assertEqual(SuccessfulSmartServerResponse(()), response)
 
896
        self.assertEqual(None, branch.get_parent())
 
897
 
 
898
    def test_set_parent_something(self):
 
899
        branch = self.make_branch('base', format="1.9")
 
900
        request = smart.branch.SmartServerBranchRequestSetParentLocation(
 
901
            self.get_transport())
 
902
        branch_token = branch.lock_write()
 
903
        repo_token = branch.repository.lock_write()
 
904
        try:
 
905
            response = request.execute('base', branch_token, repo_token,
 
906
            'http://bar/')
 
907
        finally:
 
908
            branch.repository.unlock()
 
909
            branch.unlock()
 
910
        self.assertEqual(SuccessfulSmartServerResponse(()), response)
 
911
        self.assertEqual('http://bar/', branch.get_parent())
 
912
 
 
913
 
 
914
class TestSmartServerBranchRequestGetTagsBytes(tests.TestCaseWithMemoryTransport):
 
915
    # Only called when the branch format and tags match [yay factory
 
916
    # methods] so only need to test straight forward cases.
 
917
 
 
918
    def test_get_bytes(self):
 
919
        base_branch = self.make_branch('base')
 
920
        request = smart.branch.SmartServerBranchGetTagsBytes(
 
921
            self.get_transport())
 
922
        response = request.execute('base')
 
923
        self.assertEquals(
 
924
            SuccessfulSmartServerResponse(('',)), response)
 
925
 
 
926
 
 
927
class TestSmartServerBranchRequestGetStackedOnURL(tests.TestCaseWithMemoryTransport):
 
928
 
 
929
    def test_get_stacked_on_url(self):
 
930
        base_branch = self.make_branch('base', format='1.6')
 
931
        stacked_branch = self.make_branch('stacked', format='1.6')
 
932
        # typically should be relative
 
933
        stacked_branch.set_stacked_on_url('../base')
 
934
        request = smart.branch.SmartServerBranchRequestGetStackedOnURL(
 
935
            self.get_transport())
 
936
        response = request.execute('stacked')
 
937
        self.assertEquals(
 
938
            SmartServerResponse(('ok', '../base')),
 
939
            response)
 
940
 
 
941
 
 
942
class TestSmartServerBranchRequestLockWrite(tests.TestCaseWithMemoryTransport):
 
943
 
 
944
    def setUp(self):
 
945
        tests.TestCaseWithMemoryTransport.setUp(self)
 
946
 
 
947
    def test_lock_write_on_unlocked_branch(self):
 
948
        backing = self.get_transport()
 
949
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
 
950
        branch = self.make_branch('.', format='knit')
 
951
        repository = branch.repository
 
952
        response = request.execute('')
 
953
        branch_nonce = branch.control_files._lock.peek().get('nonce')
 
954
        repository_nonce = repository.control_files._lock.peek().get('nonce')
 
955
        self.assertEqual(
 
956
            SmartServerResponse(('ok', branch_nonce, repository_nonce)),
 
957
            response)
 
958
        # The branch (and associated repository) is now locked.  Verify that
 
959
        # with a new branch object.
 
960
        new_branch = repository.bzrdir.open_branch()
 
961
        self.assertRaises(errors.LockContention, new_branch.lock_write)
 
962
        # Cleanup
 
963
        request = smart.branch.SmartServerBranchRequestUnlock(backing)
 
964
        response = request.execute('', branch_nonce, repository_nonce)
 
965
 
 
966
    def test_lock_write_on_locked_branch(self):
 
967
        backing = self.get_transport()
 
968
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
 
969
        branch = self.make_branch('.')
 
970
        branch_token = branch.lock_write()
 
971
        branch.leave_lock_in_place()
 
972
        branch.unlock()
 
973
        response = request.execute('')
 
974
        self.assertEqual(
 
975
            SmartServerResponse(('LockContention',)), response)
 
976
        # Cleanup
 
977
        branch.lock_write(branch_token)
 
978
        branch.dont_leave_lock_in_place()
 
979
        branch.unlock()
 
980
 
 
981
    def test_lock_write_with_tokens_on_locked_branch(self):
 
982
        backing = self.get_transport()
 
983
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
 
984
        branch = self.make_branch('.', format='knit')
 
985
        branch_token = branch.lock_write()
 
986
        repo_token = branch.repository.lock_write()
 
987
        branch.repository.unlock()
 
988
        branch.leave_lock_in_place()
 
989
        branch.repository.leave_lock_in_place()
 
990
        branch.unlock()
 
991
        response = request.execute('',
 
992
                                   branch_token, repo_token)
 
993
        self.assertEqual(
 
994
            SmartServerResponse(('ok', branch_token, repo_token)), response)
 
995
        # Cleanup
 
996
        branch.repository.lock_write(repo_token)
 
997
        branch.repository.dont_leave_lock_in_place()
 
998
        branch.repository.unlock()
 
999
        branch.lock_write(branch_token)
 
1000
        branch.dont_leave_lock_in_place()
 
1001
        branch.unlock()
 
1002
 
 
1003
    def test_lock_write_with_mismatched_tokens_on_locked_branch(self):
 
1004
        backing = self.get_transport()
 
1005
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
 
1006
        branch = self.make_branch('.', format='knit')
 
1007
        branch_token = branch.lock_write()
 
1008
        repo_token = branch.repository.lock_write()
 
1009
        branch.repository.unlock()
 
1010
        branch.leave_lock_in_place()
 
1011
        branch.repository.leave_lock_in_place()
 
1012
        branch.unlock()
 
1013
        response = request.execute('',
 
1014
                                   branch_token+'xxx', repo_token)
 
1015
        self.assertEqual(
 
1016
            SmartServerResponse(('TokenMismatch',)), response)
 
1017
        # Cleanup
 
1018
        branch.repository.lock_write(repo_token)
 
1019
        branch.repository.dont_leave_lock_in_place()
 
1020
        branch.repository.unlock()
 
1021
        branch.lock_write(branch_token)
 
1022
        branch.dont_leave_lock_in_place()
 
1023
        branch.unlock()
 
1024
 
 
1025
    def test_lock_write_on_locked_repo(self):
 
1026
        backing = self.get_transport()
 
1027
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
 
1028
        branch = self.make_branch('.', format='knit')
 
1029
        repo = branch.repository
 
1030
        repo_token = repo.lock_write()
 
1031
        repo.leave_lock_in_place()
 
1032
        repo.unlock()
 
1033
        response = request.execute('')
 
1034
        self.assertEqual(
 
1035
            SmartServerResponse(('LockContention',)), response)
 
1036
        # Cleanup
 
1037
        repo.lock_write(repo_token)
 
1038
        repo.dont_leave_lock_in_place()
 
1039
        repo.unlock()
 
1040
 
 
1041
    def test_lock_write_on_readonly_transport(self):
 
1042
        backing = self.get_readonly_transport()
 
1043
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
 
1044
        branch = self.make_branch('.')
 
1045
        root = self.get_transport().clone('/')
 
1046
        path = urlutils.relative_url(root.base, self.get_transport().base)
 
1047
        response = request.execute(path)
 
1048
        error_name, lock_str, why_str = response.args
 
1049
        self.assertFalse(response.is_successful())
 
1050
        self.assertEqual('LockFailed', error_name)
 
1051
 
 
1052
 
 
1053
class TestSmartServerBranchRequestUnlock(tests.TestCaseWithMemoryTransport):
 
1054
 
 
1055
    def setUp(self):
 
1056
        tests.TestCaseWithMemoryTransport.setUp(self)
 
1057
 
 
1058
    def test_unlock_on_locked_branch_and_repo(self):
 
1059
        backing = self.get_transport()
 
1060
        request = smart.branch.SmartServerBranchRequestUnlock(backing)
 
1061
        branch = self.make_branch('.', format='knit')
 
1062
        # Lock the branch
 
1063
        branch_token = branch.lock_write()
 
1064
        repo_token = branch.repository.lock_write()
 
1065
        branch.repository.unlock()
 
1066
        # Unlock the branch (and repo) object, leaving the physical locks
 
1067
        # in place.
 
1068
        branch.leave_lock_in_place()
 
1069
        branch.repository.leave_lock_in_place()
 
1070
        branch.unlock()
 
1071
        response = request.execute('',
 
1072
                                   branch_token, repo_token)
 
1073
        self.assertEqual(
 
1074
            SmartServerResponse(('ok',)), response)
 
1075
        # The branch is now unlocked.  Verify that with a new branch
 
1076
        # object.
 
1077
        new_branch = branch.bzrdir.open_branch()
 
1078
        new_branch.lock_write()
 
1079
        new_branch.unlock()
 
1080
 
 
1081
    def test_unlock_on_unlocked_branch_unlocked_repo(self):
 
1082
        backing = self.get_transport()
 
1083
        request = smart.branch.SmartServerBranchRequestUnlock(backing)
 
1084
        branch = self.make_branch('.', format='knit')
 
1085
        response = request.execute(
 
1086
            '', 'branch token', 'repo token')
 
1087
        self.assertEqual(
 
1088
            SmartServerResponse(('TokenMismatch',)), response)
 
1089
 
 
1090
    def test_unlock_on_unlocked_branch_locked_repo(self):
 
1091
        backing = self.get_transport()
 
1092
        request = smart.branch.SmartServerBranchRequestUnlock(backing)
 
1093
        branch = self.make_branch('.', format='knit')
 
1094
        # Lock the repository.
 
1095
        repo_token = branch.repository.lock_write()
 
1096
        branch.repository.leave_lock_in_place()
 
1097
        branch.repository.unlock()
 
1098
        # Issue branch lock_write request on the unlocked branch (with locked
 
1099
        # repo).
 
1100
        response = request.execute(
 
1101
            '', 'branch token', repo_token)
 
1102
        self.assertEqual(
 
1103
            SmartServerResponse(('TokenMismatch',)), response)
 
1104
        # Cleanup
 
1105
        branch.repository.lock_write(repo_token)
 
1106
        branch.repository.dont_leave_lock_in_place()
 
1107
        branch.repository.unlock()
 
1108
 
 
1109
 
 
1110
class TestSmartServerRepositoryRequest(tests.TestCaseWithMemoryTransport):
 
1111
 
 
1112
    def test_no_repository(self):
 
1113
        """Raise NoRepositoryPresent when there is a bzrdir and no repo."""
 
1114
        # we test this using a shared repository above the named path,
 
1115
        # thus checking the right search logic is used - that is, that
 
1116
        # its the exact path being looked at and the server is not
 
1117
        # searching.
 
1118
        backing = self.get_transport()
 
1119
        request = smart.repository.SmartServerRepositoryRequest(backing)
 
1120
        self.make_repository('.', shared=True)
 
1121
        self.make_bzrdir('subdir')
 
1122
        self.assertRaises(errors.NoRepositoryPresent,
 
1123
            request.execute, 'subdir')
 
1124
 
 
1125
 
 
1126
class TestSmartServerRepositoryGetParentMap(tests.TestCaseWithMemoryTransport):
 
1127
 
 
1128
    def test_trivial_bzipped(self):
 
1129
        # This tests that the wire encoding is actually bzipped
 
1130
        backing = self.get_transport()
 
1131
        request = smart.repository.SmartServerRepositoryGetParentMap(backing)
 
1132
        tree = self.make_branch_and_memory_tree('.')
 
1133
 
 
1134
        self.assertEqual(None,
 
1135
            request.execute('', 'missing-id'))
 
1136
        # Note that it returns a body that is bzipped.
 
1137
        self.assertEqual(
 
1138
            SuccessfulSmartServerResponse(('ok', ), bz2.compress('')),
 
1139
            request.do_body('\n\n0\n'))
 
1140
 
 
1141
    def test_trivial_include_missing(self):
 
1142
        backing = self.get_transport()
 
1143
        request = smart.repository.SmartServerRepositoryGetParentMap(backing)
 
1144
        tree = self.make_branch_and_memory_tree('.')
 
1145
 
 
1146
        self.assertEqual(None,
 
1147
            request.execute('', 'missing-id', 'include-missing:'))
 
1148
        self.assertEqual(
 
1149
            SuccessfulSmartServerResponse(('ok', ),
 
1150
                bz2.compress('missing:missing-id')),
 
1151
            request.do_body('\n\n0\n'))
 
1152
 
 
1153
 
 
1154
class TestSmartServerRepositoryGetRevisionGraph(tests.TestCaseWithMemoryTransport):
 
1155
 
 
1156
    def test_none_argument(self):
 
1157
        backing = self.get_transport()
 
1158
        request = smart.repository.SmartServerRepositoryGetRevisionGraph(backing)
 
1159
        tree = self.make_branch_and_memory_tree('.')
 
1160
        tree.lock_write()
 
1161
        tree.add('')
 
1162
        r1 = tree.commit('1st commit')
 
1163
        r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
 
1164
        tree.unlock()
 
1165
 
 
1166
        # the lines of revision_id->revision_parent_list has no guaranteed
 
1167
        # order coming out of a dict, so sort both our test and response
 
1168
        lines = sorted([' '.join([r2, r1]), r1])
 
1169
        response = request.execute('', '')
 
1170
        response.body = '\n'.join(sorted(response.body.split('\n')))
 
1171
 
 
1172
        self.assertEqual(
 
1173
            SmartServerResponse(('ok', ), '\n'.join(lines)), response)
 
1174
 
 
1175
    def test_specific_revision_argument(self):
 
1176
        backing = self.get_transport()
 
1177
        request = smart.repository.SmartServerRepositoryGetRevisionGraph(backing)
 
1178
        tree = self.make_branch_and_memory_tree('.')
 
1179
        tree.lock_write()
 
1180
        tree.add('')
 
1181
        rev_id_utf8 = u'\xc9'.encode('utf-8')
 
1182
        r1 = tree.commit('1st commit', rev_id=rev_id_utf8)
 
1183
        r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
 
1184
        tree.unlock()
 
1185
 
 
1186
        self.assertEqual(SmartServerResponse(('ok', ), rev_id_utf8),
 
1187
            request.execute('', rev_id_utf8))
 
1188
 
 
1189
    def test_no_such_revision(self):
 
1190
        backing = self.get_transport()
 
1191
        request = smart.repository.SmartServerRepositoryGetRevisionGraph(backing)
 
1192
        tree = self.make_branch_and_memory_tree('.')
 
1193
        tree.lock_write()
 
1194
        tree.add('')
 
1195
        r1 = tree.commit('1st commit')
 
1196
        tree.unlock()
 
1197
 
 
1198
        # Note that it still returns body (of zero bytes).
 
1199
        self.assertEqual(
 
1200
            SmartServerResponse(('nosuchrevision', 'missingrevision', ), ''),
 
1201
            request.execute('', 'missingrevision'))
 
1202
 
 
1203
 
 
1204
class TestSmartServerRepositoryGetRevIdForRevno(tests.TestCaseWithMemoryTransport):
 
1205
 
 
1206
    def test_revno_found(self):
 
1207
        backing = self.get_transport()
 
1208
        request = smart.repository.SmartServerRepositoryGetRevIdForRevno(backing)
 
1209
        tree = self.make_branch_and_memory_tree('.')
 
1210
        tree.lock_write()
 
1211
        tree.add('')
 
1212
        rev1_id_utf8 = u'\xc8'.encode('utf-8')
 
1213
        rev2_id_utf8 = u'\xc9'.encode('utf-8')
 
1214
        tree.commit('1st commit', rev_id=rev1_id_utf8)
 
1215
        tree.commit('2nd commit', rev_id=rev2_id_utf8)
 
1216
        tree.unlock()
 
1217
 
 
1218
        self.assertEqual(SmartServerResponse(('ok', rev1_id_utf8)),
 
1219
            request.execute('', 1, (2, rev2_id_utf8)))
 
1220
 
 
1221
    def test_known_revid_missing(self):
 
1222
        backing = self.get_transport()
 
1223
        request = smart.repository.SmartServerRepositoryGetRevIdForRevno(backing)
 
1224
        repo = self.make_repository('.')
 
1225
        self.assertEqual(
 
1226
            FailedSmartServerResponse(('nosuchrevision', 'ghost')),
 
1227
            request.execute('', 1, (2, 'ghost')))
 
1228
 
 
1229
    def test_history_incomplete(self):
 
1230
        backing = self.get_transport()
 
1231
        request = smart.repository.SmartServerRepositoryGetRevIdForRevno(backing)
 
1232
        parent = self.make_branch_and_memory_tree('parent', format='1.9')
 
1233
        parent.lock_write()
 
1234
        parent.add([''], ['TREE_ROOT'])
 
1235
        r1 = parent.commit(message='first commit')
 
1236
        r2 = parent.commit(message='second commit')
 
1237
        parent.unlock()
 
1238
        local = self.make_branch_and_memory_tree('local', format='1.9')
 
1239
        local.branch.pull(parent.branch)
 
1240
        local.set_parent_ids([r2])
 
1241
        r3 = local.commit(message='local commit')
 
1242
        local.branch.create_clone_on_transport(
 
1243
            self.get_transport('stacked'), stacked_on=self.get_url('parent'))
 
1244
        self.assertEqual(
 
1245
            SmartServerResponse(('history-incomplete', 2, r2)),
 
1246
            request.execute('stacked', 1, (3, r3)))
 
1247
 
 
1248
 
 
1249
class TestSmartServerRepositoryGetStream(tests.TestCaseWithMemoryTransport):
 
1250
 
 
1251
    def make_two_commit_repo(self):
 
1252
        tree = self.make_branch_and_memory_tree('.')
 
1253
        tree.lock_write()
 
1254
        tree.add('')
 
1255
        r1 = tree.commit('1st commit')
 
1256
        r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
 
1257
        tree.unlock()
 
1258
        repo = tree.branch.repository
 
1259
        return repo, r1, r2
 
1260
 
 
1261
    def test_ancestry_of(self):
 
1262
        """The search argument may be a 'ancestry-of' some heads'."""
 
1263
        backing = self.get_transport()
 
1264
        request = smart.repository.SmartServerRepositoryGetStream(backing)
 
1265
        repo, r1, r2 = self.make_two_commit_repo()
 
1266
        fetch_spec = ['ancestry-of', r2]
 
1267
        lines = '\n'.join(fetch_spec)
 
1268
        request.execute('', repo._format.network_name())
 
1269
        response = request.do_body(lines)
 
1270
        self.assertEqual(('ok',), response.args)
 
1271
        stream_bytes = ''.join(response.body_stream)
 
1272
        self.assertStartsWith(stream_bytes, 'Bazaar pack format 1')
 
1273
 
 
1274
    def test_search(self):
 
1275
        """The search argument may be a 'search' of some explicit keys."""
 
1276
        backing = self.get_transport()
 
1277
        request = smart.repository.SmartServerRepositoryGetStream(backing)
 
1278
        repo, r1, r2 = self.make_two_commit_repo()
 
1279
        fetch_spec = ['search', '%s %s' % (r1, r2), 'null:', '2']
 
1280
        lines = '\n'.join(fetch_spec)
 
1281
        request.execute('', repo._format.network_name())
 
1282
        response = request.do_body(lines)
 
1283
        self.assertEqual(('ok',), response.args)
 
1284
        stream_bytes = ''.join(response.body_stream)
 
1285
        self.assertStartsWith(stream_bytes, 'Bazaar pack format 1')
 
1286
 
 
1287
 
 
1288
class TestSmartServerRequestHasRevision(tests.TestCaseWithMemoryTransport):
 
1289
 
 
1290
    def test_missing_revision(self):
 
1291
        """For a missing revision, ('no', ) is returned."""
 
1292
        backing = self.get_transport()
 
1293
        request = smart.repository.SmartServerRequestHasRevision(backing)
 
1294
        self.make_repository('.')
 
1295
        self.assertEqual(SmartServerResponse(('no', )),
 
1296
            request.execute('', 'revid'))
 
1297
 
 
1298
    def test_present_revision(self):
 
1299
        """For a present revision, ('yes', ) is returned."""
 
1300
        backing = self.get_transport()
 
1301
        request = smart.repository.SmartServerRequestHasRevision(backing)
 
1302
        tree = self.make_branch_and_memory_tree('.')
 
1303
        tree.lock_write()
 
1304
        tree.add('')
 
1305
        rev_id_utf8 = u'\xc8abc'.encode('utf-8')
 
1306
        r1 = tree.commit('a commit', rev_id=rev_id_utf8)
 
1307
        tree.unlock()
 
1308
        self.assertTrue(tree.branch.repository.has_revision(rev_id_utf8))
 
1309
        self.assertEqual(SmartServerResponse(('yes', )),
 
1310
            request.execute('', rev_id_utf8))
 
1311
 
 
1312
 
 
1313
class TestSmartServerRepositoryGatherStats(tests.TestCaseWithMemoryTransport):
 
1314
 
 
1315
    def test_empty_revid(self):
 
1316
        """With an empty revid, we get only size an number and revisions"""
 
1317
        backing = self.get_transport()
 
1318
        request = smart.repository.SmartServerRepositoryGatherStats(backing)
 
1319
        repository = self.make_repository('.')
 
1320
        stats = repository.gather_stats()
 
1321
        expected_body = 'revisions: 0\n'
 
1322
        self.assertEqual(SmartServerResponse(('ok', ), expected_body),
 
1323
                         request.execute('', '', 'no'))
 
1324
 
 
1325
    def test_revid_with_committers(self):
 
1326
        """For a revid we get more infos."""
 
1327
        backing = self.get_transport()
 
1328
        rev_id_utf8 = u'\xc8abc'.encode('utf-8')
 
1329
        request = smart.repository.SmartServerRepositoryGatherStats(backing)
 
1330
        tree = self.make_branch_and_memory_tree('.')
 
1331
        tree.lock_write()
 
1332
        tree.add('')
 
1333
        # Let's build a predictable result
 
1334
        tree.commit('a commit', timestamp=123456.2, timezone=3600)
 
1335
        tree.commit('a commit', timestamp=654321.4, timezone=0,
 
1336
                    rev_id=rev_id_utf8)
 
1337
        tree.unlock()
 
1338
 
 
1339
        stats = tree.branch.repository.gather_stats()
 
1340
        expected_body = ('firstrev: 123456.200 3600\n'
 
1341
                         'latestrev: 654321.400 0\n'
 
1342
                         'revisions: 2\n')
 
1343
        self.assertEqual(SmartServerResponse(('ok', ), expected_body),
 
1344
                         request.execute('',
 
1345
                                         rev_id_utf8, 'no'))
 
1346
 
 
1347
    def test_not_empty_repository_with_committers(self):
 
1348
        """For a revid and requesting committers we get the whole thing."""
 
1349
        backing = self.get_transport()
 
1350
        rev_id_utf8 = u'\xc8abc'.encode('utf-8')
 
1351
        request = smart.repository.SmartServerRepositoryGatherStats(backing)
 
1352
        tree = self.make_branch_and_memory_tree('.')
 
1353
        tree.lock_write()
 
1354
        tree.add('')
 
1355
        # Let's build a predictable result
 
1356
        tree.commit('a commit', timestamp=123456.2, timezone=3600,
 
1357
                    committer='foo')
 
1358
        tree.commit('a commit', timestamp=654321.4, timezone=0,
 
1359
                    committer='bar', rev_id=rev_id_utf8)
 
1360
        tree.unlock()
 
1361
        stats = tree.branch.repository.gather_stats()
 
1362
 
 
1363
        expected_body = ('committers: 2\n'
 
1364
                         'firstrev: 123456.200 3600\n'
 
1365
                         'latestrev: 654321.400 0\n'
 
1366
                         'revisions: 2\n')
 
1367
        self.assertEqual(SmartServerResponse(('ok', ), expected_body),
 
1368
                         request.execute('',
 
1369
                                         rev_id_utf8, 'yes'))
 
1370
 
 
1371
 
 
1372
class TestSmartServerRepositoryIsShared(tests.TestCaseWithMemoryTransport):
 
1373
 
 
1374
    def test_is_shared(self):
 
1375
        """For a shared repository, ('yes', ) is returned."""
 
1376
        backing = self.get_transport()
 
1377
        request = smart.repository.SmartServerRepositoryIsShared(backing)
 
1378
        self.make_repository('.', shared=True)
 
1379
        self.assertEqual(SmartServerResponse(('yes', )),
 
1380
            request.execute('', ))
 
1381
 
 
1382
    def test_is_not_shared(self):
 
1383
        """For a shared repository, ('no', ) is returned."""
 
1384
        backing = self.get_transport()
 
1385
        request = smart.repository.SmartServerRepositoryIsShared(backing)
 
1386
        self.make_repository('.', shared=False)
 
1387
        self.assertEqual(SmartServerResponse(('no', )),
 
1388
            request.execute('', ))
 
1389
 
 
1390
 
 
1391
class TestSmartServerRepositoryLockWrite(tests.TestCaseWithMemoryTransport):
 
1392
 
 
1393
    def test_lock_write_on_unlocked_repo(self):
 
1394
        backing = self.get_transport()
 
1395
        request = smart.repository.SmartServerRepositoryLockWrite(backing)
 
1396
        repository = self.make_repository('.', format='knit')
 
1397
        response = request.execute('')
 
1398
        nonce = repository.control_files._lock.peek().get('nonce')
 
1399
        self.assertEqual(SmartServerResponse(('ok', nonce)), response)
 
1400
        # The repository is now locked.  Verify that with a new repository
 
1401
        # object.
 
1402
        new_repo = repository.bzrdir.open_repository()
 
1403
        self.assertRaises(errors.LockContention, new_repo.lock_write)
 
1404
        # Cleanup
 
1405
        request = smart.repository.SmartServerRepositoryUnlock(backing)
 
1406
        response = request.execute('', nonce)
 
1407
 
 
1408
    def test_lock_write_on_locked_repo(self):
 
1409
        backing = self.get_transport()
 
1410
        request = smart.repository.SmartServerRepositoryLockWrite(backing)
 
1411
        repository = self.make_repository('.', format='knit')
 
1412
        repo_token = repository.lock_write()
 
1413
        repository.leave_lock_in_place()
 
1414
        repository.unlock()
 
1415
        response = request.execute('')
 
1416
        self.assertEqual(
 
1417
            SmartServerResponse(('LockContention',)), response)
 
1418
        # Cleanup
 
1419
        repository.lock_write(repo_token)
 
1420
        repository.dont_leave_lock_in_place()
 
1421
        repository.unlock()
 
1422
 
 
1423
    def test_lock_write_on_readonly_transport(self):
 
1424
        backing = self.get_readonly_transport()
 
1425
        request = smart.repository.SmartServerRepositoryLockWrite(backing)
 
1426
        repository = self.make_repository('.', format='knit')
 
1427
        response = request.execute('')
 
1428
        self.assertFalse(response.is_successful())
 
1429
        self.assertEqual('LockFailed', response.args[0])
 
1430
 
 
1431
 
 
1432
class TestInsertStreamBase(tests.TestCaseWithMemoryTransport):
 
1433
 
 
1434
    def make_empty_byte_stream(self, repo):
 
1435
        byte_stream = smart.repository._stream_to_byte_stream([], repo._format)
 
1436
        return ''.join(byte_stream)
 
1437
 
 
1438
 
 
1439
class TestSmartServerRepositoryInsertStream(TestInsertStreamBase):
 
1440
 
 
1441
    def test_insert_stream_empty(self):
 
1442
        backing = self.get_transport()
 
1443
        request = smart.repository.SmartServerRepositoryInsertStream(backing)
 
1444
        repository = self.make_repository('.')
 
1445
        response = request.execute('', '')
 
1446
        self.assertEqual(None, response)
 
1447
        response = request.do_chunk(self.make_empty_byte_stream(repository))
 
1448
        self.assertEqual(None, response)
 
1449
        response = request.do_end()
 
1450
        self.assertEqual(SmartServerResponse(('ok', )), response)
 
1451
        
 
1452
 
 
1453
class TestSmartServerRepositoryInsertStreamLocked(TestInsertStreamBase):
 
1454
 
 
1455
    def test_insert_stream_empty(self):
 
1456
        backing = self.get_transport()
 
1457
        request = smart.repository.SmartServerRepositoryInsertStreamLocked(
 
1458
            backing)
 
1459
        repository = self.make_repository('.', format='knit')
 
1460
        lock_token = repository.lock_write()
 
1461
        response = request.execute('', '', lock_token)
 
1462
        self.assertEqual(None, response)
 
1463
        response = request.do_chunk(self.make_empty_byte_stream(repository))
 
1464
        self.assertEqual(None, response)
 
1465
        response = request.do_end()
 
1466
        self.assertEqual(SmartServerResponse(('ok', )), response)
 
1467
        repository.unlock()
 
1468
 
 
1469
    def test_insert_stream_with_wrong_lock_token(self):
 
1470
        backing = self.get_transport()
 
1471
        request = smart.repository.SmartServerRepositoryInsertStreamLocked(
 
1472
            backing)
 
1473
        repository = self.make_repository('.', format='knit')
 
1474
        lock_token = repository.lock_write()
 
1475
        self.assertRaises(
 
1476
            errors.TokenMismatch, request.execute, '', '', 'wrong-token')
 
1477
        repository.unlock()
 
1478
 
 
1479
 
 
1480
class TestSmartServerRepositoryUnlock(tests.TestCaseWithMemoryTransport):
 
1481
 
 
1482
    def setUp(self):
 
1483
        tests.TestCaseWithMemoryTransport.setUp(self)
 
1484
 
 
1485
    def test_unlock_on_locked_repo(self):
 
1486
        backing = self.get_transport()
 
1487
        request = smart.repository.SmartServerRepositoryUnlock(backing)
 
1488
        repository = self.make_repository('.', format='knit')
 
1489
        token = repository.lock_write()
 
1490
        repository.leave_lock_in_place()
 
1491
        repository.unlock()
 
1492
        response = request.execute('', token)
 
1493
        self.assertEqual(
 
1494
            SmartServerResponse(('ok',)), response)
 
1495
        # The repository is now unlocked.  Verify that with a new repository
 
1496
        # object.
 
1497
        new_repo = repository.bzrdir.open_repository()
 
1498
        new_repo.lock_write()
 
1499
        new_repo.unlock()
 
1500
 
 
1501
    def test_unlock_on_unlocked_repo(self):
 
1502
        backing = self.get_transport()
 
1503
        request = smart.repository.SmartServerRepositoryUnlock(backing)
 
1504
        repository = self.make_repository('.', format='knit')
 
1505
        response = request.execute('', 'some token')
 
1506
        self.assertEqual(
 
1507
            SmartServerResponse(('TokenMismatch',)), response)
 
1508
 
 
1509
 
 
1510
class TestSmartServerIsReadonly(tests.TestCaseWithMemoryTransport):
 
1511
 
 
1512
    def test_is_readonly_no(self):
 
1513
        backing = self.get_transport()
 
1514
        request = smart.request.SmartServerIsReadonly(backing)
 
1515
        response = request.execute()
 
1516
        self.assertEqual(
 
1517
            SmartServerResponse(('no',)), response)
 
1518
 
 
1519
    def test_is_readonly_yes(self):
 
1520
        backing = self.get_readonly_transport()
 
1521
        request = smart.request.SmartServerIsReadonly(backing)
 
1522
        response = request.execute()
 
1523
        self.assertEqual(
 
1524
            SmartServerResponse(('yes',)), response)
 
1525
 
 
1526
 
 
1527
class TestSmartServerRepositorySetMakeWorkingTrees(tests.TestCaseWithMemoryTransport):
 
1528
 
 
1529
    def test_set_false(self):
 
1530
        backing = self.get_transport()
 
1531
        repo = self.make_repository('.', shared=True)
 
1532
        repo.set_make_working_trees(True)
 
1533
        request_class = smart.repository.SmartServerRepositorySetMakeWorkingTrees
 
1534
        request = request_class(backing)
 
1535
        self.assertEqual(SuccessfulSmartServerResponse(('ok',)),
 
1536
            request.execute('', 'False'))
 
1537
        repo = repo.bzrdir.open_repository()
 
1538
        self.assertFalse(repo.make_working_trees())
 
1539
 
 
1540
    def test_set_true(self):
 
1541
        backing = self.get_transport()
 
1542
        repo = self.make_repository('.', shared=True)
 
1543
        repo.set_make_working_trees(False)
 
1544
        request_class = smart.repository.SmartServerRepositorySetMakeWorkingTrees
 
1545
        request = request_class(backing)
 
1546
        self.assertEqual(SuccessfulSmartServerResponse(('ok',)),
 
1547
            request.execute('', 'True'))
 
1548
        repo = repo.bzrdir.open_repository()
 
1549
        self.assertTrue(repo.make_working_trees())
 
1550
 
 
1551
 
 
1552
class TestSmartServerPackRepositoryAutopack(tests.TestCaseWithTransport):
 
1553
 
 
1554
    def make_repo_needing_autopacking(self, path='.'):
 
1555
        # Make a repo in need of autopacking.
 
1556
        tree = self.make_branch_and_tree('.', format='pack-0.92')
 
1557
        repo = tree.branch.repository
 
1558
        # monkey-patch the pack collection to disable autopacking
 
1559
        repo._pack_collection._max_pack_count = lambda count: count
 
1560
        for x in range(10):
 
1561
            tree.commit('commit %s' % x)
 
1562
        self.assertEqual(10, len(repo._pack_collection.names()))
 
1563
        del repo._pack_collection._max_pack_count
 
1564
        return repo
 
1565
 
 
1566
    def test_autopack_needed(self):
 
1567
        repo = self.make_repo_needing_autopacking()
 
1568
        repo.lock_write()
 
1569
        self.addCleanup(repo.unlock)
 
1570
        backing = self.get_transport()
 
1571
        request = smart.packrepository.SmartServerPackRepositoryAutopack(
 
1572
            backing)
 
1573
        response = request.execute('')
 
1574
        self.assertEqual(SmartServerResponse(('ok',)), response)
 
1575
        repo._pack_collection.reload_pack_names()
 
1576
        self.assertEqual(1, len(repo._pack_collection.names()))
 
1577
 
 
1578
    def test_autopack_not_needed(self):
 
1579
        tree = self.make_branch_and_tree('.', format='pack-0.92')
 
1580
        repo = tree.branch.repository
 
1581
        repo.lock_write()
 
1582
        self.addCleanup(repo.unlock)
 
1583
        for x in range(9):
 
1584
            tree.commit('commit %s' % x)
 
1585
        backing = self.get_transport()
 
1586
        request = smart.packrepository.SmartServerPackRepositoryAutopack(
 
1587
            backing)
 
1588
        response = request.execute('')
 
1589
        self.assertEqual(SmartServerResponse(('ok',)), response)
 
1590
        repo._pack_collection.reload_pack_names()
 
1591
        self.assertEqual(9, len(repo._pack_collection.names()))
 
1592
 
 
1593
    def test_autopack_on_nonpack_format(self):
 
1594
        """A request to autopack a non-pack repo is a no-op."""
 
1595
        repo = self.make_repository('.', format='knit')
 
1596
        backing = self.get_transport()
 
1597
        request = smart.packrepository.SmartServerPackRepositoryAutopack(
 
1598
            backing)
 
1599
        response = request.execute('')
 
1600
        self.assertEqual(SmartServerResponse(('ok',)), response)
 
1601
 
 
1602
 
 
1603
class TestHandlers(tests.TestCase):
 
1604
    """Tests for the request.request_handlers object."""
 
1605
 
 
1606
    def test_all_registrations_exist(self):
 
1607
        """All registered request_handlers can be found."""
 
1608
        # If there's a typo in a register_lazy call, this loop will fail with
 
1609
        # an AttributeError.
 
1610
        for key, item in smart.request.request_handlers.iteritems():
 
1611
            pass
 
1612
 
 
1613
    def assertHandlerEqual(self, verb, handler):
 
1614
        self.assertEqual(smart.request.request_handlers.get(verb), handler)
 
1615
 
 
1616
    def test_registered_methods(self):
 
1617
        """Test that known methods are registered to the correct object."""
 
1618
        self.assertHandlerEqual('Branch.get_config_file',
 
1619
            smart.branch.SmartServerBranchGetConfigFile)
 
1620
        self.assertHandlerEqual('Branch.get_parent',
 
1621
            smart.branch.SmartServerBranchGetParent)
 
1622
        self.assertHandlerEqual('Branch.get_tags_bytes',
 
1623
            smart.branch.SmartServerBranchGetTagsBytes)
 
1624
        self.assertHandlerEqual('Branch.lock_write',
 
1625
            smart.branch.SmartServerBranchRequestLockWrite)
 
1626
        self.assertHandlerEqual('Branch.last_revision_info',
 
1627
            smart.branch.SmartServerBranchRequestLastRevisionInfo)
 
1628
        self.assertHandlerEqual('Branch.revision_history',
 
1629
            smart.branch.SmartServerRequestRevisionHistory)
 
1630
        self.assertHandlerEqual('Branch.set_config_option',
 
1631
            smart.branch.SmartServerBranchRequestSetConfigOption)
 
1632
        self.assertHandlerEqual('Branch.set_last_revision',
 
1633
            smart.branch.SmartServerBranchRequestSetLastRevision)
 
1634
        self.assertHandlerEqual('Branch.set_last_revision_info',
 
1635
            smart.branch.SmartServerBranchRequestSetLastRevisionInfo)
 
1636
        self.assertHandlerEqual('Branch.set_last_revision_ex',
 
1637
            smart.branch.SmartServerBranchRequestSetLastRevisionEx)
 
1638
        self.assertHandlerEqual('Branch.set_parent_location',
 
1639
            smart.branch.SmartServerBranchRequestSetParentLocation)
 
1640
        self.assertHandlerEqual('Branch.unlock',
 
1641
            smart.branch.SmartServerBranchRequestUnlock)
 
1642
        self.assertHandlerEqual('BzrDir.find_repository',
 
1643
            smart.bzrdir.SmartServerRequestFindRepositoryV1)
 
1644
        self.assertHandlerEqual('BzrDir.find_repositoryV2',
 
1645
            smart.bzrdir.SmartServerRequestFindRepositoryV2)
 
1646
        self.assertHandlerEqual('BzrDirFormat.initialize',
 
1647
            smart.bzrdir.SmartServerRequestInitializeBzrDir)
 
1648
        self.assertHandlerEqual('BzrDirFormat.initialize_ex_1.16',
 
1649
            smart.bzrdir.SmartServerRequestBzrDirInitializeEx)
 
1650
        self.assertHandlerEqual('BzrDir.cloning_metadir',
 
1651
            smart.bzrdir.SmartServerBzrDirRequestCloningMetaDir)
 
1652
        self.assertHandlerEqual('BzrDir.get_config_file',
 
1653
            smart.bzrdir.SmartServerBzrDirRequestConfigFile)
 
1654
        self.assertHandlerEqual('BzrDir.open_branch',
 
1655
            smart.bzrdir.SmartServerRequestOpenBranch)
 
1656
        self.assertHandlerEqual('BzrDir.open_branchV2',
 
1657
            smart.bzrdir.SmartServerRequestOpenBranchV2)
 
1658
        self.assertHandlerEqual('PackRepository.autopack',
 
1659
            smart.packrepository.SmartServerPackRepositoryAutopack)
 
1660
        self.assertHandlerEqual('Repository.gather_stats',
 
1661
            smart.repository.SmartServerRepositoryGatherStats)
 
1662
        self.assertHandlerEqual('Repository.get_parent_map',
 
1663
            smart.repository.SmartServerRepositoryGetParentMap)
 
1664
        self.assertHandlerEqual('Repository.get_rev_id_for_revno',
 
1665
            smart.repository.SmartServerRepositoryGetRevIdForRevno)
 
1666
        self.assertHandlerEqual('Repository.get_revision_graph',
 
1667
            smart.repository.SmartServerRepositoryGetRevisionGraph)
 
1668
        self.assertHandlerEqual('Repository.get_stream',
 
1669
            smart.repository.SmartServerRepositoryGetStream)
 
1670
        self.assertHandlerEqual('Repository.has_revision',
 
1671
            smart.repository.SmartServerRequestHasRevision)
 
1672
        self.assertHandlerEqual('Repository.insert_stream',
 
1673
            smart.repository.SmartServerRepositoryInsertStream)
 
1674
        self.assertHandlerEqual('Repository.insert_stream_locked',
 
1675
            smart.repository.SmartServerRepositoryInsertStreamLocked)
 
1676
        self.assertHandlerEqual('Repository.is_shared',
 
1677
            smart.repository.SmartServerRepositoryIsShared)
 
1678
        self.assertHandlerEqual('Repository.lock_write',
 
1679
            smart.repository.SmartServerRepositoryLockWrite)
 
1680
        self.assertHandlerEqual('Repository.tarball',
 
1681
            smart.repository.SmartServerRepositoryTarball)
 
1682
        self.assertHandlerEqual('Repository.unlock',
 
1683
            smart.repository.SmartServerRepositoryUnlock)
 
1684
        self.assertHandlerEqual('Transport.is_readonly',
 
1685
            smart.request.SmartServerIsReadonly)