~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_smart.py

Some code cleanup passes.

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, local, memory
 
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
        self.vfs_transport_factory = memory.MemoryServer
 
85
        tests.TestCaseWithTransport.setUp(self)
 
86
        self._chroot_server = None
 
87
 
 
88
    def get_transport(self, relpath=None):
 
89
        if self._chroot_server is None:
 
90
            backing_transport = tests.TestCaseWithTransport.get_transport(self)
 
91
            self._chroot_server = chroot.ChrootServer(backing_transport)
 
92
            self.start_server(self._chroot_server)
 
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.TestCaseWithMemoryTransport):
 
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 TestSmartServerRequestOpenBzrDir(tests.TestCaseWithMemoryTransport):
 
418
    
 
419
    def test_no_directory(self):
 
420
        backing = self.get_transport()
 
421
        request = smart.bzrdir.SmartServerRequestOpenBzrDir(backing)
 
422
        self.assertEqual(SmartServerResponse(('no', )),
 
423
            request.execute('does-not-exist'))
 
424
 
 
425
    def test_empty_directory(self):
 
426
        backing = self.get_transport()
 
427
        backing.mkdir('empty')
 
428
        request = smart.bzrdir.SmartServerRequestOpenBzrDir(backing)
 
429
        self.assertEqual(SmartServerResponse(('no', )),
 
430
            request.execute('empty'))
 
431
 
 
432
    def test_outside_root_client_path(self):
 
433
        backing = self.get_transport()
 
434
        request = smart.bzrdir.SmartServerRequestOpenBzrDir(backing,
 
435
            root_client_path='root')
 
436
        self.assertEqual(SmartServerResponse(('no', )),
 
437
            request.execute('not-root'))
 
438
 
 
439
    
 
440
class TestSmartServerRequestOpenBzrDir_2_1(tests.TestCaseWithMemoryTransport):
 
441
    
 
442
    def test_no_directory(self):
 
443
        backing = self.get_transport()
 
444
        request = smart.bzrdir.SmartServerRequestOpenBzrDir_2_1(backing)
 
445
        self.assertEqual(SmartServerResponse(('no', )),
 
446
            request.execute('does-not-exist'))
 
447
 
 
448
    def test_empty_directory(self):
 
449
        backing = self.get_transport()
 
450
        backing.mkdir('empty')
 
451
        request = smart.bzrdir.SmartServerRequestOpenBzrDir_2_1(backing)
 
452
        self.assertEqual(SmartServerResponse(('no', )),
 
453
            request.execute('empty'))
 
454
 
 
455
    def test_present_without_workingtree(self):
 
456
        backing = self.get_transport()
 
457
        request = smart.bzrdir.SmartServerRequestOpenBzrDir_2_1(backing)
 
458
        self.make_bzrdir('.')
 
459
        self.assertEqual(SmartServerResponse(('yes', 'no')),
 
460
            request.execute(''))
 
461
 
 
462
    def test_outside_root_client_path(self):
 
463
        backing = self.get_transport()
 
464
        request = smart.bzrdir.SmartServerRequestOpenBzrDir_2_1(backing,
 
465
            root_client_path='root')
 
466
        self.assertEqual(SmartServerResponse(('no',)),
 
467
            request.execute('not-root'))
 
468
 
 
469
    
 
470
class TestSmartServerRequestOpenBzrDir_2_1_disk(TestCaseWithChrootedTransport):
 
471
 
 
472
    def test_present_with_workingtree(self):
 
473
        self.vfs_transport_factory = local.LocalURLServer
 
474
        backing = self.get_transport()
 
475
        request = smart.bzrdir.SmartServerRequestOpenBzrDir_2_1(backing)
 
476
        bd = self.make_bzrdir('.')
 
477
        bd.create_repository()
 
478
        bd.create_branch()
 
479
        bd.create_workingtree()
 
480
        self.assertEqual(SmartServerResponse(('yes', 'yes')),
 
481
            request.execute(''))
 
482
 
 
483
 
 
484
class TestSmartServerRequestOpenBranch(TestCaseWithChrootedTransport):
 
485
 
 
486
    def test_no_branch(self):
 
487
        """When there is no branch, ('nobranch', ) is returned."""
 
488
        backing = self.get_transport()
 
489
        request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
 
490
        self.make_bzrdir('.')
 
491
        self.assertEqual(SmartServerResponse(('nobranch', )),
 
492
            request.execute(''))
 
493
 
 
494
    def test_branch(self):
 
495
        """When there is a branch, 'ok' is returned."""
 
496
        backing = self.get_transport()
 
497
        request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
 
498
        self.make_branch('.')
 
499
        self.assertEqual(SmartServerResponse(('ok', '')),
 
500
            request.execute(''))
 
501
 
 
502
    def test_branch_reference(self):
 
503
        """When there is a branch reference, the reference URL is returned."""
 
504
        self.vfs_transport_factory = local.LocalURLServer
 
505
        backing = self.get_transport()
 
506
        request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
 
507
        branch = self.make_branch('branch')
 
508
        checkout = branch.create_checkout('reference',lightweight=True)
 
509
        reference_url = BranchReferenceFormat().get_reference(checkout.bzrdir)
 
510
        self.assertFileEqual(reference_url, 'reference/.bzr/branch/location')
 
511
        self.assertEqual(SmartServerResponse(('ok', reference_url)),
 
512
            request.execute('reference'))
 
513
 
 
514
 
 
515
class TestSmartServerRequestOpenBranchV2(TestCaseWithChrootedTransport):
 
516
 
 
517
    def test_no_branch(self):
 
518
        """When there is no branch, ('nobranch', ) is returned."""
 
519
        backing = self.get_transport()
 
520
        self.make_bzrdir('.')
 
521
        request = smart.bzrdir.SmartServerRequestOpenBranchV2(backing)
 
522
        self.assertEqual(SmartServerResponse(('nobranch', )),
 
523
            request.execute(''))
 
524
 
 
525
    def test_branch(self):
 
526
        """When there is a branch, 'ok' is returned."""
 
527
        backing = self.get_transport()
 
528
        expected = self.make_branch('.')._format.network_name()
 
529
        request = smart.bzrdir.SmartServerRequestOpenBranchV2(backing)
 
530
        self.assertEqual(SuccessfulSmartServerResponse(('branch', expected)),
 
531
            request.execute(''))
 
532
 
 
533
    def test_branch_reference(self):
 
534
        """When there is a branch reference, the reference URL is returned."""
 
535
        self.vfs_transport_factory = local.LocalURLServer
 
536
        backing = self.get_transport()
 
537
        request = smart.bzrdir.SmartServerRequestOpenBranchV2(backing)
 
538
        branch = self.make_branch('branch')
 
539
        checkout = branch.create_checkout('reference',lightweight=True)
 
540
        reference_url = BranchReferenceFormat().get_reference(checkout.bzrdir)
 
541
        self.assertFileEqual(reference_url, 'reference/.bzr/branch/location')
 
542
        self.assertEqual(SuccessfulSmartServerResponse(('ref', reference_url)),
 
543
            request.execute('reference'))
 
544
 
 
545
    def test_stacked_branch(self):
 
546
        """Opening a stacked branch does not open the stacked-on branch."""
 
547
        trunk = self.make_branch('trunk')
 
548
        feature = self.make_branch('feature')
 
549
        feature.set_stacked_on_url(trunk.base)
 
550
        opened_branches = []
 
551
        Branch.hooks.install_named_hook('open', opened_branches.append, None)
 
552
        backing = self.get_transport()
 
553
        request = smart.bzrdir.SmartServerRequestOpenBranchV2(backing)
 
554
        request.setup_jail()
 
555
        try:
 
556
            response = request.execute('feature')
 
557
        finally:
 
558
            request.teardown_jail()
 
559
        expected_format = feature._format.network_name()
 
560
        self.assertEqual(
 
561
            SuccessfulSmartServerResponse(('branch', expected_format)),
 
562
            response)
 
563
        self.assertLength(1, opened_branches)
 
564
 
 
565
 
 
566
class TestSmartServerRequestRevisionHistory(tests.TestCaseWithMemoryTransport):
 
567
 
 
568
    def test_empty(self):
 
569
        """For an empty branch, the body is empty."""
 
570
        backing = self.get_transport()
 
571
        request = smart.branch.SmartServerRequestRevisionHistory(backing)
 
572
        self.make_branch('.')
 
573
        self.assertEqual(SmartServerResponse(('ok', ), ''),
 
574
            request.execute(''))
 
575
 
 
576
    def test_not_empty(self):
 
577
        """For a non-empty branch, the body is empty."""
 
578
        backing = self.get_transport()
 
579
        request = smart.branch.SmartServerRequestRevisionHistory(backing)
 
580
        tree = self.make_branch_and_memory_tree('.')
 
