~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_smart.py

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