581
        tree.lock_write()
 
582
        tree.add('')
 
583
        r1 = tree.commit('1st commit')
 
584
        r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
 
585
        tree.unlock()
 
586
        self.assertEqual(
 
587
            SmartServerResponse(('ok', ), ('\x00'.join([r1, r2]))),
 
588
            request.execute(''))
 
589
 
 
590
 
 
591
class TestSmartServerBranchRequest(tests.TestCaseWithMemoryTransport):
 
592
 
 
593
    def test_no_branch(self):
 
594
        """When there is a bzrdir and no branch, NotBranchError is raised."""
 
595
        backing = self.get_transport()
 
596
        request = smart.branch.SmartServerBranchRequest(backing)
 
597
        self.make_bzrdir('.')
 
598
        self.assertRaises(errors.NotBranchError,
 
599
            request.execute, '')
 
600
 
 
601
    def test_branch_reference(self):
 
602
        """When there is a branch reference, NotBranchError is raised."""
 
603
        backing = self.get_transport()
 
604
        request = smart.branch.SmartServerBranchRequest(backing)
 
605
        branch = self.make_branch('branch')
 
606
        checkout = branch.create_checkout('reference',lightweight=True)
 
607
        self.assertRaises(errors.NotBranchError,
 
608
            request.execute, 'checkout')
 
609
 
 
610
 
 
611
class TestSmartServerBranchRequestLastRevisionInfo(tests.TestCaseWithMemoryTransport):
 
612
 
 
613
    def test_empty(self):
 
614
        """For an empty branch, the result is ('ok', '0', 'null:')."""
 
615
        backing = self.get_transport()
 
616
        request = smart.branch.SmartServerBranchRequestLastRevisionInfo(backing)
 
617
        self.make_branch('.')
 
618
        self.assertEqual(SmartServerResponse(('ok', '0', 'null:')),
 
619
            request.execute(''))
 
620
 
 
621
    def test_not_empty(self):
 
622
        """For a non-empty branch, the result is ('ok', 'revno', 'revid')."""
 
623
        backing = self.get_transport()
 
624
        request = smart.branch.SmartServerBranchRequestLastRevisionInfo(backing)
 
625
        tree = self.make_branch_and_memory_tree('.')
 
626
        tree.lock_write()
 
627
        tree.add('')
 
628
        rev_id_utf8 = u'\xc8'.encode('utf-8')
 
629
        r1 = tree.commit('1st commit')
 
630
        r2 = tree.commit('2nd commit', rev_id=rev_id_utf8)
 
631
        tree.unlock()
 
632
        self.assertEqual(
 
633
            SmartServerResponse(('ok', '2', rev_id_utf8)),
 
634
            request.execute(''))
 
635
 
 
636
 
 
637
class TestSmartServerBranchRequestGetConfigFile(tests.TestCaseWithMemoryTransport):
 
638
 
 
639
    def test_default(self):
 
640
        """With no file, we get empty content."""
 
641
        backing = self.get_transport()
 
642
        request = smart.branch.SmartServerBranchGetConfigFile(backing)
 
643
        branch = self.make_branch('.')
 
644
        # there should be no file by default
 
645
        content = ''
 
646
        self.assertEqual(SmartServerResponse(('ok', ), content),
 
647
            request.execute(''))
 
648
 
 
649
    def test_with_content(self):
 
650
        # SmartServerBranchGetConfigFile should return the content from
 
651
        # branch.control_files.get('branch.conf') for now - in the future it may
 
652
        # perform more complex processing.
 
653
        backing = self.get_transport()
 
654
        request = smart.branch.SmartServerBranchGetConfigFile(backing)
 
655
        branch = self.make_branch('.')
 
656
        branch._transport.put_bytes('branch.conf', 'foo bar baz')
 
657
        self.assertEqual(SmartServerResponse(('ok', ), 'foo bar baz'),
 
658
            request.execute(''))
 
659
 
 
660
 
 
661
class TestLockedBranch(tests.TestCaseWithMemoryTransport):
 
662
 
 
663
    def get_lock_tokens(self, branch):
 
664
        branch_token = branch.lock_write()
 
665
        repo_token = branch.repository.lock_write()
 
666
        branch.repository.unlock()
 
667
        return branch_token, repo_token
 
668
 
 
669
 
 
670
class TestSmartServerBranchRequestSetConfigOption(TestLockedBranch):
 
671
 
 
672
    def test_value_name(self):
 
673
        branch = self.make_branch('.')
 
674
        request = smart.branch.SmartServerBranchRequestSetConfigOption(
 
675
            branch.bzrdir.root_transport)
 
676
        branch_token, repo_token = self.get_lock_tokens(branch)
 
677
        config = branch._get_config()
 
678
        result = request.execute('', branch_token, repo_token, 'bar', 'foo',
 
679
            '')
 
680
        self.assertEqual(SuccessfulSmartServerResponse(()), result)
 
681
        self.assertEqual('bar', config.get_option('foo'))
 
682
        # Cleanup
 
683
        branch.unlock()
 
684
 
 
685
    def test_value_name_section(self):
 
686
        branch = self.make_branch('.')
 
687
        request = smart.branch.SmartServerBranchRequestSetConfigOption(
 
688
            branch.bzrdir.root_transport)
 
689
        branch_token, repo_token = self.get_lock_tokens(branch)
 
690
        config = branch._get_config()
 
691
        result = request.execute('', branch_token, repo_token, 'bar', 'foo',
 
692
            'gam')
 
693
        self.assertEqual(SuccessfulSmartServerResponse(()), result)
 
694
        self.assertEqual('bar', config.get_option('foo', 'gam'))
 
695
        # Cleanup
 
696
        branch.unlock()
 
697
 
 
698
 
 
699
class TestSmartServerBranchRequestSetTagsBytes(TestLockedBranch):
 
700
    # Only called when the branch format and tags match [yay factory
 
701
    # methods] so only need to test straight forward cases.
 
702
 
 
703
    def test_set_bytes(self):
 
704
        base_branch = self.make_branch('base')
 
705
        tag_bytes = base_branch._get_tags_bytes()
 
706
        # get_lock_tokens takes out a lock.
 
707
        branch_token, repo_token = self.get_lock_tokens(base_branch)
 
708
        request = smart.branch.SmartServerBranchSetTagsBytes(
 
709
            self.get_transport())
 
710
        response = request.execute('base', branch_token, repo_token)
 
711
        self.assertEqual(None, response)
 
712
        response = request.do_chunk(tag_bytes)
 
713
        self.assertEqual(None, response)
 
714
        response = request.do_end()
 
715
        self.assertEquals(
 
716
            SuccessfulSmartServerResponse(()), response)
 
717
        base_branch.unlock()
 
718
 
 
719
    def test_lock_failed(self):
 
720
        base_branch = self.make_branch('base')
 
721
        base_branch.lock_write()
 
722
        tag_bytes = base_branch._get_tags_bytes()
 
723
        request = smart.branch.SmartServerBranchSetTagsBytes(
 
724
            self.get_transport())
 
725
        self.assertRaises(errors.TokenMismatch, request.execute,
 
726
            'base', 'wrong token', 'wrong token')
 
727
        # The request handler will keep processing the message parts, so even
 
728
        # if the request fails immediately do_chunk and do_end are still
 
729
        # called.
 
730
        request.do_chunk(tag_bytes)
 
731
        request.do_end()
 
732
        base_branch.unlock()
 
733
 
 
734
 
 
735
 
 
736
class SetLastRevisionTestBase(TestLockedBranch):
 
737
    """Base test case for verbs that implement set_last_revision."""
 
738
 
 
739
    def setUp(self):
 
740
        tests.TestCaseWithMemoryTransport.setUp(self)
 
741
        backing_transport = self.get_transport()
 
742
        self.request = self.request_class(backing_transport)
 
743
        self.tree = self.make_branch_and_memory_tree('.')
 
744
 
 
745
    def lock_branch(self):
 
746
        return self.get_lock_tokens(self.tree.branch)
 
747
 
 
748
    def unlock_branch(self):
 
749
        self.tree.branch.unlock()
 
750
 
 
751
    def set_last_revision(self, revision_id, revno):
 
752
        branch_token, repo_token = self.lock_branch()
 
753
        response = self._set_last_revision(
 
754
            revision_id, revno, branch_token, repo_token)
 
755
        self.unlock_branch()
 
756
        return response
 
757
 
 
758
    def assertRequestSucceeds(self, revision_id, revno):
 
759
        response = self.set_last_revision(revision_id, revno)
 
760
        self.assertEqual(SuccessfulSmartServerResponse(('ok',)), response)
 
761
 
 
762
 
 
763
class TestSetLastRevisionVerbMixin(object):
 
764
    """Mixin test case for verbs that implement set_last_revision."""
 
765
 
 
766
    def test_set_null_to_null(self):
 
767
        """An empty branch can have its last revision set to 'null:'."""
 
768
        self.assertRequestSucceeds('null:', 0)
 
769
 
 
770
    def test_NoSuchRevision(self):
 
771
        """If the revision_id is not present, the verb returns NoSuchRevision.
 
772
        """
 
773
        revision_id = 'non-existent revision'
 
774
        self.assertEqual(
 
775
            FailedSmartServerResponse(('NoSuchRevision', revision_id)),
 
776
            self.set_last_revision(revision_id, 1))
 
777
 
 
778
    def make_tree_with_two_commits(self):
 
779
        self.tree.lock_write()
 
780
        self.tree.add('')
 
781
        rev_id_utf8 = u'\xc8'.encode('utf-8')
 
782
        r1 = self.tree.commit('1st commit', rev_id=rev_id_utf8)
 
783
        r2 = self.tree.commit('2nd commit', rev_id='rev-2')
 
784
        self.tree.unlock()
 
785
 
 
786
    def test_branch_last_revision_info_is_updated(self):
 
787
        """A branch's tip can be set to a revision that is present in its
 
788
        repository.
 
789
        """
 
790
        # Make a branch with an empty revision history, but two revisions in
 
791
        # its repository.
 
792
        self.make_tree_with_two_commits()
 
793
        rev_id_utf8 = u'\xc8'.encode('utf-8')
 
794
        self.tree.branch.set_revision_history([])
 
795
        self.assertEqual(
 
796
            (0, 'null:'), self.tree.branch.last_revision_info())
 
797
        # We can update the branch to a revision that is present in the
 
798
        # repository.
 
799
        self.assertRequestSucceeds(rev_id_utf8, 1)
 
800
        self.assertEqual(
 
801
            (1, rev_id_utf8), self.tree.branch.last_revision_info())
 
802
 
 
803
    def test_branch_last_revision_info_rewind(self):
 
804
        """A branch's tip can be set to a revision that is an ancestor of the
 
805
        current tip.
 
806
        """
 
807
        self.make_tree_with_two_commits()
 
808
        rev_id_utf8 = u'\xc8'.encode('utf-8')
 
809
        self.assertEqual(
 
810
            (2, 'rev-2'), self.tree.branch.last_revision_info())
 
811
        self.assertRequestSucceeds(rev_id_utf8, 1)
 
812
        self.assertEqual(
 
813
            (1, rev_id_utf8), self.tree.branch.last_revision_info())
 
814
 
 
815
    def test_TipChangeRejected(self):
 
816
        """If a pre_change_branch_tip hook raises TipChangeRejected, the verb
 
817
        returns TipChangeRejected.
 
818
        """
 
819
        rejection_message = u'rejection message\N{INTERROBANG}'
 
820
        def hook_that_rejects(params):
 
821
            raise errors.TipChangeRejected(rejection_message)
 
822
        Branch.hooks.install_named_hook(
 
823
            'pre_change_branch_tip', hook_that_rejects, None)
 
824
        self.assertEqual(
 
825
            FailedSmartServerResponse(
 
826
                ('TipChangeRejected', rejection_message.encode('utf-8'))),
 
827
            self.set_last_revision('null:', 0))
 
828
 
 
829
 
 
830
class TestSmartServerBranchRequestSetLastRevision(
 
831
        SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
 
832
    """Tests for Branch.set_last_revision verb."""
 
833
 
 
834
    request_class = smart.branch.SmartServerBranchRequestSetLastRevision
 
835
 
 
836
    def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
 
837
        return self.request.execute(
 
838
            '', branch_token, repo_token, revision_id)
 
839
 
 
840
 
 
841
class TestSmartServerBranchRequestSetLastRevisionInfo(
 
842
        SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
 
843
    """Tests for Branch.set_last_revision_info verb."""
 
844
 
 
845
    request_class = smart.branch.SmartServerBranchRequestSetLastRevisionInfo
 
846
 
 
847
    def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
 
848
        return self.request.execute(
 
849
            '', branch_token, repo_token, revno, revision_id)
 
850
 
 
851
    def test_NoSuchRevision(self):
 
852
        """Branch.set_last_revision_info does not have to return
 
853
        NoSuchRevision if the revision_id is absent.
 
854
        """
 
855
        raise tests.TestNotApplicable()
 
856
 
 
857
 
 
858
class TestSmartServerBranchRequestSetLastRevisionEx(
 
859
        SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
 
860
    """Tests for Branch.set_last_revision_ex verb."""
 
861
 
 
862
    request_class = smart.branch.SmartServerBranchRequestSetLastRevisionEx
 
863
 
 
864
    def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
 
865
        return self.request.execute(
 
866
            '', branch_token, repo_token, revision_id, 0, 0)
 
867
 
 
868
    def assertRequestSucceeds(self, revision_id, revno):
 
869
        response = self.set_last_revision(revision_id, revno)
 
870
        self.assertEqual(
 
871
            SuccessfulSmartServerResponse(('ok', revno, revision_id)),
 
872
            response)
 
873
 
 
874
    def test_branch_last_revision_info_rewind(self):
 
875
        """A branch's tip can be set to a revision that is an ancestor of the
 
876
        current tip, but only if allow_overwrite_descendant is passed.
 
877
        """
 
878
        self.make_tree_with_two_commits()
 
879
        rev_id_utf8 = u'\xc8'.encode('utf-8')
 
880
        self.assertEqual(
 
881
            (2, 'rev-2'), self.tree.branch.last_revision_info())
 
882
        # If allow_overwrite_descendant flag is 0, then trying to set the tip
 
883
        # to an older revision ID has no effect.
 
884
        branch_token, repo_token = self.lock_branch()
 
885
        response = self.request.execute(
 
886
            '', branch_token, repo_token, rev_id_utf8, 0, 0)
 
887
        self.assertEqual(
 
888
            SuccessfulSmartServerResponse(('ok', 2, 'rev-2')),
 
889
            response)
 
890
        self.assertEqual(
 
891
            (2, 'rev-2'), self.tree.branch.last_revision_info())
 
892
 
 
893
        # If allow_overwrite_descendant flag is 1, then setting the tip to an
 
894
        # ancestor works.
 
895
        response = self.request.execute(
 
896
            '', branch_token, repo_token, rev_id_utf8, 0, 1)
 
897
        self.assertEqual(
 
898
            SuccessfulSmartServerResponse(('ok', 1, rev_id_utf8)),
 
899
            response)
 
900
        self.unlock_branch()
 
901
        self.assertEqual(
 
902
            (1, rev_id_utf8), self.tree.branch.last_revision_info())
 
903
 
 
904
    def make_branch_with_divergent_history(self):
 
905
        """Make a branch with divergent history in its repo.
 
906
 
 
907
        The branch's tip will be 'child-2', and the repo will also contain
 
908
        'child-1', which diverges from a common base revision.
 
909
        """
 
910
        self.tree.lock_write()
 
911
        self.tree.add('')
 
912
        r1 = self.tree.commit('1st commit')
 
913
        revno_1, revid_1 = self.tree.branch.last_revision_info()
 
914
        r2 = self.tree.commit('2nd commit', rev_id='child-1')
 
915
        # Undo the second commit
 
916
        self.tree.branch.set_last_revision_info(revno_1, revid_1)
 
917
        self.tree.set_parent_ids([revid_1])
 
918
        # Make a new second commit, child-2.  child-2 has diverged from
 
919
        # child-1.
 
920
        new_r2 = self.tree.commit('2nd commit', rev_id='child-2')
 
921
        self.tree.unlock()
 
922
 
 
923
    def test_not_allow_diverged(self):
 
924
        """If allow_diverged is not passed, then setting a divergent history
 
925
        returns a Diverged error.
 
926
        """
 
927
        self.make_branch_with_divergent_history()
 
928
        self.assertEqual(
 
929
            FailedSmartServerResponse(('Diverged',)),
 
930
            self.set_last_revision('child-1', 2))
 
931
        # The branch tip was not changed.
 
932
        self.assertEqual('child-2', self.tree.branch.last_revision())
 
933
 
 
934
    def test_allow_diverged(self):
 
935
        """If allow_diverged is passed, then setting a divergent history
 
936
        succeeds.
 
937
        """
 
938
        self.make_branch_with_divergent_history()
 
939
        branch_token, repo_token = self.lock_branch()
 
940
        response = self.request.execute(
 
941
            '', branch_token, repo_token, 'child-1', 1, 0)
 
942
        self.assertEqual(
 
943
            SuccessfulSmartServerResponse(('ok', 2, 'child-1')),
 
944
            response)
 
945
        self.unlock_branch()
 
946
        # The branch tip was changed.
 
947
        self.assertEqual('child-1', self.tree.branch.last_revision())
 
948
 
 
949
 
 
950
class TestSmartServerBranchRequestGetParent(tests.TestCaseWithMemoryTransport):
 
951
 
 
952
    def test_get_parent_none(self):
 
953
        base_branch = self.make_branch('base')
 
954
        request = smart.branch.SmartServerBranchGetParent(self.get_transport())
 
955
        response = request.execute('base')
 
956
        self.assertEquals(
 
957
            SuccessfulSmartServerResponse(('',)), response)
 
958
 
 
959
    def test_get_parent_something(self):
 
960
        base_branch = self.make_branch('base')
 
961
        base_branch.set_parent(self.get_url('foo'))
 
962
        request = smart.branch.SmartServerBranchGetParent(self.get_transport())
 
963
        response = request.execute('base')
 
964
        self.assertEquals(
 
965
            SuccessfulSmartServerResponse(("../foo",)),
 
966
            response)
 
967
 
 
968
 
 
969
class TestSmartServerBranchRequestSetParent(tests.TestCaseWithMemoryTransport):
 
970
 
 
971
    def test_set_parent_none(self):
 
972
        branch = self.make_branch('base', format="1.9")
 
973
        branch.lock_write()
 
974
        branch._set_parent_location('foo')
 
975
        branch.unlock()
 
976
        request = smart.branch.SmartServerBranchRequestSetParentLocation(
 
977
            self.get_transport())
 
978
        branch_token = branch.lock_write()
 
979
        repo_token = branch.repository.lock_write()
 
980
        try:
 
981
            response = request.execute('base', branch_token, repo_token, '')
 
982
        finally:
 
983
            branch.repository.unlock()
 
984
            branch.unlock()
 
985
        self.assertEqual(SuccessfulSmartServerResponse(()), response)
 
986
        self.assertEqual(None, branch.get_parent())
 
987
 
 
988
    def test_set_parent_something(self):
 
989
        branch = self.make_branch('base', format="1.9")
 
990
        request = smart.branch.SmartServerBranchRequestSetParentLocation(
 
991
            self.get_transport())
 
992
        branch_token = branch.lock_write()
 
993
        repo_token = branch.repository.lock_write()
 
994
        try:
 
995
            response = request.execute('base', branch_token, repo_token,
 
996
            'http://bar/')
 
997
        finally:
 
998
            branch.repository.unlock()
 
999
            branch.unlock()
 
1000
        self.assertEqual(SuccessfulSmartServerResponse(()), response)
 
1001
        self.assertEqual('http://bar/', branch.get_parent())
 
1002
 
 
1003
 
 
1004
class TestSmartServerBranchRequestGetTagsBytes(tests.TestCaseWithMemoryTransport):
 
1005
    # Only called when the branch format and tags match [yay factory
 
1006
    # methods] so only need to test straight forward cases.
 
1007
 
 
1008
    def test_get_bytes(self):
 
1009
        base_branch = self.make_branch('base')
 
1010
        request = smart.branch.SmartServerBranchGetTagsBytes(
 
1011
            self.get_transport())
 
1012
        response = request.execute('base')
 
1013
        self.assertEquals(
 
1014
            SuccessfulSmartServerResponse(('',)), response)
 
1015
 
 
1016
 
 
1017
class TestSmartServerBranchRequestGetStackedOnURL(tests.TestCaseWithMemoryTransport):
 
1018
 
 
1019
    def test_get_stacked_on_url(self):
 
1020
        base_branch = self.make_branch('base', format='1.6')
 
1021
        stacked_branch = self.make_branch('stacked', format='1.6')
 
1022
        # typically should be relative
 
1023
        stacked_branch.set_stacked_on_url('../base')
 
1024
        request = smart.branch.SmartServerBranchRequestGetStackedOnURL(
 
1025
            self.get_transport())
 
1026
        response = request.execute('stacked')
 
1027
        self.assertEquals(
 
1028
            SmartServerResponse(('ok', '../base')),
 
1029
            response)
 
1030
 
 
1031
 
 
1032
class TestSmartServerBranchRequestLockWrite(tests.TestCaseWithMemoryTransport):
 
1033
 
 
1034
    def setUp(self):
 
1035
        tests.TestCaseWithMemoryTransport.setUp(self)
 
1036
 
 
1037
    def test_lock_write_on_unlocked_branch(self):
 
1038
        backing = self.get_transport()
 
1039
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
 
1040
        branch = self.make_branch('.', format='knit')
 
1041
        repository = branch.repository
 
1042
        response = request.execute('')
 
1043
        branch_nonce = branch.control_files._lock.peek().get('nonce')
 
1044
        repository_nonce = repository.control_files._lock.peek().get('nonce')
 
1045
        self.assertEqual(
 
1046
            SmartServerResponse(('ok', branch_nonce, repository_nonce)),
 
1047
            response)
 
1048
        # The branch (and associated repository) is now locked.  Verify that
 
1049
        # with a new branch object.
 
1050
        new_branch = repository.bzrdir.open_branch()
 
1051
        self.assertRaises(errors.LockContention, new_branch.lock_write)
 
1052
        # Cleanup
 
1053
        request = smart.branch.SmartServerBranchRequestUnlock(backing)
 
1054
        response = request.execute('', branch_nonce, repository_nonce)
 
1055
 
 
1056
    def test_lock_write_on_locked_branch(self):
 
1057
        backing = self.get_transport()
 
1058
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
 
1059
        branch = self.make_branch('.')
 
1060
        branch_token = branch.lock_write()
 
1061
        branch.leave_lock_in_place()
 
1062
        branch.unlock()
 
1063
        response = request.execute('')
 
1064
        self.assertEqual(
 
1065
            SmartServerResponse(('LockContention',)), response)
 
1066
        # Cleanup
 
1067
        branch.lock_write(branch_token)
 
1068
        branch.dont_leave_lock_in_place()
 
1069
        branch.unlock()
 
1070
 
 
1071
    def test_lock_write_with_tokens_on_locked_branch(self):
 
1072
        backing = self.get_transport()
 
1073
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
 
1074
        branch = self.make_branch('.', format='knit')
 
1075
        branch_token = branch.lock_write()
 
1076
        repo_token = branch.repository.lock_write()
 
1077
        branch.repository.unlock()
 
1078
        branch.leave_lock_in_place()
 
1079
        branch.repository.leave_lock_in_place()
 
1080
        branch.unlock()
 
1081
        response = request.execute('',
 
1082
                                   branch_token, repo_token)
 
1083
        self.assertEqual(
 
1084
            SmartServerResponse(('ok', branch_token, repo_token)), response)
 
1085
        # Cleanup
 
1086
        branch.repository.lock_write(repo_token)
 
1087
        branch.repository.dont_leave_lock_in_place()
 
1088
        branch.repository.unlock()
 
1089
        branch.lock_write(branch_token)
 
1090
        branch.dont_leave_lock_in_place()
 
1091
        branch.unlock()
 
1092
 
 
1093
    def test_lock_write_with_mismatched_tokens_on_locked_branch(self):
 
1094
        backing = self.get_transport()
 
1095
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
 
1096
        branch = self.make_branch('.', format='knit')
 
1097
        branch_token = branch.lock_write()
 
1098
        repo_token = branch.repository.lock_write()
 
1099
        branch.repository.unlock()
 
1100
        branch.leave_lock_in_place()
 
1101
        branch.repository.leave_lock_in_place()
 
1102
        branch.unlock()
 
1103
        response = request.execute('',
 
1104
                                   branch_token+'xxx', repo_token)
 
1105
        self.assertEqual(
 
1106
            SmartServerResponse(('TokenMismatch',)), response)
 
1107
        # Cleanup
 
1108
        branch.repository.lock_write(repo_token)
 
1109
        branch.repository.dont_leave_lock_in_place()
 
1110
        branch.repository.unlock()
 
1111
        branch.lock_write(branch_token)
 
1112
        branch.dont_leave_lock_in_place()
 
1113
        branch.unlock()
 
1114
 
 
1115
    def test_lock_write_on_locked_repo(self):
 
1116
        backing = self.get_transport()
 
1117
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
 
1118
        branch = self.make_branch('.', format='knit')
 
1119
        repo = branch.repository
 
1120
        repo_token = repo.lock_write()
 
1121
        repo.leave_lock_in_place()
 
1122
        repo.unlock()
 
1123
        response = request.execute('')
 
1124
        self.assertEqual(
 
1125
            SmartServerResponse(('LockContention',)), response)
 
1126
        # Cleanup
 
1127
        repo.lock_write(repo_token)
 
1128
        repo.dont_leave_lock_in_place()
 
1129
        repo.unlock()
 
1130
 
 
1131
    def test_lock_write_on_readonly_transport(self):
 
1132
        backing = self.get_readonly_transport()
 
1133
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
 
1134
        branch = self.make_branch('.')
 
1135
        root = self.get_transport().clone('/')
 
1136
        path = urlutils.relative_url(root.base, self.get_transport().base)
 
1137
        response = request.execute(path)
 
1138
        error_name, lock_str, why_str = response.args
 
1139
        self.assertFalse(response.is_successful())
 
1140
        self.assertEqual('LockFailed', error_name)
 
1141
 
 
1142
 
 
1143
class TestSmartServerBranchRequestUnlock(tests.TestCaseWithMemoryTransport):
 
1144
 
 
1145
    def setUp(self):
 
1146
        tests.TestCaseWithMemoryTransport.setUp(self)
 
1147
 
 
1148
    def test_unlock_on_locked_branch_and_repo(self):
 
1149
        backing = self.get_transport()
 
1150
        request = smart.branch.SmartServerBranchRequestUnlock(backing)
 
1151
        branch = self.make_branch('.', format='knit')
 
1152
        # Lock the branch
 
1153
        branch_token = branch.lock_write()
 
1154
        repo_token = branch.repository.lock_write()
 
1155
        branch.repository.unlock()
 
1156
        # Unlock the branch (and repo) object, leaving the physical locks
 
1157
        # in place.
 
1158
        branch.leave_lock_in_place()
 
1159
        branch.repository.leave_lock_in_place()
 
1160
        branch.unlock()
 
1161
        response = request.execute('',
 
1162
                                   branch_token, repo_token)
 
1163
        self.assertEqual(
 
1164
            SmartServerResponse(('ok',)), response)
 
1165
        # The branch is now unlocked.  Verify that with a new branch
 
1166
        # object.
 
1167
        new_branch = branch.bzrdir.open_branch()
 
1168
        new_branch.lock_write()
 
1169
        new_branch.unlock()
 
1170
 
 
1171
    def test_unlock_on_unlocked_branch_unlocked_repo(self):
 
1172
        backing = self.get_transport()
 
1173
        request = smart.branch.SmartServerBranchRequestUnlock(backing)
 
1174
        branch = self.make_branch('.', format='knit')
 
1175
        response = request.execute(
 
1176
            '', 'branch token', 'repo token')
 
1177
        self.assertEqual(
 
1178
            SmartServerResponse(('TokenMismatch',)), response)
 
1179
 
 
1180
    def test_unlock_on_unlocked_branch_locked_repo(self):
 
1181
        backing = self.get_transport()
 
1182
        request = smart.branch.SmartServerBranchRequestUnlock(backing)
 
1183
        branch = self.make_branch('.', format='knit')
 
1184
        # Lock the repository.
 
1185
        repo_token = branch.repository.lock_write()
 
1186
        branch.repository.leave_lock_in_place()
 
1187
        branch.repository.unlock()
 
1188
        # Issue branch lock_write request on the unlocked branch (with locked
 
1189
        # repo).
 
1190
        response = request.execute(
 
1191
            '', 'branch token', repo_token)
 
1192
        self.assertEqual(
 
1193
            SmartServerResponse(('TokenMismatch',)), response)
 
1194
        # Cleanup
 
1195
        branch.repository.lock_write(repo_token)
 
1196
        branch.repository.dont_leave_lock_in_place()
 
1197
        branch.repository.unlock()
 
1198
 
 
1199
 
 
1200
class TestSmartServerRepositoryRequest(tests.TestCaseWithMemoryTransport):
 
1201
 
 
1202
    def test_no_repository(self):
 
1203
        """Raise NoRepositoryPresent when there is a bzrdir and no repo."""
 
1204
        # we test this using a shared repository above the named path,
 
1205
        # thus checking the right search logic is used - that is, that
 
1206
        # its the exact path being looked at and the server is not
 
1207
        # searching.
 
1208
        backing = self.get_transport()
 
1209
        request = smart.repository.SmartServerRepositoryRequest(backing)
 
1210
        self.make_repository('.', shared=True)
 
1211
        self.make_bzrdir('subdir')
 
1212
        self.assertRaises(errors.NoRepositoryPresent,
 
1213
            request.execute, 'subdir')
 
1214
 
 
1215
 
 
1216
class TestSmartServerRepositoryGetParentMap(tests.TestCaseWithMemoryTransport):
 
1217
 
 
1218
    def test_trivial_bzipped(self):
 
1219
        # This tests that the wire encoding is actually bzipped
 
1220
        backing = self.get_transport()
 
1221
        request = smart.repository.SmartServerRepositoryGetParentMap(backing)
 
1222
        tree = self.make_branch_and_memory_tree('.')
 
1223
 
 
1224
        self.assertEqual(None,
 
1225
            request.execute('', 'missing-id'))
 
1226
        # Note that it returns a body that is bzipped.
 
1227
        self.assertEqual(
 
1228
            SuccessfulSmartServerResponse(('ok', ), bz2.compress('')),
 
1229
            request.do_body('\n\n0\n'))
 
1230
 
 
1231
    def test_trivial_include_missing(self):
 
1232
        backing = self.get_transport()
 
1233
        request = smart.repository.SmartServerRepositoryGetParentMap(backing)
 
1234
        tree = self.make_branch_and_memory_tree('.')
 
1235
 
 
1236
        self.assertEqual(None,
 
1237
            request.execute('', 'missing-id', 'include-missing:'))
 
1238
        self.assertEqual(
 
1239
            SuccessfulSmartServerResponse(('ok', ),
 
1240
                bz2.compress('missing:missing-id')),
 
1241
            request.do_body('\n\n0\n'))
 
1242
 
 
1243
 
 
1244
class TestSmartServerRepositoryGetRevisionGraph(tests.TestCaseWithMemoryTransport):
 
1245
 
 
1246
    def test_none_argument(self):
 
1247
        backing = self.get_transport()
 
1248
        request = smart.repository.SmartServerRepositoryGetRevisionGraph(backing)
 
1249
        tree = self.make_branch_and_memory_tree('.')
 
1250
        tree.lock_write()
 
1251
        tree.add('')
 
1252
        r1 = tree.commit('1st commit')
 
1253
        r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
 
1254
        tree.unlock()
 
1255
 
 
1256
        # the lines of revision_id->revision_parent_list has no guaranteed
 
1257
        # order coming out of a dict, so sort both our test and response
 
1258
        lines = sorted([' '.join([r2, r1]), r1])
 
1259
        response = request.execute('', '')
 
1260
        response.body = '\n'.join(sorted(response.body.split('\n')))
 
1261
 
 
1262
        self.assertEqual(
 
1263
            SmartServerResponse(('ok', ), '\n'.join(lines)), response)
 
1264
 
 
1265
    def test_specific_revision_argument(self):
 
1266
        backing = self.get_transport()
 
1267
        request = smart.repository.SmartServerRepositoryGetRevisionGraph(backing)
 
1268
        tree = self.make_branch_and_memory_tree('.')
 
1269
        tree.lock_write()
 
1270
        tree.add('')
 
1271
        rev_id_utf8 = u'\xc9'.encode('utf-8')
 
1272
        r1 = tree.commit('1st commit', rev_id=rev_id_utf8)
 
1273
        r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
 
1274
        tree.unlock()
 
1275
 
 
1276
        self.assertEqual(SmartServerResponse(('ok', ), rev_id_utf8),
 
1277
            request.execute('', rev_id_utf8))
 
1278
 
 
1279
    def test_no_such_revision(self):
 
1280
        backing = self.get_transport()
 
1281
        request = smart.repository.SmartServerRepositoryGetRevisionGraph(backing)
 
1282
        tree = self.make_branch_and_memory_tree('.')
 
1283
        tree.lock_write()
 
1284
        tree.add('')
 
1285
        r1 = tree.commit('1st commit')
 
1286
        tree.unlock()
 
1287
 
 
1288
        # Note that it still returns body (of zero bytes).
 
1289
        self.assertEqual(
 
1290
            SmartServerResponse(('nosuchrevision', 'missingrevision', ), ''),
 
1291
            request.execute('', 'missingrevision'))
 
1292
 
 
1293
 
 
1294
class TestSmartServerRepositoryGetRevIdForRevno(tests.TestCaseWithMemoryTransport):
 
1295
 
 
1296
    def test_revno_found(self):
 
1297
        backing = self.get_transport()
 
1298
        request = smart.repository.SmartServerRepositoryGetRevIdForRevno(backing)
 
1299
        tree = self.make_branch_and_memory_tree('.')
 
1300
        tree.lock_write()
 
1301
        tree.add('')
 
1302
        rev1_id_utf8 = u'\xc8'.encode('utf-8')
 
1303
        rev2_id_utf8 = u'\xc9'.encode('utf-8')
 
1304
        tree.commit('1st commit', rev_id=rev1_id_utf8)
 
1305
        tree.commit('2nd commit', rev_id=rev2_id_utf8)
 
1306
        tree.unlock()
 
1307
 
 
1308
        self.assertEqual(SmartServerResponse(('ok', rev1_id_utf8)),
 
1309
            request.execute('', 1, (2, rev2_id_utf8)))
 
1310
 
 
1311
    def test_known_revid_missing(self):
 
1312
        backing = self.get_transport()
 
1313
        request = smart.repository.SmartServerRepositoryGetRevIdForRevno(backing)
 
1314
        repo = self.make_repository('.')
 
1315
        self.assertEqual(
 
1316
            FailedSmartServerResponse(('nosuchrevision', 'ghost')),
 
1317
            request.execute('', 1, (2, 'ghost')))
 
1318
 
 
1319
    def test_history_incomplete(self):
 
1320
        backing = self.get_transport()
 
1321
        request = smart.repository.SmartServerRepositoryGetRevIdForRevno(backing)
 
1322
        parent = self.make_branch_and_memory_tree('parent', format='1.9')
 
1323
        parent.lock_write()
 
1324
        parent.add([''], ['TREE_ROOT'])
 
1325
        r1 = parent.commit(message='first commit')
 
1326
        r2 = parent.commit(message='second commit')
 
1327
        parent.unlock()
 
1328
        local = self.make_branch_and_memory_tree('local', format='1.9')
 
1329
        local.branch.pull(parent.branch)
 
1330
        local.set_parent_ids([r2])
 
1331
        r3 = local.commit(message='local commit')
 
1332
        local.branch.create_clone_on_transport(
 
1333
            self.get_transport('stacked'), stacked_on=self.get_url('parent'))
 
1334
        self.assertEqual(
 
1335
            SmartServerResponse(('history-incomplete', 2, r2)),
 
1336
            request.execute('stacked', 1, (3, r3)))
 
1337
 
 
1338
 
 
1339
class TestSmartServerRepositoryGetStream(tests.TestCaseWithMemoryTransport):
 
1340
 
 
1341
    def make_two_commit_repo(self):
 
1342
        tree = self.make_branch_and_memory_tree('.')
 
1343
        tree.lock_write()
 
1344
        tree.add('')
 
1345
        r1 = tree.commit('1st commit')
 
1346
        r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
 
1347
        tree.unlock()
 
1348
        repo = tree.branch.repository
 
1349
        return repo, r1, r2
 
1350
 
 
1351
    def test_ancestry_of(self):
 
1352
        """The search argument may be a 'ancestry-of' some heads'."""
 
1353
        backing = self.get_transport()
 
1354
        request = smart.repository.SmartServerRepositoryGetStream(backing)
 
1355
        repo, r1, r2 = self.make_two_commit_repo()
 
1356
        fetch_spec = ['ancestry-of', r2]
 
1357
        lines = '\n'.join(fetch_spec)
 
1358
        request.execute('', repo._format.network_name())
 
1359
        response = request.do_body(lines)
 
1360
        self.assertEqual(('ok',), response.args)
 
1361
        stream_bytes = ''.join(response.body_stream)
 
1362
        self.assertStartsWith(stream_bytes, 'Bazaar pack format 1')
 
1363
 
 
1364
    def test_search(self):
 
1365
        """The search argument may be a 'search' of some explicit keys."""
 
1366
        backing = self.get_transport()
 
1367
        request = smart.repository.SmartServerRepositoryGetStream(backing)
 
1368
        repo, r1, r2 = self.make_two_commit_repo()
 
1369
        fetch_spec = ['search', '%s %s' % (r1, r2), 'null:', '2']
 
1370
        lines = '\n'.join(fetch_spec)
 
1371
        request.execute('', repo._format.network_name())
 
1372
        response = request.do_body(lines)
 
1373
        self.assertEqual(('ok',), response.args)
 
1374
        stream_bytes = ''.join(response.body_stream)
 
1375
        self.assertStartsWith(stream_bytes, 'Bazaar pack format 1')
 
1376
 
 
1377
 
 
1378
class TestSmartServerRequestHasRevision(tests.TestCaseWithMemoryTransport):
 
1379
 
 
1380
    def test_missing_revision(self):
 
1381
        """For a missing revision, ('no', ) is returned."""
 
1382
        backing = self.get_transport()
 
1383
        request = smart.repository.SmartServerRequestHasRevision(backing)
 
1384
        self.make_repository('.')
 
1385
        self.assertEqual(SmartServerResponse(('no', )),
 
1386
            request.execute('', 'revid'))
 
1387
 
 
1388
    def test_present_revision(self):
 
1389
        """For a present revision, ('yes', ) is returned."""
 
1390
        backing = self.get_transport()
 
1391
        request = smart.repository.SmartServerRequestHasRevision(backing)
 
1392
        tree = self.make_branch_and_memory_tree('.')
 
1393
        tree.lock_write()
 
1394
        tree.add('')
 
1395
        rev_id_utf8 = u'\xc8abc'.encode('utf-8')
 
1396
        r1 = tree.commit('a commit', rev_id=rev_id_utf8)
 
1397
        tree.unlock()
 
1398
        self.assertTrue(tree.branch.repository.has_revision(rev_id_utf8))
 
1399
        self.assertEqual(SmartServerResponse(('yes', )),
 
1400
            request.execute('', rev_id_utf8))
 
1401
 
 
1402
 
 
1403
class TestSmartServerRepositoryGatherStats(tests.TestCaseWithMemoryTransport):
 
1404
 
 
1405
    def test_empty_revid(self):
 
1406
        """With an empty revid, we get only size an number and revisions"""
 
1407
        backing = self.get_transport()
 
1408
        request = smart.repository.SmartServerRepositoryGatherStats(backing)
 
1409
        repository = self.make_repository('.')
 
1410
        stats = repository.gather_stats()
 
1411
        expected_body = 'revisions: 0\n'
 
1412
        self.assertEqual(SmartServerResponse(('ok', ), expected_body),
 
1413
                         request.execute('', '', 'no'))
 
1414
 
 
1415
    def test_revid_with_committers(self):
 
1416
        """For a revid we get more infos."""
 
1417
        backing = self.get_transport()
 
1418
        rev_id_utf8 = u'\xc8abc'.encode('utf-8')
 
1419
        request = smart.repository.SmartServerRepositoryGatherStats(backing)
 
1420
        tree = self.make_branch_and_memory_tree('.')
 
1421
        tree.lock_write()
 
1422
        tree.add('')
 
1423
        # Let's build a predictable result
 
1424
        tree.commit('a commit', timestamp=123456.2, timezone=3600)
 
1425
        tree.commit('a commit', timestamp=654321.4, timezone=0,
 
1426
                    rev_id=rev_id_utf8)
 
1427
        tree.unlock()
 
1428
 
 
1429
        stats = tree.branch.repository.gather_stats()
 
1430
        expected_body = ('firstrev: 123456.200 3600\n'
 
1431
                         'latestrev: 654321.400 0\n'
 
1432
                         'revisions: 2\n')
 
1433
        self.assertEqual(SmartServerResponse(('ok', ), expected_body),
 
1434
                         request.execute('',
 
1435
                                         rev_id_utf8, 'no'))
 
1436
 
 
1437
    def test_not_empty_repository_with_committers(self):
 
1438
        """For a revid and requesting committers we get the whole thing."""
 
1439
        backing = self.get_transport()
 
1440
        rev_id_utf8 = u'\xc8abc'.encode('utf-8')
 
1441
        request = smart.repository.SmartServerRepositoryGatherStats(backing)
 
1442
        tree = self.make_branch_and_memory_tree('.')
 
1443
        tree.lock_write()
 
1444
        tree.add('')
 
1445
        # Let's build a predictable result
 
1446
        tree.commit('a commit', timestamp=123456.2, timezone=3600,
 
1447
                    committer='foo')
 
1448
        tree.commit('a commit', timestamp=654321.4, timezone=0,
 
1449
                    committer='bar', rev_id=rev_id_utf8)
 
1450
        tree.unlock()
 
1451
        stats = tree.branch.repository.gather_stats()
 
1452
 
 
1453
        expected_body = ('committers: 2\n'
 
1454
                         'firstrev: 123456.200 3600\n'
 
1455
                         'latestrev: 654321.400 0\n'
 
1456
                         'revisions: 2\n')
 
1457
        self.assertEqual(SmartServerResponse(('ok', ), expected_body),
 
1458
                         request.execute('',
 
1459
                                         rev_id_utf8, 'yes'))
 
1460
 
 
1461
 
 
1462
class TestSmartServerRepositoryIsShared(tests.TestCaseWithMemoryTransport):
 
1463
 
 
1464
    def test_is_shared(self):
 
1465
        """For a shared repository, ('yes', ) is returned."""
 
1466
        backing = self.get_transport()
 
1467
        request = smart.repository.SmartServerRepositoryIsShared(backing)
 
1468
        self.make_repository('.', shared=True)
 
1469
        self.assertEqual(SmartServerResponse(('yes', )),
 
1470
            request.execute('', ))
 
1471
 
 
1472
    def test_is_not_shared(self):
 
1473
        """For a shared repository, ('no', ) is returned."""
 
1474
        backing = self.get_transport()
 
1475
        request = smart.repository.SmartServerRepositoryIsShared(backing)
 
1476
        self.make_repository('.', shared=False)
 
1477
        self.assertEqual(SmartServerResponse(('no', )),
 
1478
            request.execute('', ))
 
1479
 
 
1480
 
 
1481
class TestSmartServerRepositoryLockWrite(tests.TestCaseWithMemoryTransport):
 
1482
 
 
1483
    def test_lock_write_on_unlocked_repo(self):
 
1484
        backing = self.get_transport()
 
1485
        request = smart.repository.SmartServerRepositoryLockWrite(backing)
 
1486
        repository = self.make_repository('.', format='knit')
 
1487
        response = request.execute('')
 
1488
        nonce = repository.control_files._lock.peek().get('nonce')
 
1489
        self.assertEqual(SmartServerResponse(('ok', nonce)), response)
 
1490
        # The repository is now locked.  Verify that with a new repository
 
1491
        # object.
 
1492
        new_repo = repository.bzrdir.open_repository()
 
1493
        self.assertRaises(errors.LockContention, new_repo.lock_write)
 
1494
        # Cleanup
 
1495
        request = smart.repository.SmartServerRepositoryUnlock(backing)
 
1496
        response = request.execute('', nonce)
 
1497
 
 
1498
    def test_lock_write_on_locked_repo(self):
 
1499
        backing = self.get_transport()
 
1500
        request = smart.repository.SmartServerRepositoryLockWrite(backing)
 
1501
        repository = self.make_repository('.', format='knit')
 
1502
        repo_token = repository.lock_write()
 
1503
        repository.leave_lock_in_place()
 
1504
        repository.unlock()
 
1505
        response = request.execute('')
 
1506
        self.assertEqual(
 
1507
            SmartServerResponse(('LockContention',)), response)
 
1508
        # Cleanup
 
1509
        repository.lock_write(repo_token)
 
1510
        repository.dont_leave_lock_in_place()
 
1511
        repository.unlock()
 
1512
 
 
1513
    def test_lock_write_on_readonly_transport(self):
 
1514
        backing = self.get_readonly_transport()
 
1515
        request = smart.repository.SmartServerRepositoryLockWrite(backing)
 
1516
        repository = self.make_repository('.', format='knit')
 
1517
        response = request.execute('')
 
1518
        self.assertFalse(response.is_successful())
 
1519
        self.assertEqual('LockFailed', response.args[0])
 
1520
 
 
1521
 
 
1522
class TestInsertStreamBase(tests.TestCaseWithMemoryTransport):
 
1523
 
 
1524
    def make_empty_byte_stream(self, repo):
 
1525
        byte_stream = smart.repository._stream_to_byte_stream([], repo._format)
 
1526
        return ''.join(byte_stream)
 
1527
 
 
1528
 
 
1529
class TestSmartServerRepositoryInsertStream(TestInsertStreamBase):
 
1530
 
 
1531
    def test_insert_stream_empty(self):
 
1532
        backing = self.get_transport()
 
1533
        request = smart.repository.SmartServerRepositoryInsertStream(backing)
 
1534
        repository = self.make_repository('.')
 
1535
        response = request.execute('', '')
 
1536
        self.assertEqual(None, response)
 
1537
        response = request.do_chunk(self.make_empty_byte_stream(repository))
 
1538
        self.assertEqual(None, response)
 
1539
        response = request.do_end()
 
1540
        self.assertEqual(SmartServerResponse(('ok', )), response)
 
1541
        
 
1542
 
 
1543
class TestSmartServerRepositoryInsertStreamLocked(TestInsertStreamBase):
 
1544
 
 
1545
    def test_insert_stream_empty(self):
 
1546
        backing = self.get_transport()
 
1547
        request = smart.repository.SmartServerRepositoryInsertStreamLocked(
 
1548
            backing)
 
1549
        repository = self.make_repository('.', format='knit')
 
1550
        lock_token = repository.lock_write()
 
1551
        response = request.execute('', '', lock_token)
 
1552
        self.assertEqual(None, response)
 
1553
        response = request.do_chunk(self.make_empty_byte_stream(repository))
 
1554
        self.assertEqual(None, response)
 
1555
        response = request.do_end()
 
1556
        self.assertEqual(SmartServerResponse(('ok', )), response)
 
1557
        repository.unlock()
 
1558
 
 
1559
    def test_insert_stream_with_wrong_lock_token(self):
 
1560
        backing = self.get_transport()
 
1561
        request = smart.repository.SmartServerRepositoryInsertStreamLocked(
 
1562
            backing)
 
1563
        repository = self.make_repository('.', format='knit')
 
1564
        lock_token = repository.lock_write()
 
1565
        self.assertRaises(
 
1566
            errors.TokenMismatch, request.execute, '', '', 'wrong-token')
 
1567
        repository.unlock()
 
1568
 
 
1569
 
 
1570
class TestSmartServerRepositoryUnlock(tests.TestCaseWithMemoryTransport):
 
1571
 
 
1572
    def setUp(self):
 
1573
        tests.TestCaseWithMemoryTransport.setUp(self)
 
1574
 
 
1575
    def test_unlock_on_locked_repo(self):
 
1576
        backing = self.get_transport()
 
1577
        request = smart.repository.SmartServerRepositoryUnlock(backing)
 
1578
        repository = self.make_repository('.', format='knit')
 
1579
        token = repository.lock_write()
 
1580
        repository.leave_lock_in_place()
 
1581
        repository.unlock()
 
1582
        response = request.execute('', token)
 
1583
        self.assertEqual(
 
1584
            SmartServerResponse(('ok',)), response)
 
1585
        # The repository is now unlocked.  Verify that with a new repository
 
1586
        # object.
 
1587
        new_repo = repository.bzrdir.open_repository()
 
1588
        new_repo.lock_write()
 
1589
        new_repo.unlock()
 
1590
 
 
1591
    def test_unlock_on_unlocked_repo(self):
 
1592
        backing = self.get_transport()
 
1593
        request = smart.repository.SmartServerRepositoryUnlock(backing)
 
1594
        repository = self.make_repository('.', format='knit')
 
1595
        response = request.execute('', 'some token')
 
1596
        self.assertEqual(
 
1597
            SmartServerResponse(('TokenMismatch',)), response)
 
1598
 
 
1599
 
 
1600
class TestSmartServerIsReadonly(tests.TestCaseWithMemoryTransport):
 
1601
 
 
1602
    def test_is_readonly_no(self):
 
1603
        backing = self.get_transport()
 
1604
        request = smart.request.SmartServerIsReadonly(backing)
 
1605
        response = request.execute()
 
1606
        self.assertEqual(
 
1607
            SmartServerResponse(('no',)), response)
 
1608
 
 
1609
    def test_is_readonly_yes(self):
 
1610
        backing = self.get_readonly_transport()
 
1611
        request = smart.request.SmartServerIsReadonly(backing)
 
1612
        response = request.execute()
 
1613
        self.assertEqual(
 
1614
            SmartServerResponse(('yes',)), response)
 
1615
 
 
1616
 
 
1617
class TestSmartServerRepositorySetMakeWorkingTrees(tests.TestCaseWithMemoryTransport):
 
1618
 
 
1619
    def test_set_false(self):
 
1620
        backing = self.get_transport()
 
1621
        repo = self.make_repository('.', shared=True)
 
1622
        repo.set_make_working_trees(True)
 
1623
        request_class = smart.repository.SmartServerRepositorySetMakeWorkingTrees
 
1624
        request = request_class(backing)
 
1625
        self.assertEqual(SuccessfulSmartServerResponse(('ok',)),
 
1626
            request.execute('', 'False'))
 
1627
        repo = repo.bzrdir.open_repository()
 
1628
        self.assertFalse(repo.make_working_trees())
 
1629
 
 
1630
    def test_set_true(self):
 
1631
        backing = self.get_transport()
 
1632
        repo = self.make_repository('.', shared=True)
 
1633
        repo.set_make_working_trees(False)
 
1634
        request_class = smart.repository.SmartServerRepositorySetMakeWorkingTrees
 
1635
        request = request_class(backing)
 
1636
        self.assertEqual(SuccessfulSmartServerResponse(('ok',)),
 
1637
            request.execute('', 'True'))
 
1638
        repo = repo.bzrdir.open_repository()
 
1639
        self.assertTrue(repo.make_working_trees())
 
1640
 
 
1641
 
 
1642
class TestSmartServerPackRepositoryAutopack(tests.TestCaseWithTransport):
 
1643
 
 
1644
    def make_repo_needing_autopacking(self, path='.'):
 
1645
        # Make a repo in need of autopacking.
 
1646
        tree = self.make_branch_and_tree('.', format='pack-0.92')
 
1647
        repo = tree.branch.repository
 
1648
        # monkey-patch the pack collection to disable autopacking
 
1649
        repo._pack_collection._max_pack_count = lambda count: count
 
1650
        for x in range(10):
 
1651
            tree.commit('commit %s' % x)
 
1652
        self.assertEqual(10, len(repo._pack_collection.names()))
 
1653
        del repo._pack_collection._max_pack_count
 
1654
        return repo
 
1655
 
 
1656
    def test_autopack_needed(self):
 
1657
        repo = self.make_repo_needing_autopacking()
 
1658
        repo.lock_write()
 
1659
        self.addCleanup(repo.unlock)
 
1660
        backing = self.get_transport()
 
1661
        request = smart.packrepository.SmartServerPackRepositoryAutopack(
 
1662
            backing)
 
1663
        response = request.execute('')
 
1664
        self.assertEqual(SmartServerResponse(('ok',)), response)
 
1665
        repo._pack_collection.reload_pack_names()
 
1666
        self.assertEqual(1, len(repo._pack_collection.names()))
 
1667
 
 
1668
    def test_autopack_not_needed(self):
 
1669
        tree = self.make_branch_and_tree('.', format='pack-0.92')
 
1670
        repo = tree.branch.repository
 
1671
        repo.lock_write()
 
1672
        self.addCleanup(repo.unlock)
 
1673
        for x in range(9):
 
1674
            tree.commit('commit %s' % x)
 
1675
        backing = self.get_transport()
 
1676
        request = smart.packrepository.SmartServerPackRepositoryAutopack(
 
1677
            backing)
 
1678
        response = request.execute('')
 
1679
        self.assertEqual(SmartServerResponse(('ok',)), response)
 
1680
        repo._pack_collection.reload_pack_names()
 
1681
        self.assertEqual(9, len(repo._pack_collection.names()))
 
1682
 
 
1683
    def test_autopack_on_nonpack_format(self):
 
1684
        """A request to autopack a non-pack repo is a no-op."""
 
1685
        repo = self.make_repository('.', format='knit')
 
1686
        backing = self.get_transport()
 
1687
        request = smart.packrepository.SmartServerPackRepositoryAutopack(
 
1688
            backing)
 
1689
        response = request.execute('')
 
1690
        self.assertEqual(SmartServerResponse(('ok',)), response)
 
1691
 
 
1692
 
 
1693
class TestHandlers(tests.TestCase):
 
1694
    """Tests for the request.request_handlers object."""
 
1695
 
 
1696
    def test_all_registrations_exist(self):
 
1697
        """All registered request_handlers can be found."""
 
1698
        # If there's a typo in a register_lazy call, this loop will fail with
 
1699
        # an AttributeError.
 
1700
        for key, item in smart.request.request_handlers.iteritems():
 
1701
            pass
 
1702
 
 
1703
    def assertHandlerEqual(self, verb, handler):
 
1704
        self.assertEqual(smart.request.request_handlers.get(verb), handler)
 
1705
 
 
1706
    def test_registered_methods(self):
 
1707
        """Test that known methods are registered to the correct object."""
 
1708
        self.assertHandlerEqual('Branch.get_config_file',
 
1709
            smart.branch.SmartServerBranchGetConfigFile)
 
1710
        self.assertHandlerEqual('Branch.get_parent',
 
1711
            smart.branch.SmartServerBranchGetParent)
 
1712
        self.assertHandlerEqual('Branch.get_tags_bytes',
 
1713
            smart.branch.SmartServerBranchGetTagsBytes)
 
1714
        self.assertHandlerEqual('Branch.lock_write',
 
1715
            smart.branch.SmartServerBranchRequestLockWrite)
 
1716
        self.assertHandlerEqual('Branch.last_revision_info',
 
1717
            smart.branch.SmartServerBranchRequestLastRevisionInfo)
 
1718
        self.assertHandlerEqual('Branch.revision_history',
 
1719
            smart.branch.SmartServerRequestRevisionHistory)
 
1720
        self.assertHandlerEqual('Branch.set_config_option',
 
1721
            smart.branch.SmartServerBranchRequestSetConfigOption)
 
1722
        self.assertHandlerEqual('Branch.set_last_revision',
 
1723
            smart.branch.SmartServerBranchRequestSetLastRevision)
 
1724
        self.assertHandlerEqual('Branch.set_last_revision_info',
 
1725
            smart.branch.SmartServerBranchRequestSetLastRevisionInfo)
 
1726
        self.assertHandlerEqual('Branch.set_last_revision_ex',
 
1727
            smart.branch.SmartServerBranchRequestSetLastRevisionEx)
 
1728
        self.assertHandlerEqual('Branch.set_parent_location',
 
1729
            smart.branch.SmartServerBranchRequestSetParentLocation)
 
1730
        self.assertHandlerEqual('Branch.unlock',
 
1731
            smart.branch.SmartServerBranchRequestUnlock)
 
1732
        self.assertHandlerEqual('BzrDir.find_repository',
 
1733
            smart.bzrdir.SmartServerRequestFindRepositoryV1)
 
1734
        self.assertHandlerEqual('BzrDir.find_repositoryV2',
 
1735
            smart.bzrdir.SmartServerRequestFindRepositoryV2)
 
1736
        self.assertHandlerEqual('BzrDirFormat.initialize',
 
1737
            smart.bzrdir.SmartServerRequestInitializeBzrDir)
 
1738
        self.assertHandlerEqual('BzrDirFormat.initialize_ex_1.16',
 
1739
            smart.bzrdir.SmartServerRequestBzrDirInitializeEx)
 
1740
        self.assertHandlerEqual('BzrDir.cloning_metadir',
 
1741
            smart.bzrdir.SmartServerBzrDirRequestCloningMetaDir)
 
1742
        self.assertHandlerEqual('BzrDir.get_config_file',
 
1743
            smart.bzrdir.SmartServerBzrDirRequestConfigFile)
 
1744
        self.assertHandlerEqual('BzrDir.open_branch',
 
1745
            smart.bzrdir.SmartServerRequestOpenBranch)
 
1746
        self.assertHandlerEqual('BzrDir.open_branchV2',
 
1747
            smart.bzrdir.SmartServerRequestOpenBranchV2)
 
1748
        self.assertHandlerEqual('PackRepository.autopack',
 
1749
            smart.packrepository.SmartServerPackRepositoryAutopack)
 
1750
        self.assertHandlerEqual('Repository.gather_stats',
 
1751
            smart.repository.SmartServerRepositoryGatherStats)
 
1752
        self.assertHandlerEqual('Repository.get_parent_map',
 
1753
            smart.repository.SmartServerRepositoryGetParentMap)
 
1754
        self.assertHandlerEqual('Repository.get_rev_id_for_revno',
 
1755
            smart.repository.SmartServerRepositoryGetRevIdForRevno)
 
1756
        self.assertHandlerEqual('Repository.get_revision_graph',
 
1757
            smart.repository.SmartServerRepositoryGetRevisionGraph)
 
1758
        self.assertHandlerEqual('Repository.get_stream',
 
1759
            smart.repository.SmartServerRepositoryGetStream)
 
1760
        self.assertHandlerEqual('Repository.has_revision',
 
1761
            smart.repository.SmartServerRequestHasRevision)
 
1762
        self.assertHandlerEqual('Repository.insert_stream',
 
1763
            smart.repository.SmartServerRepositoryInsertStream)
 
1764
        self.assertHandlerEqual('Repository.insert_stream_locked',
 
1765
            smart.repository.SmartServerRepositoryInsertStreamLocked)
 
1766
        self.assertHandlerEqual('Repository.is_shared',
 
1767
            smart.repository.SmartServerRepositoryIsShared)
 
1768
        self.assertHandlerEqual('Repository.lock_write',
 
1769
            smart.repository.SmartServerRepositoryLockWrite)
 
1770
        self.assertHandlerEqual('Repository.tarball',
 
1771
            smart.repository.SmartServerRepositoryTarball)
 
1772
        self.assertHandlerEqual('Repository.unlock',
 
1773
            smart.repository.SmartServerRepositoryUnlock)
 
1774
        self.assertHandlerEqual('Transport.is_readonly',
 
1775
            smart.request.SmartServerIsReadonly)