/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_smart.py

  • Committer: Vincent Ladeuil
  • Date: 2012-01-18 14:09:19 UTC
  • mto: This revision was merged to the branch mainline in revision 6468.
  • Revision ID: v.ladeuil+lp@free.fr-20120118140919-rlvdrhpc0nq1lbwi
Change set/remove to require a lock for the branch config files.

This means that tests (or any plugin for that matter) do not requires an
explicit lock on the branch anymore to change a single option. This also
means the optimisation becomes "opt-in" and as such won't be as
spectacular as it may be and/or harder to get right (nothing fails
anymore).

This reduces the diff by ~300 lines.

Code/tests that were updating more than one config option is still taking
a lock to at least avoid some IOs and demonstrate the benefits through
the decreased number of hpss calls.

The duplication between BranchStack and BranchOnlyStack will be removed
once the same sharing is in place for local config files, at which point
the Stack class itself may be able to host the changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2006-2012 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
import zlib
 
29
 
 
30
from bzrlib import (
 
31
    branch as _mod_branch,
 
32
    bzrdir,
 
33
    errors,
 
34
    gpg,
 
35
    inventory_delta,
 
36
    tests,
 
37
    transport,
 
38
    urlutils,
 
39
    versionedfile,
 
40
    )
 
41
from bzrlib.smart import (
 
42
    branch as smart_branch,
 
43
    bzrdir as smart_dir,
 
44
    repository as smart_repo,
 
45
    packrepository as smart_packrepo,
 
46
    request as smart_req,
 
47
    server,
 
48
    vfs,
 
49
    )
 
50
from bzrlib.testament import Testament
 
51
from bzrlib.tests import test_server
 
52
from bzrlib.transport import (
 
53
    chroot,
 
54
    memory,
 
55
    )
 
56
 
 
57
 
 
58
def load_tests(standard_tests, module, loader):
 
59
    """Multiply tests version and protocol consistency."""
 
60
    # FindRepository tests.
 
61
    scenarios = [
 
62
        ("find_repository", {
 
63
            "_request_class": smart_dir.SmartServerRequestFindRepositoryV1}),
 
64
        ("find_repositoryV2", {
 
65
            "_request_class": smart_dir.SmartServerRequestFindRepositoryV2}),
 
66
        ("find_repositoryV3", {
 
67
            "_request_class": smart_dir.SmartServerRequestFindRepositoryV3}),
 
68
        ]
 
69
    to_adapt, result = tests.split_suite_by_re(standard_tests,
 
70
        "TestSmartServerRequestFindRepository")
 
71
    v2_only, v1_and_2 = tests.split_suite_by_re(to_adapt,
 
72
        "_v2")
 
73
    tests.multiply_tests(v1_and_2, scenarios, result)
 
74
    # The first scenario is only applicable to v1 protocols, it is deleted
 
75
    # since.
 
76
    tests.multiply_tests(v2_only, scenarios[1:], result)
 
77
    return result
 
78
 
 
79
 
 
80
class TestCaseWithChrootedTransport(tests.TestCaseWithTransport):
 
81
 
 
82
    def setUp(self):
 
83
        self.vfs_transport_factory = memory.MemoryServer
 
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.start_server(self._chroot_server)
 
92
        t = transport.get_transport_from_url(self._chroot_server.get_url())
 
93
        if relpath is not None:
 
94
            t = t.clone(relpath)
 
95
        return t
 
96
 
 
97
 
 
98
class TestCaseWithSmartMedium(tests.TestCaseWithMemoryTransport):
 
99
 
 
100
    def setUp(self):
 
101
        super(TestCaseWithSmartMedium, self).setUp()
 
102
        # We're allowed to set  the transport class here, so that we don't use
 
103
        # the default or a parameterized class, but rather use the
 
104
        # TestCaseWithTransport infrastructure to set up a smart server and
 
105
        # transport.
 
106
        self.overrideAttr(self, "transport_server", self.make_transport_server)
 
107
 
 
108
    def make_transport_server(self):
 
109
        return test_server.SmartTCPServer_for_testing('-' + self.id())
 
110
 
 
111
    def get_smart_medium(self):
 
112
        """Get a smart medium to use in tests."""
 
113
        return self.get_transport().get_smart_medium()
 
114
 
 
115
 
 
116
class TestByteStreamToStream(tests.TestCase):
 
117
 
 
118
    def test_repeated_substreams_same_kind_are_one_stream(self):
 
119
        # Make a stream - an iterable of bytestrings.
 
120
        stream = [('text', [versionedfile.FulltextContentFactory(('k1',), None,
 
121
            None, 'foo')]),('text', [
 
122
            versionedfile.FulltextContentFactory(('k2',), None, None, 'bar')])]
 
123
        fmt = bzrdir.format_registry.get('pack-0.92')().repository_format
 
124
        bytes = smart_repo._stream_to_byte_stream(stream, fmt)
 
125
        streams = []
 
126
        # Iterate the resulting iterable; checking that we get only one stream
 
127
        # out.
 
128
        fmt, stream = smart_repo._byte_stream_to_stream(bytes)
 
129
        for kind, substream in stream:
 
130
            streams.append((kind, list(substream)))
 
131
        self.assertLength(1, streams)
 
132
        self.assertLength(2, streams[0][1])
 
133
 
 
134
 
 
135
class TestSmartServerResponse(tests.TestCase):
 
136
 
 
137
    def test__eq__(self):
 
138
        self.assertEqual(smart_req.SmartServerResponse(('ok', )),
 
139
            smart_req.SmartServerResponse(('ok', )))
 
140
        self.assertEqual(smart_req.SmartServerResponse(('ok', ), 'body'),
 
141
            smart_req.SmartServerResponse(('ok', ), 'body'))
 
142
        self.assertNotEqual(smart_req.SmartServerResponse(('ok', )),
 
143
            smart_req.SmartServerResponse(('notok', )))
 
144
        self.assertNotEqual(smart_req.SmartServerResponse(('ok', ), 'body'),
 
145
            smart_req.SmartServerResponse(('ok', )))
 
146
        self.assertNotEqual(None,
 
147
            smart_req.SmartServerResponse(('ok', )))
 
148
 
 
149
    def test__str__(self):
 
150
        """SmartServerResponses can be stringified."""
 
151
        self.assertEqual(
 
152
            "<SuccessfulSmartServerResponse args=('args',) body='body'>",
 
153
            str(smart_req.SuccessfulSmartServerResponse(('args',), 'body')))
 
154
        self.assertEqual(
 
155
            "<FailedSmartServerResponse args=('args',) body='body'>",
 
156
            str(smart_req.FailedSmartServerResponse(('args',), 'body')))
 
157
 
 
158
 
 
159
class TestSmartServerRequest(tests.TestCaseWithMemoryTransport):
 
160
 
 
161
    def test_translate_client_path(self):
 
162
        transport = self.get_transport()
 
163
        request = smart_req.SmartServerRequest(transport, 'foo/')
 
164
        self.assertEqual('./', request.translate_client_path('foo/'))
 
165
        self.assertRaises(
 
166
            errors.InvalidURLJoin, request.translate_client_path, 'foo/..')
 
167
        self.assertRaises(
 
168
            errors.PathNotChild, request.translate_client_path, '/')
 
169
        self.assertRaises(
 
170
            errors.PathNotChild, request.translate_client_path, 'bar/')
 
171
        self.assertEqual('./baz', request.translate_client_path('foo/baz'))
 
172
        e_acute = u'\N{LATIN SMALL LETTER E WITH ACUTE}'.encode('utf-8')
 
173
        self.assertEqual('./' + urlutils.escape(e_acute),
 
174
                         request.translate_client_path('foo/' + e_acute))
 
175
 
 
176
    def test_translate_client_path_vfs(self):
 
177
        """VfsRequests receive escaped paths rather than raw UTF-8."""
 
178
        transport = self.get_transport()
 
179
        request = vfs.VfsRequest(transport, 'foo/')
 
180
        e_acute = u'\N{LATIN SMALL LETTER E WITH ACUTE}'.encode('utf-8')
 
181
        escaped = urlutils.escape('foo/' + e_acute)
 
182
        self.assertEqual('./' + urlutils.escape(e_acute),
 
183
                         request.translate_client_path(escaped))
 
184
 
 
185
    def test_transport_from_client_path(self):
 
186
        transport = self.get_transport()
 
187
        request = smart_req.SmartServerRequest(transport, 'foo/')
 
188
        self.assertEqual(
 
189
            transport.base,
 
190
            request.transport_from_client_path('foo/').base)
 
191
 
 
192
 
 
193
class TestSmartServerBzrDirRequestCloningMetaDir(
 
194
    tests.TestCaseWithMemoryTransport):
 
195
    """Tests for BzrDir.cloning_metadir."""
 
196
 
 
197
    def test_cloning_metadir(self):
 
198
        """When there is a bzrdir present, the call succeeds."""
 
199
        backing = self.get_transport()
 
200
        dir = self.make_bzrdir('.')
 
201
        local_result = dir.cloning_metadir()
 
202
        request_class = smart_dir.SmartServerBzrDirRequestCloningMetaDir
 
203
        request = request_class(backing)
 
204
        expected = smart_req.SuccessfulSmartServerResponse(
 
205
            (local_result.network_name(),
 
206
            local_result.repository_format.network_name(),
 
207
            ('branch', local_result.get_branch_format().network_name())))
 
208
        self.assertEqual(expected, request.execute('', 'False'))
 
209
 
 
210
    def test_cloning_metadir_reference(self):
 
211
        """The request fails when bzrdir contains a branch reference."""
 
212
        backing = self.get_transport()
 
213
        referenced_branch = self.make_branch('referenced')
 
214
        dir = self.make_bzrdir('.')
 
215
        local_result = dir.cloning_metadir()
 
216
        reference = _mod_branch.BranchReferenceFormat().initialize(
 
217
            dir, target_branch=referenced_branch)
 
218
        reference_url = _mod_branch.BranchReferenceFormat().get_reference(dir)
 
219
        # The server shouldn't try to follow the branch reference, so it's fine
 
220
        # if the referenced branch isn't reachable.
 
221
        backing.rename('referenced', 'moved')
 
222
        request_class = smart_dir.SmartServerBzrDirRequestCloningMetaDir
 
223
        request = request_class(backing)
 
224
        expected = smart_req.FailedSmartServerResponse(('BranchReference',))
 
225
        self.assertEqual(expected, request.execute('', 'False'))
 
226
 
 
227
 
 
228
class TestSmartServerBzrDirRequestCloningMetaDir(
 
229
    tests.TestCaseWithMemoryTransport):
 
230
    """Tests for BzrDir.checkout_metadir."""
 
231
 
 
232
    def test_checkout_metadir(self):
 
233
        backing = self.get_transport()
 
234
        request = smart_dir.SmartServerBzrDirRequestCheckoutMetaDir(
 
235
            backing)
 
236
        branch = self.make_branch('.', format='2a')
 
237
        response = request.execute('')
 
238
        self.assertEqual(
 
239
            smart_req.SmartServerResponse(
 
240
                ('Bazaar-NG meta directory, format 1\n',
 
241
                 'Bazaar repository format 2a (needs bzr 1.16 or later)\n',
 
242
                 'Bazaar Branch Format 7 (needs bzr 1.6)\n')),
 
243
            response)
 
244
 
 
245
 
 
246
class TestSmartServerBzrDirRequestDestroyBranch(
 
247
    tests.TestCaseWithMemoryTransport):
 
248
    """Tests for BzrDir.destroy_branch."""
 
249
 
 
250
    def test_destroy_branch_default(self):
 
251
        """The default branch can be removed."""
 
252
        backing = self.get_transport()
 
253
        dir = self.make_branch('.').bzrdir
 
254
        request_class = smart_dir.SmartServerBzrDirRequestDestroyBranch
 
255
        request = request_class(backing)
 
256
        expected = smart_req.SuccessfulSmartServerResponse(('ok',))
 
257
        self.assertEqual(expected, request.execute('', None))
 
258
 
 
259
    def test_destroy_branch_named(self):
 
260
        """A named branch can be removed."""
 
261
        backing = self.get_transport()
 
262
        dir = self.make_repository('.', format="development-colo").bzrdir
 
263
        dir.create_branch(name="branchname")
 
264
        request_class = smart_dir.SmartServerBzrDirRequestDestroyBranch
 
265
        request = request_class(backing)
 
266
        expected = smart_req.SuccessfulSmartServerResponse(('ok',))
 
267
        self.assertEqual(expected, request.execute('', "branchname"))
 
268
 
 
269
    def test_destroy_branch_missing(self):
 
270
        """An error is raised if the branch didn't exist."""
 
271
        backing = self.get_transport()
 
272
        dir = self.make_bzrdir('.', format="development-colo")
 
273
        request_class = smart_dir.SmartServerBzrDirRequestDestroyBranch
 
274
        request = request_class(backing)
 
275
        expected = smart_req.FailedSmartServerResponse(('nobranch',), None)
 
276
        self.assertEqual(expected, request.execute('', "branchname"))
 
277
 
 
278
 
 
279
class TestSmartServerBzrDirRequestHasWorkingTree(
 
280
    tests.TestCaseWithTransport):
 
281
    """Tests for BzrDir.has_workingtree."""
 
282
 
 
283
    def test_has_workingtree_yes(self):
 
284
        """A working tree is present."""
 
285
        backing = self.get_transport()
 
286
        dir = self.make_branch_and_tree('.').bzrdir
 
287
        request_class = smart_dir.SmartServerBzrDirRequestHasWorkingTree
 
288
        request = request_class(backing)
 
289
        expected = smart_req.SuccessfulSmartServerResponse(('yes',))
 
290
        self.assertEqual(expected, request.execute(''))
 
291
 
 
292
    def test_has_workingtree_no(self):
 
293
        """A working tree is missing."""
 
294
        backing = self.get_transport()
 
295
        dir = self.make_bzrdir('.')
 
296
        request_class = smart_dir.SmartServerBzrDirRequestHasWorkingTree
 
297
        request = request_class(backing)
 
298
        expected = smart_req.SuccessfulSmartServerResponse(('no',))
 
299
        self.assertEqual(expected, request.execute(''))
 
300
 
 
301
 
 
302
class TestSmartServerBzrDirRequestDestroyRepository(
 
303
    tests.TestCaseWithMemoryTransport):
 
304
    """Tests for BzrDir.destroy_repository."""
 
305
 
 
306
    def test_destroy_repository_default(self):
 
307
        """The repository can be removed."""
 
308
        backing = self.get_transport()
 
309
        dir = self.make_repository('.').bzrdir
 
310
        request_class = smart_dir.SmartServerBzrDirRequestDestroyRepository
 
311
        request = request_class(backing)
 
312
        expected = smart_req.SuccessfulSmartServerResponse(('ok',))
 
313
        self.assertEqual(expected, request.execute(''))
 
314
 
 
315
    def test_destroy_repository_missing(self):
 
316
        """An error is raised if the repository didn't exist."""
 
317
        backing = self.get_transport()
 
318
        dir = self.make_bzrdir('.')
 
319
        request_class = smart_dir.SmartServerBzrDirRequestDestroyRepository
 
320
        request = request_class(backing)
 
321
        expected = smart_req.FailedSmartServerResponse(
 
322
            ('norepository',), None)
 
323
        self.assertEqual(expected, request.execute(''))
 
324
 
 
325
 
 
326
class TestSmartServerRequestCreateRepository(tests.TestCaseWithMemoryTransport):
 
327
    """Tests for BzrDir.create_repository."""
 
328
 
 
329
    def test_makes_repository(self):
 
330
        """When there is a bzrdir present, the call succeeds."""
 
331
        backing = self.get_transport()
 
332
        self.make_bzrdir('.')
 
333
        request_class = smart_dir.SmartServerRequestCreateRepository
 
334
        request = request_class(backing)
 
335
        reference_bzrdir_format = bzrdir.format_registry.get('pack-0.92')()
 
336
        reference_format = reference_bzrdir_format.repository_format
 
337
        network_name = reference_format.network_name()
 
338
        expected = smart_req.SuccessfulSmartServerResponse(
 
339
            ('ok', 'no', 'no', 'no', network_name))
 
340
        self.assertEqual(expected, request.execute('', network_name, 'True'))
 
341
 
 
342
 
 
343
class TestSmartServerRequestFindRepository(tests.TestCaseWithMemoryTransport):
 
344
    """Tests for BzrDir.find_repository."""
 
345
 
 
346
    def test_no_repository(self):
 
347
        """When there is no repository to be found, ('norepository', ) is returned."""
 
348
        backing = self.get_transport()
 
349
        request = self._request_class(backing)
 
350
        self.make_bzrdir('.')
 
351
        self.assertEqual(smart_req.SmartServerResponse(('norepository', )),
 
352
            request.execute(''))
 
353
 
 
354
    def test_nonshared_repository(self):
 
355
        # nonshared repositorys only allow 'find' to return a handle when the
 
356
        # path the repository is being searched on is the same as that that
 
357
        # the repository is at.
 
358
        backing = self.get_transport()
 
359
        request = self._request_class(backing)
 
360
        result = self._make_repository_and_result()
 
361
        self.assertEqual(result, request.execute(''))
 
362
        self.make_bzrdir('subdir')
 
363
        self.assertEqual(smart_req.SmartServerResponse(('norepository', )),
 
364
            request.execute('subdir'))
 
365
 
 
366
    def _make_repository_and_result(self, shared=False, format=None):
 
367
        """Convenience function to setup a repository.
 
368
 
 
369
        :result: The SmartServerResponse to expect when opening it.
 
370
        """
 
371
        repo = self.make_repository('.', shared=shared, format=format)
 
372
        if repo.supports_rich_root():
 
373
            rich_root = 'yes'
 
374
        else:
 
375
            rich_root = 'no'
 
376
        if repo._format.supports_tree_reference:
 
377
            subtrees = 'yes'
 
378
        else:
 
379
            subtrees = 'no'
 
380
        if repo._format.supports_external_lookups:
 
381
            external = 'yes'
 
382
        else:
 
383
            external = 'no'
 
384
        if (smart_dir.SmartServerRequestFindRepositoryV3 ==
 
385
            self._request_class):
 
386
            return smart_req.SuccessfulSmartServerResponse(
 
387
                ('ok', '', rich_root, subtrees, external,
 
388
                 repo._format.network_name()))
 
389
        elif (smart_dir.SmartServerRequestFindRepositoryV2 ==
 
390
            self._request_class):
 
391
            # All tests so far are on formats, and for non-external
 
392
            # repositories.
 
393
            return smart_req.SuccessfulSmartServerResponse(
 
394
                ('ok', '', rich_root, subtrees, external))
 
395
        else:
 
396
            return smart_req.SuccessfulSmartServerResponse(
 
397
                ('ok', '', rich_root, subtrees))
 
398
 
 
399
    def test_shared_repository(self):
 
400
        """When there is a shared repository, we get 'ok', 'relpath-to-repo'."""
 
401
        backing = self.get_transport()
 
402
        request = self._request_class(backing)
 
403
        result = self._make_repository_and_result(shared=True)
 
404
        self.assertEqual(result, request.execute(''))
 
405
        self.make_bzrdir('subdir')
 
406
        result2 = smart_req.SmartServerResponse(
 
407
            result.args[0:1] + ('..', ) + result.args[2:])
 
408
        self.assertEqual(result2,
 
409
            request.execute('subdir'))
 
410
        self.make_bzrdir('subdir/deeper')
 
411
        result3 = smart_req.SmartServerResponse(
 
412
            result.args[0:1] + ('../..', ) + result.args[2:])
 
413
        self.assertEqual(result3,
 
414
            request.execute('subdir/deeper'))
 
415
 
 
416
    def test_rich_root_and_subtree_encoding(self):
 
417
        """Test for the format attributes for rich root and subtree support."""
 
418
        backing = self.get_transport()
 
419
        request = self._request_class(backing)
 
420
        result = self._make_repository_and_result(
 
421
            format='dirstate-with-subtree')
 
422
        # check the test will be valid
 
423
        self.assertEqual('yes', result.args[2])
 
424
        self.assertEqual('yes', result.args[3])
 
425
        self.assertEqual(result, request.execute(''))
 
426
 
 
427
    def test_supports_external_lookups_no_v2(self):
 
428
        """Test for the supports_external_lookups attribute."""
 
429
        backing = self.get_transport()
 
430
        request = self._request_class(backing)
 
431
        result = self._make_repository_and_result(
 
432
            format='dirstate-with-subtree')
 
433
        # check the test will be valid
 
434
        self.assertEqual('no', result.args[4])
 
435
        self.assertEqual(result, request.execute(''))
 
436
 
 
437
 
 
438
class TestSmartServerBzrDirRequestGetConfigFile(
 
439
    tests.TestCaseWithMemoryTransport):
 
440
    """Tests for BzrDir.get_config_file."""
 
441
 
 
442
    def test_present(self):
 
443
        backing = self.get_transport()
 
444
        dir = self.make_bzrdir('.')
 
445
        dir.get_config().set_default_stack_on("/")
 
446
        local_result = dir._get_config()._get_config_file().read()
 
447
        request_class = smart_dir.SmartServerBzrDirRequestConfigFile
 
448
        request = request_class(backing)
 
449
        expected = smart_req.SuccessfulSmartServerResponse((), local_result)
 
450
        self.assertEqual(expected, request.execute(''))
 
451
 
 
452
    def test_missing(self):
 
453
        backing = self.get_transport()
 
454
        dir = self.make_bzrdir('.')
 
455
        request_class = smart_dir.SmartServerBzrDirRequestConfigFile
 
456
        request = request_class(backing)
 
457
        expected = smart_req.SuccessfulSmartServerResponse((), '')
 
458
        self.assertEqual(expected, request.execute(''))
 
459
 
 
460
 
 
461
class TestSmartServerRequestInitializeBzrDir(tests.TestCaseWithMemoryTransport):
 
462
 
 
463
    def test_empty_dir(self):
 
464
        """Initializing an empty dir should succeed and do it."""
 
465
        backing = self.get_transport()
 
466
        request = smart_dir.SmartServerRequestInitializeBzrDir(backing)
 
467
        self.assertEqual(smart_req.SmartServerResponse(('ok', )),
 
468
            request.execute(''))
 
469
        made_dir = bzrdir.BzrDir.open_from_transport(backing)
 
470
        # no branch, tree or repository is expected with the current
 
471
        # default formart.
 
472
        self.assertRaises(errors.NoWorkingTree, made_dir.open_workingtree)
 
473
        self.assertRaises(errors.NotBranchError, made_dir.open_branch)
 
474
        self.assertRaises(errors.NoRepositoryPresent, made_dir.open_repository)
 
475
 
 
476
    def test_missing_dir(self):
 
477
        """Initializing a missing directory should fail like the bzrdir api."""
 
478
        backing = self.get_transport()
 
479
        request = smart_dir.SmartServerRequestInitializeBzrDir(backing)
 
480
        self.assertRaises(errors.NoSuchFile,
 
481
            request.execute, 'subdir')
 
482
 
 
483
    def test_initialized_dir(self):
 
484
        """Initializing an extant bzrdir should fail like the bzrdir api."""
 
485
        backing = self.get_transport()
 
486
        request = smart_dir.SmartServerRequestInitializeBzrDir(backing)
 
487
        self.make_bzrdir('subdir')
 
488
        self.assertRaises(errors.FileExists,
 
489
            request.execute, 'subdir')
 
490
 
 
491
 
 
492
class TestSmartServerRequestBzrDirInitializeEx(
 
493
    tests.TestCaseWithMemoryTransport):
 
494
    """Basic tests for BzrDir.initialize_ex_1.16 in the smart server.
 
495
 
 
496
    The main unit tests in test_bzrdir exercise the API comprehensively.
 
497
    """
 
498
 
 
499
    def test_empty_dir(self):
 
500
        """Initializing an empty dir should succeed and do it."""
 
501
        backing = self.get_transport()
 
502
        name = self.make_bzrdir('reference')._format.network_name()
 
503
        request = smart_dir.SmartServerRequestBzrDirInitializeEx(backing)
 
504
        self.assertEqual(
 
505
            smart_req.SmartServerResponse(('', '', '', '', '', '', name,
 
506
                                           'False', '', '', '')),
 
507
            request.execute(name, '', 'True', 'False', 'False', '', '', '', '',
 
508
                            'False'))
 
509
        made_dir = bzrdir.BzrDir.open_from_transport(backing)
 
510
        # no branch, tree or repository is expected with the current
 
511
        # default format.
 
512
        self.assertRaises(errors.NoWorkingTree, made_dir.open_workingtree)
 
513
        self.assertRaises(errors.NotBranchError, made_dir.open_branch)
 
514
        self.assertRaises(errors.NoRepositoryPresent, made_dir.open_repository)
 
515
 
 
516
    def test_missing_dir(self):
 
517
        """Initializing a missing directory should fail like the bzrdir api."""
 
518
        backing = self.get_transport()
 
519
        name = self.make_bzrdir('reference')._format.network_name()
 
520
        request = smart_dir.SmartServerRequestBzrDirInitializeEx(backing)
 
521
        self.assertRaises(errors.NoSuchFile, request.execute, name,
 
522
            'subdir/dir', 'False', 'False', 'False', '', '', '', '', 'False')
 
523
 
 
524
    def test_initialized_dir(self):
 
525
        """Initializing an extant directory should fail like the bzrdir api."""
 
526
        backing = self.get_transport()
 
527
        name = self.make_bzrdir('reference')._format.network_name()
 
528
        request = smart_dir.SmartServerRequestBzrDirInitializeEx(backing)
 
529
        self.make_bzrdir('subdir')
 
530
        self.assertRaises(errors.FileExists, request.execute, name, 'subdir',
 
531
            'False', 'False', 'False', '', '', '', '', 'False')
 
532
 
 
533
 
 
534
class TestSmartServerRequestOpenBzrDir(tests.TestCaseWithMemoryTransport):
 
535
 
 
536
    def test_no_directory(self):
 
537
        backing = self.get_transport()
 
538
        request = smart_dir.SmartServerRequestOpenBzrDir(backing)
 
539
        self.assertEqual(smart_req.SmartServerResponse(('no', )),
 
540
            request.execute('does-not-exist'))
 
541
 
 
542
    def test_empty_directory(self):
 
543
        backing = self.get_transport()
 
544
        backing.mkdir('empty')
 
545
        request = smart_dir.SmartServerRequestOpenBzrDir(backing)
 
546
        self.assertEqual(smart_req.SmartServerResponse(('no', )),
 
547
            request.execute('empty'))
 
548
 
 
549
    def test_outside_root_client_path(self):
 
550
        backing = self.get_transport()
 
551
        request = smart_dir.SmartServerRequestOpenBzrDir(backing,
 
552
            root_client_path='root')
 
553
        self.assertEqual(smart_req.SmartServerResponse(('no', )),
 
554
            request.execute('not-root'))
 
555
 
 
556
 
 
557
class TestSmartServerRequestOpenBzrDir_2_1(tests.TestCaseWithMemoryTransport):
 
558
 
 
559
    def test_no_directory(self):
 
560
        backing = self.get_transport()
 
561
        request = smart_dir.SmartServerRequestOpenBzrDir_2_1(backing)
 
562
        self.assertEqual(smart_req.SmartServerResponse(('no', )),
 
563
            request.execute('does-not-exist'))
 
564
 
 
565
    def test_empty_directory(self):
 
566
        backing = self.get_transport()
 
567
        backing.mkdir('empty')
 
568
        request = smart_dir.SmartServerRequestOpenBzrDir_2_1(backing)
 
569
        self.assertEqual(smart_req.SmartServerResponse(('no', )),
 
570
            request.execute('empty'))
 
571
 
 
572
    def test_present_without_workingtree(self):
 
573
        backing = self.get_transport()
 
574
        request = smart_dir.SmartServerRequestOpenBzrDir_2_1(backing)
 
575
        self.make_bzrdir('.')
 
576
        self.assertEqual(smart_req.SmartServerResponse(('yes', 'no')),
 
577
            request.execute(''))
 
578
 
 
579
    def test_outside_root_client_path(self):
 
580
        backing = self.get_transport()
 
581
        request = smart_dir.SmartServerRequestOpenBzrDir_2_1(backing,
 
582
            root_client_path='root')
 
583
        self.assertEqual(smart_req.SmartServerResponse(('no',)),
 
584
            request.execute('not-root'))
 
585
 
 
586
 
 
587
class TestSmartServerRequestOpenBzrDir_2_1_disk(TestCaseWithChrootedTransport):
 
588
 
 
589
    def test_present_with_workingtree(self):
 
590
        self.vfs_transport_factory = test_server.LocalURLServer
 
591
        backing = self.get_transport()
 
592
        request = smart_dir.SmartServerRequestOpenBzrDir_2_1(backing)
 
593
        bd = self.make_bzrdir('.')
 
594
        bd.create_repository()
 
595
        bd.create_branch()
 
596
        bd.create_workingtree()
 
597
        self.assertEqual(smart_req.SmartServerResponse(('yes', 'yes')),
 
598
            request.execute(''))
 
599
 
 
600
 
 
601
class TestSmartServerRequestOpenBranch(TestCaseWithChrootedTransport):
 
602
 
 
603
    def test_no_branch(self):
 
604
        """When there is no branch, ('nobranch', ) is returned."""
 
605
        backing = self.get_transport()
 
606
        request = smart_dir.SmartServerRequestOpenBranch(backing)
 
607
        self.make_bzrdir('.')
 
608
        self.assertEqual(smart_req.SmartServerResponse(('nobranch', )),
 
609
            request.execute(''))
 
610
 
 
611
    def test_branch(self):
 
612
        """When there is a branch, 'ok' is returned."""
 
613
        backing = self.get_transport()
 
614
        request = smart_dir.SmartServerRequestOpenBranch(backing)
 
615
        self.make_branch('.')
 
616
        self.assertEqual(smart_req.SmartServerResponse(('ok', '')),
 
617
            request.execute(''))
 
618
 
 
619
    def test_branch_reference(self):
 
620
        """When there is a branch reference, the reference URL is returned."""
 
621
        self.vfs_transport_factory = test_server.LocalURLServer
 
622
        backing = self.get_transport()
 
623
        request = smart_dir.SmartServerRequestOpenBranch(backing)
 
624
        branch = self.make_branch('branch')
 
625
        checkout = branch.create_checkout('reference',lightweight=True)
 
626
        reference_url = _mod_branch.BranchReferenceFormat().get_reference(
 
627
            checkout.bzrdir)
 
628
        self.assertFileEqual(reference_url, 'reference/.bzr/branch/location')
 
629
        self.assertEqual(smart_req.SmartServerResponse(('ok', reference_url)),
 
630
            request.execute('reference'))
 
631
 
 
632
    def test_notification_on_branch_from_repository(self):
 
633
        """When there is a repository, the error should return details."""
 
634
        backing = self.get_transport()
 
635
        request = smart_dir.SmartServerRequestOpenBranch(backing)
 
636
        repo = self.make_repository('.')
 
637
        self.assertEqual(smart_req.SmartServerResponse(('nobranch',)),
 
638
            request.execute(''))
 
639
 
 
640
 
 
641
class TestSmartServerRequestOpenBranchV2(TestCaseWithChrootedTransport):
 
642
 
 
643
    def test_no_branch(self):
 
644
        """When there is no branch, ('nobranch', ) is returned."""
 
645
        backing = self.get_transport()
 
646
        self.make_bzrdir('.')
 
647
        request = smart_dir.SmartServerRequestOpenBranchV2(backing)
 
648
        self.assertEqual(smart_req.SmartServerResponse(('nobranch', )),
 
649
            request.execute(''))
 
650
 
 
651
    def test_branch(self):
 
652
        """When there is a branch, 'ok' is returned."""
 
653
        backing = self.get_transport()
 
654
        expected = self.make_branch('.')._format.network_name()
 
655
        request = smart_dir.SmartServerRequestOpenBranchV2(backing)
 
656
        self.assertEqual(smart_req.SuccessfulSmartServerResponse(
 
657
                ('branch', expected)),
 
658
                         request.execute(''))
 
659
 
 
660
    def test_branch_reference(self):
 
661
        """When there is a branch reference, the reference URL is returned."""
 
662
        self.vfs_transport_factory = test_server.LocalURLServer
 
663
        backing = self.get_transport()
 
664
        request = smart_dir.SmartServerRequestOpenBranchV2(backing)
 
665
        branch = self.make_branch('branch')
 
666
        checkout = branch.create_checkout('reference',lightweight=True)
 
667
        reference_url = _mod_branch.BranchReferenceFormat().get_reference(
 
668
            checkout.bzrdir)
 
669
        self.assertFileEqual(reference_url, 'reference/.bzr/branch/location')
 
670
        self.assertEqual(smart_req.SuccessfulSmartServerResponse(
 
671
                ('ref', reference_url)),
 
672
                         request.execute('reference'))
 
673
 
 
674
    def test_stacked_branch(self):
 
675
        """Opening a stacked branch does not open the stacked-on branch."""
 
676
        trunk = self.make_branch('trunk')
 
677
        feature = self.make_branch('feature')
 
678
        feature.set_stacked_on_url(trunk.base)
 
679
        opened_branches = []
 
680
        _mod_branch.Branch.hooks.install_named_hook(
 
681
            'open', opened_branches.append, None)
 
682
        backing = self.get_transport()
 
683
        request = smart_dir.SmartServerRequestOpenBranchV2(backing)
 
684
        request.setup_jail()
 
685
        try:
 
686
            response = request.execute('feature')
 
687
        finally:
 
688
            request.teardown_jail()
 
689
        expected_format = feature._format.network_name()
 
690
        self.assertEqual(smart_req.SuccessfulSmartServerResponse(
 
691
                ('branch', expected_format)),
 
692
                         response)
 
693
        self.assertLength(1, opened_branches)
 
694
 
 
695
    def test_notification_on_branch_from_repository(self):
 
696
        """When there is a repository, the error should return details."""
 
697
        backing = self.get_transport()
 
698
        request = smart_dir.SmartServerRequestOpenBranchV2(backing)
 
699
        repo = self.make_repository('.')
 
700
        self.assertEqual(smart_req.SmartServerResponse(('nobranch',)),
 
701
            request.execute(''))
 
702
 
 
703
 
 
704
class TestSmartServerRequestOpenBranchV3(TestCaseWithChrootedTransport):
 
705
 
 
706
    def test_no_branch(self):
 
707
        """When there is no branch, ('nobranch', ) is returned."""
 
708
        backing = self.get_transport()
 
709
        self.make_bzrdir('.')
 
710
        request = smart_dir.SmartServerRequestOpenBranchV3(backing)
 
711
        self.assertEqual(smart_req.SmartServerResponse(('nobranch',)),
 
712
            request.execute(''))
 
713
 
 
714
    def test_branch(self):
 
715
        """When there is a branch, 'ok' is returned."""
 
716
        backing = self.get_transport()
 
717
        expected = self.make_branch('.')._format.network_name()
 
718
        request = smart_dir.SmartServerRequestOpenBranchV3(backing)
 
719
        self.assertEqual(smart_req.SuccessfulSmartServerResponse(
 
720
                ('branch', expected)),
 
721
                         request.execute(''))
 
722
 
 
723
    def test_branch_reference(self):
 
724
        """When there is a branch reference, the reference URL is returned."""
 
725
        self.vfs_transport_factory = test_server.LocalURLServer
 
726
        backing = self.get_transport()
 
727
        request = smart_dir.SmartServerRequestOpenBranchV3(backing)
 
728
        branch = self.make_branch('branch')
 
729
        checkout = branch.create_checkout('reference',lightweight=True)
 
730
        reference_url = _mod_branch.BranchReferenceFormat().get_reference(
 
731
            checkout.bzrdir)
 
732
        self.assertFileEqual(reference_url, 'reference/.bzr/branch/location')
 
733
        self.assertEqual(smart_req.SuccessfulSmartServerResponse(
 
734
                ('ref', reference_url)),
 
735
                         request.execute('reference'))
 
736
 
 
737
    def test_stacked_branch(self):
 
738
        """Opening a stacked branch does not open the stacked-on branch."""
 
739
        trunk = self.make_branch('trunk')
 
740
        feature = self.make_branch('feature')
 
741
        feature.set_stacked_on_url(trunk.base)
 
742
        opened_branches = []
 
743
        _mod_branch.Branch.hooks.install_named_hook(
 
744
            'open', opened_branches.append, None)
 
745
        backing = self.get_transport()
 
746
        request = smart_dir.SmartServerRequestOpenBranchV3(backing)
 
747
        request.setup_jail()
 
748
        try:
 
749
            response = request.execute('feature')
 
750
        finally:
 
751
            request.teardown_jail()
 
752
        expected_format = feature._format.network_name()
 
753
        self.assertEqual(smart_req.SuccessfulSmartServerResponse(
 
754
                ('branch', expected_format)),
 
755
                         response)
 
756
        self.assertLength(1, opened_branches)
 
757
 
 
758
    def test_notification_on_branch_from_repository(self):
 
759
        """When there is a repository, the error should return details."""
 
760
        backing = self.get_transport()
 
761
        request = smart_dir.SmartServerRequestOpenBranchV3(backing)
 
762
        repo = self.make_repository('.')
 
763
        self.assertEqual(smart_req.SmartServerResponse(
 
764
                ('nobranch', 'location is a repository')),
 
765
                         request.execute(''))
 
766
 
 
767
 
 
768
class TestSmartServerRequestRevisionHistory(tests.TestCaseWithMemoryTransport):
 
769
 
 
770
    def test_empty(self):
 
771
        """For an empty branch, the body is empty."""
 
772
        backing = self.get_transport()
 
773
        request = smart_branch.SmartServerRequestRevisionHistory(backing)
 
774
        self.make_branch('.')
 
775
        self.assertEqual(smart_req.SmartServerResponse(('ok', ), ''),
 
776
            request.execute(''))
 
777
 
 
778
    def test_not_empty(self):
 
779
        """For a non-empty branch, the body is empty."""
 
780
        backing = self.get_transport()
 
781
        request = smart_branch.SmartServerRequestRevisionHistory(backing)
 
782
        tree = self.make_branch_and_memory_tree('.')
 
783
        tree.lock_write()
 
784
        tree.add('')
 
785
        r1 = tree.commit('1st commit')
 
786
        r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
 
787
        tree.unlock()
 
788
        self.assertEqual(
 
789
            smart_req.SmartServerResponse(('ok', ), ('\x00'.join([r1, r2]))),
 
790
            request.execute(''))
 
791
 
 
792
 
 
793
class TestSmartServerBranchRequest(tests.TestCaseWithMemoryTransport):
 
794
 
 
795
    def test_no_branch(self):
 
796
        """When there is a bzrdir and no branch, NotBranchError is raised."""
 
797
        backing = self.get_transport()
 
798
        request = smart_branch.SmartServerBranchRequest(backing)
 
799
        self.make_bzrdir('.')
 
800
        self.assertRaises(errors.NotBranchError,
 
801
            request.execute, '')
 
802
 
 
803
    def test_branch_reference(self):
 
804
        """When there is a branch reference, NotBranchError is raised."""
 
805
        backing = self.get_transport()
 
806
        request = smart_branch.SmartServerBranchRequest(backing)
 
807
        branch = self.make_branch('branch')
 
808
        checkout = branch.create_checkout('reference',lightweight=True)
 
809
        self.assertRaises(errors.NotBranchError,
 
810
            request.execute, 'checkout')
 
811
 
 
812
 
 
813
class TestSmartServerBranchRequestLastRevisionInfo(
 
814
    tests.TestCaseWithMemoryTransport):
 
815
 
 
816
    def test_empty(self):
 
817
        """For an empty branch, the result is ('ok', '0', 'null:')."""
 
818
        backing = self.get_transport()
 
819
        request = smart_branch.SmartServerBranchRequestLastRevisionInfo(backing)
 
820
        self.make_branch('.')
 
821
        self.assertEqual(smart_req.SmartServerResponse(('ok', '0', 'null:')),
 
822
            request.execute(''))
 
823
 
 
824
    def test_not_empty(self):
 
825
        """For a non-empty branch, the result is ('ok', 'revno', 'revid')."""
 
826
        backing = self.get_transport()
 
827
        request = smart_branch.SmartServerBranchRequestLastRevisionInfo(backing)
 
828
        tree = self.make_branch_and_memory_tree('.')
 
829
        tree.lock_write()
 
830
        tree.add('')
 
831
        rev_id_utf8 = u'\xc8'.encode('utf-8')
 
832
        r1 = tree.commit('1st commit')
 
833
        r2 = tree.commit('2nd commit', rev_id=rev_id_utf8)
 
834
        tree.unlock()
 
835
        self.assertEqual(
 
836
            smart_req.SmartServerResponse(('ok', '2', rev_id_utf8)),
 
837
            request.execute(''))
 
838
 
 
839
 
 
840
class TestSmartServerBranchRequestRevisionIdToRevno(
 
841
    tests.TestCaseWithMemoryTransport):
 
842
 
 
843
    def test_null(self):
 
844
        backing = self.get_transport()
 
845
        request = smart_branch.SmartServerBranchRequestRevisionIdToRevno(
 
846
            backing)
 
847
        self.make_branch('.')
 
848
        self.assertEqual(smart_req.SmartServerResponse(('ok', '0')),
 
849
            request.execute('', 'null:'))
 
850
 
 
851
    def test_simple(self):
 
852
        backing = self.get_transport()
 
853
        request = smart_branch.SmartServerBranchRequestRevisionIdToRevno(
 
854
            backing)
 
855
        tree = self.make_branch_and_memory_tree('.')
 
856
        tree.lock_write()
 
857
        tree.add('')
 
858
        r1 = tree.commit('1st commit')
 
859
        tree.unlock()
 
860
        self.assertEqual(
 
861
            smart_req.SmartServerResponse(('ok', '1')),
 
862
            request.execute('', r1))
 
863
 
 
864
    def test_not_found(self):
 
865
        backing = self.get_transport()
 
866
        request = smart_branch.SmartServerBranchRequestRevisionIdToRevno(
 
867
            backing)
 
868
        branch = self.make_branch('.')
 
869
        self.assertEqual(
 
870
            smart_req.FailedSmartServerResponse(
 
871
                ('NoSuchRevision', 'idontexist')),
 
872
            request.execute('', 'idontexist'))
 
873
 
 
874
 
 
875
class TestSmartServerBranchRequestGetConfigFile(
 
876
    tests.TestCaseWithMemoryTransport):
 
877
 
 
878
    def test_default(self):
 
879
        """With no file, we get empty content."""
 
880
        backing = self.get_transport()
 
881
        request = smart_branch.SmartServerBranchGetConfigFile(backing)
 
882
        branch = self.make_branch('.')
 
883
        # there should be no file by default
 
884
        content = ''
 
885
        self.assertEqual(smart_req.SmartServerResponse(('ok', ), content),
 
886
            request.execute(''))
 
887
 
 
888
    def test_with_content(self):
 
889
        # SmartServerBranchGetConfigFile should return the content from
 
890
        # branch.control_files.get('branch.conf') for now - in the future it may
 
891
        # perform more complex processing.
 
892
        backing = self.get_transport()
 
893
        request = smart_branch.SmartServerBranchGetConfigFile(backing)
 
894
        branch = self.make_branch('.')
 
895
        branch._transport.put_bytes('branch.conf', 'foo bar baz')
 
896
        self.assertEqual(smart_req.SmartServerResponse(('ok', ), 'foo bar baz'),
 
897
            request.execute(''))
 
898
 
 
899
 
 
900
class TestLockedBranch(tests.TestCaseWithMemoryTransport):
 
901
 
 
902
    def get_lock_tokens(self, branch):
 
903
        branch_token = branch.lock_write().branch_token
 
904
        repo_token = branch.repository.lock_write().repository_token
 
905
        branch.repository.unlock()
 
906
        return branch_token, repo_token
 
907
 
 
908
 
 
909
class TestSmartServerBranchRequestPutConfigFile(TestLockedBranch):
 
910
 
 
911
    def test_with_content(self):
 
912
        backing = self.get_transport()
 
913
        request = smart_branch.SmartServerBranchPutConfigFile(backing)
 
914
        branch = self.make_branch('.')
 
915
        branch_token, repo_token = self.get_lock_tokens(branch)
 
916
        self.assertIs(None, request.execute('', branch_token, repo_token))
 
917
        self.assertEqual(
 
918
            smart_req.SmartServerResponse(('ok', )),
 
919
            request.do_body('foo bar baz'))
 
920
        self.assertEquals(
 
921
            branch.control_transport.get_bytes('branch.conf'),
 
922
            'foo bar baz')
 
923
        branch.unlock()
 
924
 
 
925
 
 
926
class TestSmartServerBranchRequestSetConfigOption(TestLockedBranch):
 
927
 
 
928
    def test_value_name(self):
 
929
        branch = self.make_branch('.')
 
930
        request = smart_branch.SmartServerBranchRequestSetConfigOption(
 
931
            branch.bzrdir.root_transport)
 
932
        branch_token, repo_token = self.get_lock_tokens(branch)
 
933
        config = branch._get_config()
 
934
        result = request.execute('', branch_token, repo_token, 'bar', 'foo',
 
935
            '')
 
936
        self.assertEqual(smart_req.SuccessfulSmartServerResponse(()), result)
 
937
        self.assertEqual('bar', config.get_option('foo'))
 
938
        # Cleanup
 
939
        branch.unlock()
 
940
 
 
941
    def test_value_name_section(self):
 
942
        branch = self.make_branch('.')
 
943
        request = smart_branch.SmartServerBranchRequestSetConfigOption(
 
944
            branch.bzrdir.root_transport)
 
945
        branch_token, repo_token = self.get_lock_tokens(branch)
 
946
        config = branch._get_config()
 
947
        result = request.execute('', branch_token, repo_token, 'bar', 'foo',
 
948
            'gam')
 
949
        self.assertEqual(smart_req.SuccessfulSmartServerResponse(()), result)
 
950
        self.assertEqual('bar', config.get_option('foo', 'gam'))
 
951
        # Cleanup
 
952
        branch.unlock()
 
953
 
 
954
 
 
955
class TestSmartServerBranchRequestSetConfigOptionDict(TestLockedBranch):
 
956
 
 
957
    def setUp(self):
 
958
        TestLockedBranch.setUp(self)
 
959
        # A dict with non-ascii keys and values to exercise unicode
 
960
        # roundtripping.
 
961
        self.encoded_value_dict = (
 
962
            'd5:ascii1:a11:unicode \xe2\x8c\x9a3:\xe2\x80\xbde')
 
963
        self.value_dict = {
 
964
            'ascii': 'a', u'unicode \N{WATCH}': u'\N{INTERROBANG}'}
 
965
 
 
966
    def test_value_name(self):
 
967
        branch = self.make_branch('.')
 
968
        request = smart_branch.SmartServerBranchRequestSetConfigOptionDict(
 
969
            branch.bzrdir.root_transport)
 
970
        branch_token, repo_token = self.get_lock_tokens(branch)
 
971
        config = branch._get_config()
 
972
        result = request.execute('', branch_token, repo_token,
 
973
            self.encoded_value_dict, 'foo', '')
 
974
        self.assertEqual(smart_req.SuccessfulSmartServerResponse(()), result)
 
975
        self.assertEqual(self.value_dict, config.get_option('foo'))
 
976
        # Cleanup
 
977
        branch.unlock()
 
978
 
 
979
    def test_value_name_section(self):
 
980
        branch = self.make_branch('.')
 
981
        request = smart_branch.SmartServerBranchRequestSetConfigOptionDict(
 
982
            branch.bzrdir.root_transport)
 
983
        branch_token, repo_token = self.get_lock_tokens(branch)
 
984
        config = branch._get_config()
 
985
        result = request.execute('', branch_token, repo_token,
 
986
            self.encoded_value_dict, 'foo', 'gam')
 
987
        self.assertEqual(smart_req.SuccessfulSmartServerResponse(()), result)
 
988
        self.assertEqual(self.value_dict, config.get_option('foo', 'gam'))
 
989
        # Cleanup
 
990
        branch.unlock()
 
991
 
 
992
 
 
993
class TestSmartServerBranchRequestSetTagsBytes(TestLockedBranch):
 
994
    # Only called when the branch format and tags match [yay factory
 
995
    # methods] so only need to test straight forward cases.
 
996
 
 
997
    def test_set_bytes(self):
 
998
        base_branch = self.make_branch('base')
 
999
        tag_bytes = base_branch._get_tags_bytes()
 
1000
        # get_lock_tokens takes out a lock.
 
1001
        branch_token, repo_token = self.get_lock_tokens(base_branch)
 
1002
        request = smart_branch.SmartServerBranchSetTagsBytes(
 
1003
            self.get_transport())
 
1004
        response = request.execute('base', branch_token, repo_token)
 
1005
        self.assertEqual(None, response)
 
1006
        response = request.do_chunk(tag_bytes)
 
1007
        self.assertEqual(None, response)
 
1008
        response = request.do_end()
 
1009
        self.assertEquals(
 
1010
            smart_req.SuccessfulSmartServerResponse(()), response)
 
1011
        base_branch.unlock()
 
1012
 
 
1013
    def test_lock_failed(self):
 
1014
        base_branch = self.make_branch('base')
 
1015
        base_branch.lock_write()
 
1016
        tag_bytes = base_branch._get_tags_bytes()
 
1017
        request = smart_branch.SmartServerBranchSetTagsBytes(
 
1018
            self.get_transport())
 
1019
        self.assertRaises(errors.TokenMismatch, request.execute,
 
1020
            'base', 'wrong token', 'wrong token')
 
1021
        # The request handler will keep processing the message parts, so even
 
1022
        # if the request fails immediately do_chunk and do_end are still
 
1023
        # called.
 
1024
        request.do_chunk(tag_bytes)
 
1025
        request.do_end()
 
1026
        base_branch.unlock()
 
1027
 
 
1028
 
 
1029
 
 
1030
class SetLastRevisionTestBase(TestLockedBranch):
 
1031
    """Base test case for verbs that implement set_last_revision."""
 
1032
 
 
1033
    def setUp(self):
 
1034
        tests.TestCaseWithMemoryTransport.setUp(self)
 
1035
        backing_transport = self.get_transport()
 
1036
        self.request = self.request_class(backing_transport)
 
1037
        self.tree = self.make_branch_and_memory_tree('.')
 
1038
 
 
1039
    def lock_branch(self):
 
1040
        return self.get_lock_tokens(self.tree.branch)
 
1041
 
 
1042
    def unlock_branch(self):
 
1043
        self.tree.branch.unlock()
 
1044
 
 
1045
    def set_last_revision(self, revision_id, revno):
 
1046
        branch_token, repo_token = self.lock_branch()
 
1047
        response = self._set_last_revision(
 
1048
            revision_id, revno, branch_token, repo_token)
 
1049
        self.unlock_branch()
 
1050
        return response
 
1051
 
 
1052
    def assertRequestSucceeds(self, revision_id, revno):
 
1053
        response = self.set_last_revision(revision_id, revno)
 
1054
        self.assertEqual(smart_req.SuccessfulSmartServerResponse(('ok',)),
 
1055
                         response)
 
1056
 
 
1057
 
 
1058
class TestSetLastRevisionVerbMixin(object):
 
1059
    """Mixin test case for verbs that implement set_last_revision."""
 
1060
 
 
1061
    def test_set_null_to_null(self):
 
1062
        """An empty branch can have its last revision set to 'null:'."""
 
1063
        self.assertRequestSucceeds('null:', 0)
 
1064
 
 
1065
    def test_NoSuchRevision(self):
 
1066
        """If the revision_id is not present, the verb returns NoSuchRevision.
 
1067
        """
 
1068
        revision_id = 'non-existent revision'
 
1069
        self.assertEqual(smart_req.FailedSmartServerResponse(('NoSuchRevision',
 
1070
                                                              revision_id)),
 
1071
                         self.set_last_revision(revision_id, 1))
 
1072
 
 
1073
    def make_tree_with_two_commits(self):
 
1074
        self.tree.lock_write()
 
1075
        self.tree.add('')
 
1076
        rev_id_utf8 = u'\xc8'.encode('utf-8')
 
1077
        r1 = self.tree.commit('1st commit', rev_id=rev_id_utf8)
 
1078
        r2 = self.tree.commit('2nd commit', rev_id='rev-2')
 
1079
        self.tree.unlock()
 
1080
 
 
1081
    def test_branch_last_revision_info_is_updated(self):
 
1082
        """A branch's tip can be set to a revision that is present in its
 
1083
        repository.
 
1084
        """
 
1085
        # Make a branch with an empty revision history, but two revisions in
 
1086
        # its repository.
 
1087
        self.make_tree_with_two_commits()
 
1088
        rev_id_utf8 = u'\xc8'.encode('utf-8')
 
1089
        self.tree.branch.set_last_revision_info(0, 'null:')
 
1090
        self.assertEqual(
 
1091
            (0, 'null:'), self.tree.branch.last_revision_info())
 
1092
        # We can update the branch to a revision that is present in the
 
1093
        # repository.
 
1094
        self.assertRequestSucceeds(rev_id_utf8, 1)
 
1095
        self.assertEqual(
 
1096
            (1, rev_id_utf8), self.tree.branch.last_revision_info())
 
1097
 
 
1098
    def test_branch_last_revision_info_rewind(self):
 
1099
        """A branch's tip can be set to a revision that is an ancestor of the
 
1100
        current tip.
 
1101
        """
 
1102
        self.make_tree_with_two_commits()
 
1103
        rev_id_utf8 = u'\xc8'.encode('utf-8')
 
1104
        self.assertEqual(
 
1105
            (2, 'rev-2'), self.tree.branch.last_revision_info())
 
1106
        self.assertRequestSucceeds(rev_id_utf8, 1)
 
1107
        self.assertEqual(
 
1108
            (1, rev_id_utf8), self.tree.branch.last_revision_info())
 
1109
 
 
1110
    def test_TipChangeRejected(self):
 
1111
        """If a pre_change_branch_tip hook raises TipChangeRejected, the verb
 
1112
        returns TipChangeRejected.
 
1113
        """
 
1114
        rejection_message = u'rejection message\N{INTERROBANG}'
 
1115
        def hook_that_rejects(params):
 
1116
            raise errors.TipChangeRejected(rejection_message)
 
1117
        _mod_branch.Branch.hooks.install_named_hook(
 
1118
            'pre_change_branch_tip', hook_that_rejects, None)
 
1119
        self.assertEqual(
 
1120
            smart_req.FailedSmartServerResponse(
 
1121
                ('TipChangeRejected', rejection_message.encode('utf-8'))),
 
1122
            self.set_last_revision('null:', 0))
 
1123
 
 
1124
 
 
1125
class TestSmartServerBranchRequestSetLastRevision(
 
1126
        SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
 
1127
    """Tests for Branch.set_last_revision verb."""
 
1128
 
 
1129
    request_class = smart_branch.SmartServerBranchRequestSetLastRevision
 
1130
 
 
1131
    def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
 
1132
        return self.request.execute(
 
1133
            '', branch_token, repo_token, revision_id)
 
1134
 
 
1135
 
 
1136
class TestSmartServerBranchRequestSetLastRevisionInfo(
 
1137
        SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
 
1138
    """Tests for Branch.set_last_revision_info verb."""
 
1139
 
 
1140
    request_class = smart_branch.SmartServerBranchRequestSetLastRevisionInfo
 
1141
 
 
1142
    def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
 
1143
        return self.request.execute(
 
1144
            '', branch_token, repo_token, revno, revision_id)
 
1145
 
 
1146
    def test_NoSuchRevision(self):
 
1147
        """Branch.set_last_revision_info does not have to return
 
1148
        NoSuchRevision if the revision_id is absent.
 
1149
        """
 
1150
        raise tests.TestNotApplicable()
 
1151
 
 
1152
 
 
1153
class TestSmartServerBranchRequestSetLastRevisionEx(
 
1154
        SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
 
1155
    """Tests for Branch.set_last_revision_ex verb."""
 
1156
 
 
1157
    request_class = smart_branch.SmartServerBranchRequestSetLastRevisionEx
 
1158
 
 
1159
    def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
 
1160
        return self.request.execute(
 
1161
            '', branch_token, repo_token, revision_id, 0, 0)
 
1162
 
 
1163
    def assertRequestSucceeds(self, revision_id, revno):
 
1164
        response = self.set_last_revision(revision_id, revno)
 
1165
        self.assertEqual(
 
1166
            smart_req.SuccessfulSmartServerResponse(('ok', revno, revision_id)),
 
1167
            response)
 
1168
 
 
1169
    def test_branch_last_revision_info_rewind(self):
 
1170
        """A branch's tip can be set to a revision that is an ancestor of the
 
1171
        current tip, but only if allow_overwrite_descendant is passed.
 
1172
        """
 
1173
        self.make_tree_with_two_commits()
 
1174
        rev_id_utf8 = u'\xc8'.encode('utf-8')
 
1175
        self.assertEqual(
 
1176
            (2, 'rev-2'), self.tree.branch.last_revision_info())
 
1177
        # If allow_overwrite_descendant flag is 0, then trying to set the tip
 
1178
        # to an older revision ID has no effect.
 
1179
        branch_token, repo_token = self.lock_branch()
 
1180
        response = self.request.execute(
 
1181
            '', branch_token, repo_token, rev_id_utf8, 0, 0)
 
1182
        self.assertEqual(
 
1183
            smart_req.SuccessfulSmartServerResponse(('ok', 2, 'rev-2')),
 
1184
            response)
 
1185
        self.assertEqual(
 
1186
            (2, 'rev-2'), self.tree.branch.last_revision_info())
 
1187
 
 
1188
        # If allow_overwrite_descendant flag is 1, then setting the tip to an
 
1189
        # ancestor works.
 
1190
        response = self.request.execute(
 
1191
            '', branch_token, repo_token, rev_id_utf8, 0, 1)
 
1192
        self.assertEqual(
 
1193
            smart_req.SuccessfulSmartServerResponse(('ok', 1, rev_id_utf8)),
 
1194
            response)
 
1195
        self.unlock_branch()
 
1196
        self.assertEqual(
 
1197
            (1, rev_id_utf8), self.tree.branch.last_revision_info())
 
1198
 
 
1199
    def make_branch_with_divergent_history(self):
 
1200
        """Make a branch with divergent history in its repo.
 
1201
 
 
1202
        The branch's tip will be 'child-2', and the repo will also contain
 
1203
        'child-1', which diverges from a common base revision.
 
1204
        """
 
1205
        self.tree.lock_write()
 
1206
        self.tree.add('')
 
1207
        r1 = self.tree.commit('1st commit')
 
1208
        revno_1, revid_1 = self.tree.branch.last_revision_info()
 
1209
        r2 = self.tree.commit('2nd commit', rev_id='child-1')
 
1210
        # Undo the second commit
 
1211
        self.tree.branch.set_last_revision_info(revno_1, revid_1)
 
1212
        self.tree.set_parent_ids([revid_1])
 
1213
        # Make a new second commit, child-2.  child-2 has diverged from
 
1214
        # child-1.
 
1215
        new_r2 = self.tree.commit('2nd commit', rev_id='child-2')
 
1216
        self.tree.unlock()
 
1217
 
 
1218
    def test_not_allow_diverged(self):
 
1219
        """If allow_diverged is not passed, then setting a divergent history
 
1220
        returns a Diverged error.
 
1221
        """
 
1222
        self.make_branch_with_divergent_history()
 
1223
        self.assertEqual(
 
1224
            smart_req.FailedSmartServerResponse(('Diverged',)),
 
1225
            self.set_last_revision('child-1', 2))
 
1226
        # The branch tip was not changed.
 
1227
        self.assertEqual('child-2', self.tree.branch.last_revision())
 
1228
 
 
1229
    def test_allow_diverged(self):
 
1230
        """If allow_diverged is passed, then setting a divergent history
 
1231
        succeeds.
 
1232
        """
 
1233
        self.make_branch_with_divergent_history()
 
1234
        branch_token, repo_token = self.lock_branch()
 
1235
        response = self.request.execute(
 
1236
            '', branch_token, repo_token, 'child-1', 1, 0)
 
1237
        self.assertEqual(
 
1238
            smart_req.SuccessfulSmartServerResponse(('ok', 2, 'child-1')),
 
1239
            response)
 
1240
        self.unlock_branch()
 
1241
        # The branch tip was changed.
 
1242
        self.assertEqual('child-1', self.tree.branch.last_revision())
 
1243
 
 
1244
 
 
1245
class TestSmartServerBranchBreakLock(tests.TestCaseWithMemoryTransport):
 
1246
 
 
1247
    def test_lock_to_break(self):
 
1248
        base_branch = self.make_branch('base')
 
1249
        request = smart_branch.SmartServerBranchBreakLock(
 
1250
            self.get_transport())
 
1251
        base_branch.lock_write()
 
1252
        self.assertEqual(
 
1253
            smart_req.SuccessfulSmartServerResponse(('ok', ), None),
 
1254
            request.execute('base'))
 
1255
 
 
1256
    def test_nothing_to_break(self):
 
1257
        base_branch = self.make_branch('base')
 
1258
        request = smart_branch.SmartServerBranchBreakLock(
 
1259
            self.get_transport())
 
1260
        self.assertEqual(
 
1261
            smart_req.SuccessfulSmartServerResponse(('ok', ), None),
 
1262
            request.execute('base'))
 
1263
 
 
1264
 
 
1265
class TestSmartServerBranchRequestGetParent(tests.TestCaseWithMemoryTransport):
 
1266
 
 
1267
    def test_get_parent_none(self):
 
1268
        base_branch = self.make_branch('base')
 
1269
        request = smart_branch.SmartServerBranchGetParent(self.get_transport())
 
1270
        response = request.execute('base')
 
1271
        self.assertEquals(
 
1272
            smart_req.SuccessfulSmartServerResponse(('',)), response)
 
1273
 
 
1274
    def test_get_parent_something(self):
 
1275
        base_branch = self.make_branch('base')
 
1276
        base_branch.set_parent(self.get_url('foo'))
 
1277
        request = smart_branch.SmartServerBranchGetParent(self.get_transport())
 
1278
        response = request.execute('base')
 
1279
        self.assertEquals(
 
1280
            smart_req.SuccessfulSmartServerResponse(("../foo",)),
 
1281
            response)
 
1282
 
 
1283
 
 
1284
class TestSmartServerBranchRequestSetParent(TestLockedBranch):
 
1285
 
 
1286
    def test_set_parent_none(self):
 
1287
        branch = self.make_branch('base', format="1.9")
 
1288
        branch.lock_write()
 
1289
        branch._set_parent_location('foo')
 
1290
        branch.unlock()
 
1291
        request = smart_branch.SmartServerBranchRequestSetParentLocation(
 
1292
            self.get_transport())
 
1293
        branch_token, repo_token = self.get_lock_tokens(branch)
 
1294
        try:
 
1295
            response = request.execute('base', branch_token, repo_token, '')
 
1296
        finally:
 
1297
            branch.unlock()
 
1298
        self.assertEqual(smart_req.SuccessfulSmartServerResponse(()), response)
 
1299
        # Refresh branch as SetParentLocation modified it
 
1300
        branch = branch.bzrdir.open_branch()
 
1301
        self.assertEqual(None, branch.get_parent())
 
1302
 
 
1303
    def test_set_parent_something(self):
 
1304
        branch = self.make_branch('base', format="1.9")
 
1305
        request = smart_branch.SmartServerBranchRequestSetParentLocation(
 
1306
            self.get_transport())
 
1307
        branch_token, repo_token = self.get_lock_tokens(branch)
 
1308
        try:
 
1309
            response = request.execute('base', branch_token, repo_token,
 
1310
                                       'http://bar/')
 
1311
        finally:
 
1312
            branch.unlock()
 
1313
        self.assertEqual(smart_req.SuccessfulSmartServerResponse(()), response)
 
1314
        refreshed = _mod_branch.Branch.open(branch.base)
 
1315
        self.assertEqual('http://bar/', refreshed.get_parent())
 
1316
 
 
1317
 
 
1318
class TestSmartServerBranchRequestGetTagsBytes(
 
1319
    tests.TestCaseWithMemoryTransport):
 
1320
    # Only called when the branch format and tags match [yay factory
 
1321
    # methods] so only need to test straight forward cases.
 
1322
 
 
1323
    def test_get_bytes(self):
 
1324
        base_branch = self.make_branch('base')
 
1325
        request = smart_branch.SmartServerBranchGetTagsBytes(
 
1326
            self.get_transport())
 
1327
        response = request.execute('base')
 
1328
        self.assertEquals(
 
1329
            smart_req.SuccessfulSmartServerResponse(('',)), response)
 
1330
 
 
1331
 
 
1332
class TestSmartServerBranchRequestGetStackedOnURL(tests.TestCaseWithMemoryTransport):
 
1333
 
 
1334
    def test_get_stacked_on_url(self):
 
1335
        base_branch = self.make_branch('base', format='1.6')
 
1336
        stacked_branch = self.make_branch('stacked', format='1.6')
 
1337
        # typically should be relative
 
1338
        stacked_branch.set_stacked_on_url('../base')
 
1339
        request = smart_branch.SmartServerBranchRequestGetStackedOnURL(
 
1340
            self.get_transport())
 
1341
        response = request.execute('stacked')
 
1342
        self.assertEquals(
 
1343
            smart_req.SmartServerResponse(('ok', '../base')),
 
1344
            response)
 
1345
 
 
1346
 
 
1347
class TestSmartServerBranchRequestLockWrite(TestLockedBranch):
 
1348
 
 
1349
    def setUp(self):
 
1350
        tests.TestCaseWithMemoryTransport.setUp(self)
 
1351
 
 
1352
    def test_lock_write_on_unlocked_branch(self):
 
1353
        backing = self.get_transport()
 
1354
        request = smart_branch.SmartServerBranchRequestLockWrite(backing)
 
1355
        branch = self.make_branch('.', format='knit')
 
1356
        repository = branch.repository
 
1357
        response = request.execute('')
 
1358
        branch_nonce = branch.control_files._lock.peek().get('nonce')
 
1359
        repository_nonce = repository.control_files._lock.peek().get('nonce')
 
1360
        self.assertEqual(smart_req.SmartServerResponse(
 
1361
                ('ok', branch_nonce, repository_nonce)),
 
1362
                         response)
 
1363
        # The branch (and associated repository) is now locked.  Verify that
 
1364
        # with a new branch object.
 
1365
        new_branch = repository.bzrdir.open_branch()
 
1366
        self.assertRaises(errors.LockContention, new_branch.lock_write)
 
1367
        # Cleanup
 
1368
        request = smart_branch.SmartServerBranchRequestUnlock(backing)
 
1369
        response = request.execute('', branch_nonce, repository_nonce)
 
1370
 
 
1371
    def test_lock_write_on_locked_branch(self):
 
1372
        backing = self.get_transport()
 
1373
        request = smart_branch.SmartServerBranchRequestLockWrite(backing)
 
1374
        branch = self.make_branch('.')
 
1375
        branch_token = branch.lock_write().branch_token
 
1376
        branch.leave_lock_in_place()
 
1377
        branch.unlock()
 
1378
        response = request.execute('')
 
1379
        self.assertEqual(
 
1380
            smart_req.SmartServerResponse(('LockContention',)), response)
 
1381
        # Cleanup
 
1382
        branch.lock_write(branch_token)
 
1383
        branch.dont_leave_lock_in_place()
 
1384
        branch.unlock()
 
1385
 
 
1386
    def test_lock_write_with_tokens_on_locked_branch(self):
 
1387
        backing = self.get_transport()
 
1388
        request = smart_branch.SmartServerBranchRequestLockWrite(backing)
 
1389
        branch = self.make_branch('.', format='knit')
 
1390
        branch_token, repo_token = self.get_lock_tokens(branch)
 
1391
        branch.leave_lock_in_place()
 
1392
        branch.repository.leave_lock_in_place()
 
1393
        branch.unlock()
 
1394
        response = request.execute('',
 
1395
                                   branch_token, repo_token)
 
1396
        self.assertEqual(
 
1397
            smart_req.SmartServerResponse(('ok', branch_token, repo_token)),
 
1398
            response)
 
1399
        # Cleanup
 
1400
        branch.repository.lock_write(repo_token)
 
1401
        branch.repository.dont_leave_lock_in_place()
 
1402
        branch.repository.unlock()
 
1403
        branch.lock_write(branch_token)
 
1404
        branch.dont_leave_lock_in_place()
 
1405
        branch.unlock()
 
1406
 
 
1407
    def test_lock_write_with_mismatched_tokens_on_locked_branch(self):
 
1408
        backing = self.get_transport()
 
1409
        request = smart_branch.SmartServerBranchRequestLockWrite(backing)
 
1410
        branch = self.make_branch('.', format='knit')
 
1411
        branch_token, repo_token = self.get_lock_tokens(branch)
 
1412
        branch.leave_lock_in_place()
 
1413
        branch.repository.leave_lock_in_place()
 
1414
        branch.unlock()
 
1415
        response = request.execute('',
 
1416
                                   branch_token+'xxx', repo_token)
 
1417
        self.assertEqual(
 
1418
            smart_req.SmartServerResponse(('TokenMismatch',)), response)
 
1419
        # Cleanup
 
1420
        branch.repository.lock_write(repo_token)
 
1421
        branch.repository.dont_leave_lock_in_place()
 
1422
        branch.repository.unlock()
 
1423
        branch.lock_write(branch_token)
 
1424
        branch.dont_leave_lock_in_place()
 
1425
        branch.unlock()
 
1426
 
 
1427
    def test_lock_write_on_locked_repo(self):
 
1428
        backing = self.get_transport()
 
1429
        request = smart_branch.SmartServerBranchRequestLockWrite(backing)
 
1430
        branch = self.make_branch('.', format='knit')
 
1431
        repo = branch.repository
 
1432
        repo_token = repo.lock_write().repository_token
 
1433
        repo.leave_lock_in_place()
 
1434
        repo.unlock()
 
1435
        response = request.execute('')
 
1436
        self.assertEqual(
 
1437
            smart_req.SmartServerResponse(('LockContention',)), response)
 
1438
        # Cleanup
 
1439
        repo.lock_write(repo_token)
 
1440
        repo.dont_leave_lock_in_place()
 
1441
        repo.unlock()
 
1442
 
 
1443
    def test_lock_write_on_readonly_transport(self):
 
1444
        backing = self.get_readonly_transport()
 
1445
        request = smart_branch.SmartServerBranchRequestLockWrite(backing)
 
1446
        branch = self.make_branch('.')
 
1447
        root = self.get_transport().clone('/')
 
1448
        path = urlutils.relative_url(root.base, self.get_transport().base)
 
1449
        response = request.execute(path)
 
1450
        error_name, lock_str, why_str = response.args
 
1451
        self.assertFalse(response.is_successful())
 
1452
        self.assertEqual('LockFailed', error_name)
 
1453
 
 
1454
 
 
1455
class TestSmartServerBranchRequestGetPhysicalLockStatus(TestLockedBranch):
 
1456
 
 
1457
    def setUp(self):
 
1458
        tests.TestCaseWithMemoryTransport.setUp(self)
 
1459
 
 
1460
    def test_true(self):
 
1461
        backing = self.get_transport()
 
1462
        request = smart_branch.SmartServerBranchRequestGetPhysicalLockStatus(
 
1463
            backing)
 
1464
        branch = self.make_branch('.')
 
1465
        branch_token, repo_token = self.get_lock_tokens(branch)
 
1466
        self.assertEquals(True, branch.get_physical_lock_status())
 
1467
        response = request.execute('')
 
1468
        self.assertEqual(
 
1469
            smart_req.SmartServerResponse(('yes',)), response)
 
1470
        branch.unlock()
 
1471
 
 
1472
    def test_false(self):
 
1473
        backing = self.get_transport()
 
1474
        request = smart_branch.SmartServerBranchRequestGetPhysicalLockStatus(
 
1475
            backing)
 
1476
        branch = self.make_branch('.')
 
1477
        self.assertEquals(False, branch.get_physical_lock_status())
 
1478
        response = request.execute('')
 
1479
        self.assertEqual(
 
1480
            smart_req.SmartServerResponse(('no',)), response)
 
1481
 
 
1482
 
 
1483
class TestSmartServerBranchRequestUnlock(TestLockedBranch):
 
1484
 
 
1485
    def setUp(self):
 
1486
        tests.TestCaseWithMemoryTransport.setUp(self)
 
1487
 
 
1488
    def test_unlock_on_locked_branch_and_repo(self):
 
1489
        backing = self.get_transport()
 
1490
        request = smart_branch.SmartServerBranchRequestUnlock(backing)
 
1491
        branch = self.make_branch('.', format='knit')
 
1492
        # Lock the branch
 
1493
        branch_token, repo_token = self.get_lock_tokens(branch)
 
1494
        # Unlock the branch (and repo) object, leaving the physical locks
 
1495
        # in place.
 
1496
        branch.leave_lock_in_place()
 
1497
        branch.repository.leave_lock_in_place()
 
1498
        branch.unlock()
 
1499
        response = request.execute('',
 
1500
                                   branch_token, repo_token)
 
1501
        self.assertEqual(
 
1502
            smart_req.SmartServerResponse(('ok',)), response)
 
1503
        # The branch is now unlocked.  Verify that with a new branch
 
1504
        # object.
 
1505
        new_branch = branch.bzrdir.open_branch()
 
1506
        new_branch.lock_write()
 
1507
        new_branch.unlock()
 
1508
 
 
1509
    def test_unlock_on_unlocked_branch_unlocked_repo(self):
 
1510
        backing = self.get_transport()
 
1511
        request = smart_branch.SmartServerBranchRequestUnlock(backing)
 
1512
        branch = self.make_branch('.', format='knit')
 
1513
        response = request.execute(
 
1514
            '', 'branch token', 'repo token')
 
1515
        self.assertEqual(
 
1516
            smart_req.SmartServerResponse(('TokenMismatch',)), response)
 
1517
 
 
1518
    def test_unlock_on_unlocked_branch_locked_repo(self):
 
1519
        backing = self.get_transport()
 
1520
        request = smart_branch.SmartServerBranchRequestUnlock(backing)
 
1521
        branch = self.make_branch('.', format='knit')
 
1522
        # Lock the repository.
 
1523
        repo_token = branch.repository.lock_write().repository_token
 
1524
        branch.repository.leave_lock_in_place()
 
1525
        branch.repository.unlock()
 
1526
        # Issue branch lock_write request on the unlocked branch (with locked
 
1527
        # repo).
 
1528
        response = request.execute('', 'branch token', repo_token)
 
1529
        self.assertEqual(
 
1530
            smart_req.SmartServerResponse(('TokenMismatch',)), response)
 
1531
        # Cleanup
 
1532
        branch.repository.lock_write(repo_token)
 
1533
        branch.repository.dont_leave_lock_in_place()
 
1534
        branch.repository.unlock()
 
1535
 
 
1536
 
 
1537
class TestSmartServerRepositoryRequest(tests.TestCaseWithMemoryTransport):
 
1538
 
 
1539
    def test_no_repository(self):
 
1540
        """Raise NoRepositoryPresent when there is a bzrdir and no repo."""
 
1541
        # we test this using a shared repository above the named path,
 
1542
        # thus checking the right search logic is used - that is, that
 
1543
        # its the exact path being looked at and the server is not
 
1544
        # searching.
 
1545
        backing = self.get_transport()
 
1546
        request = smart_repo.SmartServerRepositoryRequest(backing)
 
1547
        self.make_repository('.', shared=True)
 
1548
        self.make_bzrdir('subdir')
 
1549
        self.assertRaises(errors.NoRepositoryPresent,
 
1550
            request.execute, 'subdir')
 
1551
 
 
1552
 
 
1553
class TestSmartServerRepositoryAddSignatureText(tests.TestCaseWithMemoryTransport):
 
1554
 
 
1555
    def test_add_text(self):
 
1556
        backing = self.get_transport()
 
1557
        request = smart_repo.SmartServerRepositoryAddSignatureText(backing)
 
1558
        tree = self.make_branch_and_memory_tree('.')
 
1559
        write_token = tree.lock_write()
 
1560
        self.addCleanup(tree.unlock)
 
1561
        tree.add('')
 
1562
        tree.commit("Message", rev_id='rev1')
 
1563
        tree.branch.repository.start_write_group()
 
1564
        write_group_tokens = tree.branch.repository.suspend_write_group()
 
1565
        self.assertEqual(None, request.execute('', write_token,
 
1566
            'rev1', *write_group_tokens))
 
1567
        response = request.do_body('somesignature')
 
1568
        self.assertTrue(response.is_successful())
 
1569
        self.assertEqual(response.args[0], 'ok')
 
1570
        write_group_tokens = response.args[1:]
 
1571
        tree.branch.repository.resume_write_group(write_group_tokens)
 
1572
        tree.branch.repository.commit_write_group()
 
1573
        tree.unlock()
 
1574
        self.assertEqual("somesignature",
 
1575
            tree.branch.repository.get_signature_text("rev1"))
 
1576
 
 
1577
 
 
1578
class TestSmartServerRepositoryAllRevisionIds(
 
1579
    tests.TestCaseWithMemoryTransport):
 
1580
 
 
1581
    def test_empty(self):
 
1582
        """An empty body should be returned for an empty repository."""
 
1583
        backing = self.get_transport()
 
1584
        request = smart_repo.SmartServerRepositoryAllRevisionIds(backing)
 
1585
        self.make_repository('.')
 
1586
        self.assertEquals(
 
1587
            smart_req.SuccessfulSmartServerResponse(("ok", ), ""),
 
1588
            request.execute(''))
 
1589
 
 
1590
    def test_some_revisions(self):
 
1591
        """An empty body should be returned for an empty repository."""
 
1592
        backing = self.get_transport()
 
1593
        request = smart_repo.SmartServerRepositoryAllRevisionIds(backing)
 
1594
        tree = self.make_branch_and_memory_tree('.')
 
1595
        tree.lock_write()
 
1596
        tree.add('')
 
1597
        tree.commit(rev_id='origineel', message="message")
 
1598
        tree.commit(rev_id='nog-een-revisie', message="message")
 
1599
        tree.unlock()
 
1600
        self.assertEquals(
 
1601
            smart_req.SuccessfulSmartServerResponse(("ok", ),
 
1602
                "origineel\nnog-een-revisie"),
 
1603
            request.execute(''))
 
1604
 
 
1605
 
 
1606
class TestSmartServerRepositoryBreakLock(tests.TestCaseWithMemoryTransport):
 
1607
 
 
1608
    def test_lock_to_break(self):
 
1609
        backing = self.get_transport()
 
1610
        request = smart_repo.SmartServerRepositoryBreakLock(backing)
 
1611
        tree = self.make_branch_and_memory_tree('.')
 
1612
        tree.branch.repository.lock_write()
 
1613
        self.assertEqual(
 
1614
            smart_req.SuccessfulSmartServerResponse(('ok', ), None),
 
1615
            request.execute(''))
 
1616
 
 
1617
    def test_nothing_to_break(self):
 
1618
        backing = self.get_transport()
 
1619
        request = smart_repo.SmartServerRepositoryBreakLock(backing)
 
1620
        tree = self.make_branch_and_memory_tree('.')
 
1621
        self.assertEqual(
 
1622
            smart_req.SuccessfulSmartServerResponse(('ok', ), None),
 
1623
            request.execute(''))
 
1624
 
 
1625
 
 
1626
class TestSmartServerRepositoryGetParentMap(tests.TestCaseWithMemoryTransport):
 
1627
 
 
1628
    def test_trivial_bzipped(self):
 
1629
        # This tests that the wire encoding is actually bzipped
 
1630
        backing = self.get_transport()
 
1631
        request = smart_repo.SmartServerRepositoryGetParentMap(backing)
 
1632
        tree = self.make_branch_and_memory_tree('.')
 
1633
 
 
1634
        self.assertEqual(None,
 
1635
            request.execute('', 'missing-id'))
 
1636
        # Note that it returns a body that is bzipped.
 
1637
        self.assertEqual(
 
1638
            smart_req.SuccessfulSmartServerResponse(('ok', ), bz2.compress('')),
 
1639
            request.do_body('\n\n0\n'))
 
1640
 
 
1641
    def test_trivial_include_missing(self):
 
1642
        backing = self.get_transport()
 
1643
        request = smart_repo.SmartServerRepositoryGetParentMap(backing)
 
1644
        tree = self.make_branch_and_memory_tree('.')
 
1645
 
 
1646
        self.assertEqual(None,
 
1647
            request.execute('', 'missing-id', 'include-missing:'))
 
1648
        self.assertEqual(
 
1649
            smart_req.SuccessfulSmartServerResponse(('ok', ),
 
1650
                bz2.compress('missing:missing-id')),
 
1651
            request.do_body('\n\n0\n'))
 
1652
 
 
1653
 
 
1654
class TestSmartServerRepositoryGetRevisionGraph(
 
1655
    tests.TestCaseWithMemoryTransport):
 
1656
 
 
1657
    def test_none_argument(self):
 
1658
        backing = self.get_transport()
 
1659
        request = smart_repo.SmartServerRepositoryGetRevisionGraph(backing)
 
1660
        tree = self.make_branch_and_memory_tree('.')
 
1661
        tree.lock_write()
 
1662
        tree.add('')
 
1663
        r1 = tree.commit('1st commit')
 
1664
        r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
 
1665
        tree.unlock()
 
1666
 
 
1667
        # the lines of revision_id->revision_parent_list has no guaranteed
 
1668
        # order coming out of a dict, so sort both our test and response
 
1669
        lines = sorted([' '.join([r2, r1]), r1])
 
1670
        response = request.execute('', '')
 
1671
        response.body = '\n'.join(sorted(response.body.split('\n')))
 
1672
 
 
1673
        self.assertEqual(
 
1674
            smart_req.SmartServerResponse(('ok', ), '\n'.join(lines)), response)
 
1675
 
 
1676
    def test_specific_revision_argument(self):
 
1677
        backing = self.get_transport()
 
1678
        request = smart_repo.SmartServerRepositoryGetRevisionGraph(backing)
 
1679
        tree = self.make_branch_and_memory_tree('.')
 
1680
        tree.lock_write()
 
1681
        tree.add('')
 
1682
        rev_id_utf8 = u'\xc9'.encode('utf-8')
 
1683
        r1 = tree.commit('1st commit', rev_id=rev_id_utf8)
 
1684
        r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
 
1685
        tree.unlock()
 
1686
 
 
1687
        self.assertEqual(smart_req.SmartServerResponse(('ok', ), rev_id_utf8),
 
1688
            request.execute('', rev_id_utf8))
 
1689
 
 
1690
    def test_no_such_revision(self):
 
1691
        backing = self.get_transport()
 
1692
        request = smart_repo.SmartServerRepositoryGetRevisionGraph(backing)
 
1693
        tree = self.make_branch_and_memory_tree('.')
 
1694
        tree.lock_write()
 
1695
        tree.add('')
 
1696
        r1 = tree.commit('1st commit')
 
1697
        tree.unlock()
 
1698
 
 
1699
        # Note that it still returns body (of zero bytes).
 
1700
        self.assertEqual(smart_req.SmartServerResponse(
 
1701
                ('nosuchrevision', 'missingrevision', ), ''),
 
1702
                         request.execute('', 'missingrevision'))
 
1703
 
 
1704
 
 
1705
class TestSmartServerRepositoryGetRevIdForRevno(
 
1706
    tests.TestCaseWithMemoryTransport):
 
1707
 
 
1708
    def test_revno_found(self):
 
1709
        backing = self.get_transport()
 
1710
        request = smart_repo.SmartServerRepositoryGetRevIdForRevno(backing)
 
1711
        tree = self.make_branch_and_memory_tree('.')
 
1712
        tree.lock_write()
 
1713
        tree.add('')
 
1714
        rev1_id_utf8 = u'\xc8'.encode('utf-8')
 
1715
        rev2_id_utf8 = u'\xc9'.encode('utf-8')
 
1716
        tree.commit('1st commit', rev_id=rev1_id_utf8)
 
1717
        tree.commit('2nd commit', rev_id=rev2_id_utf8)
 
1718
        tree.unlock()
 
1719
 
 
1720
        self.assertEqual(smart_req.SmartServerResponse(('ok', rev1_id_utf8)),
 
1721
            request.execute('', 1, (2, rev2_id_utf8)))
 
1722
 
 
1723
    def test_known_revid_missing(self):
 
1724
        backing = self.get_transport()
 
1725
        request = smart_repo.SmartServerRepositoryGetRevIdForRevno(backing)
 
1726
        repo = self.make_repository('.')
 
1727
        self.assertEqual(
 
1728
            smart_req.FailedSmartServerResponse(('nosuchrevision', 'ghost')),
 
1729
            request.execute('', 1, (2, 'ghost')))
 
1730
 
 
1731
    def test_history_incomplete(self):
 
1732
        backing = self.get_transport()
 
1733
        request = smart_repo.SmartServerRepositoryGetRevIdForRevno(backing)
 
1734
        parent = self.make_branch_and_memory_tree('parent', format='1.9')
 
1735
        parent.lock_write()
 
1736
        parent.add([''], ['TREE_ROOT'])
 
1737
        r1 = parent.commit(message='first commit')
 
1738
        r2 = parent.commit(message='second commit')
 
1739
        parent.unlock()
 
1740
        local = self.make_branch_and_memory_tree('local', format='1.9')
 
1741
        local.branch.pull(parent.branch)
 
1742
        local.set_parent_ids([r2])
 
1743
        r3 = local.commit(message='local commit')
 
1744
        local.branch.create_clone_on_transport(
 
1745
            self.get_transport('stacked'), stacked_on=self.get_url('parent'))
 
1746
        self.assertEqual(
 
1747
            smart_req.SmartServerResponse(('history-incomplete', 2, r2)),
 
1748
            request.execute('stacked', 1, (3, r3)))
 
1749
 
 
1750
 
 
1751
class TestSmartServerRepositoryIterRevisions(
 
1752
    tests.TestCaseWithMemoryTransport):
 
1753
 
 
1754
    def test_basic(self):
 
1755
        backing = self.get_transport()
 
1756
        request = smart_repo.SmartServerRepositoryIterRevisions(backing)
 
1757
        tree = self.make_branch_and_memory_tree('.', format='2a')
 
1758
        tree.lock_write()
 
1759
        tree.add('')
 
1760
        tree.commit('1st commit', rev_id="rev1")
 
1761
        tree.commit('2nd commit', rev_id="rev2")
 
1762
        tree.unlock()
 
1763
 
 
1764
        self.assertIs(None, request.execute(''))
 
1765
        response = request.do_body("rev1\nrev2")
 
1766
        self.assertTrue(response.is_successful())
 
1767
        # Format 2a uses serializer format 10
 
1768
        self.assertEquals(response.args, ("ok", "10"))
 
1769
 
 
1770
        self.addCleanup(tree.branch.lock_read().unlock)
 
1771
        entries = [zlib.compress(record.get_bytes_as("fulltext")) for record in
 
1772
            tree.branch.repository.revisions.get_record_stream(
 
1773
            [("rev1", ), ("rev2", )], "unordered", True)]
 
1774
 
 
1775
        contents = "".join(response.body_stream)
 
1776
        self.assertTrue(contents in (
 
1777
            "".join([entries[0], entries[1]]),
 
1778
            "".join([entries[1], entries[0]])))
 
1779
 
 
1780
    def test_missing(self):
 
1781
        backing = self.get_transport()
 
1782
        request = smart_repo.SmartServerRepositoryIterRevisions(backing)
 
1783
        tree = self.make_branch_and_memory_tree('.', format='2a')
 
1784
 
 
1785
        self.assertIs(None, request.execute(''))
 
1786
        response = request.do_body("rev1\nrev2")
 
1787
        self.assertTrue(response.is_successful())
 
1788
        # Format 2a uses serializer format 10
 
1789
        self.assertEquals(response.args, ("ok", "10"))
 
1790
 
 
1791
        contents = "".join(response.body_stream)
 
1792
        self.assertEquals(contents, "")
 
1793
 
 
1794
 
 
1795
class GetStreamTestBase(tests.TestCaseWithMemoryTransport):
 
1796
 
 
1797
    def make_two_commit_repo(self):
 
1798
        tree = self.make_branch_and_memory_tree('.')
 
1799
        tree.lock_write()
 
1800
        tree.add('')
 
1801
        r1 = tree.commit('1st commit')
 
1802
        r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
 
1803
        tree.unlock()
 
1804
        repo = tree.branch.repository
 
1805
        return repo, r1, r2
 
1806
 
 
1807
 
 
1808
class TestSmartServerRepositoryGetStream(GetStreamTestBase):
 
1809
 
 
1810
    def test_ancestry_of(self):
 
1811
        """The search argument may be a 'ancestry-of' some heads'."""
 
1812
        backing = self.get_transport()
 
1813
        request = smart_repo.SmartServerRepositoryGetStream(backing)
 
1814
        repo, r1, r2 = self.make_two_commit_repo()
 
1815
        fetch_spec = ['ancestry-of', r2]
 
1816
        lines = '\n'.join(fetch_spec)
 
1817
        request.execute('', repo._format.network_name())
 
1818
        response = request.do_body(lines)
 
1819
        self.assertEqual(('ok',), response.args)
 
1820
        stream_bytes = ''.join(response.body_stream)
 
1821
        self.assertStartsWith(stream_bytes, 'Bazaar pack format 1')
 
1822
 
 
1823
    def test_search(self):
 
1824
        """The search argument may be a 'search' of some explicit keys."""
 
1825
        backing = self.get_transport()
 
1826
        request = smart_repo.SmartServerRepositoryGetStream(backing)
 
1827
        repo, r1, r2 = self.make_two_commit_repo()
 
1828
        fetch_spec = ['search', '%s %s' % (r1, r2), 'null:', '2']
 
1829
        lines = '\n'.join(fetch_spec)
 
1830
        request.execute('', repo._format.network_name())
 
1831
        response = request.do_body(lines)
 
1832
        self.assertEqual(('ok',), response.args)
 
1833
        stream_bytes = ''.join(response.body_stream)
 
1834
        self.assertStartsWith(stream_bytes, 'Bazaar pack format 1')
 
1835
 
 
1836
    def test_search_everything(self):
 
1837
        """A search of 'everything' returns a stream."""
 
1838
        backing = self.get_transport()
 
1839
        request = smart_repo.SmartServerRepositoryGetStream_1_19(backing)
 
1840
        repo, r1, r2 = self.make_two_commit_repo()
 
1841
        serialised_fetch_spec = 'everything'
 
1842
        request.execute('', repo._format.network_name())
 
1843
        response = request.do_body(serialised_fetch_spec)
 
1844
        self.assertEqual(('ok',), response.args)
 
1845
        stream_bytes = ''.join(response.body_stream)
 
1846
        self.assertStartsWith(stream_bytes, 'Bazaar pack format 1')
 
1847
 
 
1848
 
 
1849
class TestSmartServerRequestHasRevision(tests.TestCaseWithMemoryTransport):
 
1850
 
 
1851
    def test_missing_revision(self):
 
1852
        """For a missing revision, ('no', ) is returned."""
 
1853
        backing = self.get_transport()
 
1854
        request = smart_repo.SmartServerRequestHasRevision(backing)
 
1855
        self.make_repository('.')
 
1856
        self.assertEqual(smart_req.SmartServerResponse(('no', )),
 
1857
            request.execute('', 'revid'))
 
1858
 
 
1859
    def test_present_revision(self):
 
1860
        """For a present revision, ('yes', ) is returned."""
 
1861
        backing = self.get_transport()
 
1862
        request = smart_repo.SmartServerRequestHasRevision(backing)
 
1863
        tree = self.make_branch_and_memory_tree('.')
 
1864
        tree.lock_write()
 
1865
        tree.add('')
 
1866
        rev_id_utf8 = u'\xc8abc'.encode('utf-8')
 
1867
        r1 = tree.commit('a commit', rev_id=rev_id_utf8)
 
1868
        tree.unlock()
 
1869
        self.assertTrue(tree.branch.repository.has_revision(rev_id_utf8))
 
1870
        self.assertEqual(smart_req.SmartServerResponse(('yes', )),
 
1871
            request.execute('', rev_id_utf8))
 
1872
 
 
1873
 
 
1874
class TestSmartServerRepositoryIterFilesBytes(tests.TestCaseWithTransport):
 
1875
 
 
1876
    def test_single(self):
 
1877
        backing = self.get_transport()
 
1878
        request = smart_repo.SmartServerRepositoryIterFilesBytes(backing)
 
1879
        t = self.make_branch_and_tree('.')
 
1880
        self.addCleanup(t.lock_write().unlock)
 
1881
        self.build_tree_contents([("file", "somecontents")])
 
1882
        t.add(["file"], ["thefileid"])
 
1883
        t.commit(rev_id='somerev', message="add file")
 
1884
        self.assertIs(None, request.execute(''))
 
1885
        response = request.do_body("thefileid\0somerev\n")
 
1886
        self.assertTrue(response.is_successful())
 
1887
        self.assertEquals(response.args, ("ok", ))
 
1888
        self.assertEquals("".join(response.body_stream),
 
1889
            "ok\x000\n" + zlib.compress("somecontents"))
 
1890
 
 
1891
    def test_missing(self):
 
1892
        backing = self.get_transport()
 
1893
        request = smart_repo.SmartServerRepositoryIterFilesBytes(backing)
 
1894
        t = self.make_branch_and_tree('.')
 
1895
        self.addCleanup(t.lock_write().unlock)
 
1896
        self.assertIs(None, request.execute(''))
 
1897
        response = request.do_body("thefileid\0revision\n")
 
1898
        self.assertTrue(response.is_successful())
 
1899
        self.assertEquals(response.args, ("ok", ))
 
1900
        self.assertEquals("".join(response.body_stream),
 
1901
            "absent\x00thefileid\x00revision\x000\n")
 
1902
 
 
1903
 
 
1904
class TestSmartServerRequestHasSignatureForRevisionId(
 
1905
        tests.TestCaseWithMemoryTransport):
 
1906
 
 
1907
    def test_missing_revision(self):
 
1908
        """For a missing revision, NoSuchRevision is returned."""
 
1909
        backing = self.get_transport()
 
1910
        request = smart_repo.SmartServerRequestHasSignatureForRevisionId(
 
1911
            backing)
 
1912
        self.make_repository('.')
 
1913
        self.assertEqual(
 
1914
            smart_req.FailedSmartServerResponse(
 
1915
                ('nosuchrevision', 'revid'), None),
 
1916
            request.execute('', 'revid'))
 
1917
 
 
1918
    def test_missing_signature(self):
 
1919
        """For a missing signature, ('no', ) is returned."""
 
1920
        backing = self.get_transport()
 
1921
        request = smart_repo.SmartServerRequestHasSignatureForRevisionId(
 
1922
            backing)
 
1923
        tree = self.make_branch_and_memory_tree('.')
 
1924
        tree.lock_write()
 
1925
        tree.add('')
 
1926
        r1 = tree.commit('a commit', rev_id='A')
 
1927
        tree.unlock()
 
1928
        self.assertTrue(tree.branch.repository.has_revision('A'))
 
1929
        self.assertEqual(smart_req.SmartServerResponse(('no', )),
 
1930
            request.execute('', 'A'))
 
1931
 
 
1932
    def test_present_signature(self):
 
1933
        """For a present signature, ('yes', ) is returned."""
 
1934
        backing = self.get_transport()
 
1935
        request = smart_repo.SmartServerRequestHasSignatureForRevisionId(
 
1936
            backing)
 
1937
        strategy = gpg.LoopbackGPGStrategy(None)
 
1938
        tree = self.make_branch_and_memory_tree('.')
 
1939
        tree.lock_write()
 
1940
        tree.add('')
 
1941
        r1 = tree.commit('a commit', rev_id='A')
 
1942
        tree.branch.repository.start_write_group()
 
1943
        tree.branch.repository.sign_revision('A', strategy)
 
1944
        tree.branch.repository.commit_write_group()
 
1945
        tree.unlock()
 
1946
        self.assertTrue(tree.branch.repository.has_revision('A'))
 
1947
        self.assertEqual(smart_req.SmartServerResponse(('yes', )),
 
1948
            request.execute('', 'A'))
 
1949
 
 
1950
 
 
1951
class TestSmartServerRepositoryGatherStats(tests.TestCaseWithMemoryTransport):
 
1952
 
 
1953
    def test_empty_revid(self):
 
1954
        """With an empty revid, we get only size an number and revisions"""
 
1955
        backing = self.get_transport()
 
1956
        request = smart_repo.SmartServerRepositoryGatherStats(backing)
 
1957
        repository = self.make_repository('.')
 
1958
        stats = repository.gather_stats()
 
1959
        expected_body = 'revisions: 0\n'
 
1960
        self.assertEqual(smart_req.SmartServerResponse(('ok', ), expected_body),
 
1961
                         request.execute('', '', 'no'))
 
1962
 
 
1963
    def test_revid_with_committers(self):
 
1964
        """For a revid we get more infos."""
 
1965
        backing = self.get_transport()
 
1966
        rev_id_utf8 = u'\xc8abc'.encode('utf-8')
 
1967
        request = smart_repo.SmartServerRepositoryGatherStats(backing)
 
1968
        tree = self.make_branch_and_memory_tree('.')
 
1969
        tree.lock_write()
 
1970
        tree.add('')
 
1971
        # Let's build a predictable result
 
1972
        tree.commit('a commit', timestamp=123456.2, timezone=3600)
 
1973
        tree.commit('a commit', timestamp=654321.4, timezone=0,
 
1974
                    rev_id=rev_id_utf8)
 
1975
        tree.unlock()
 
1976
 
 
1977
        stats = tree.branch.repository.gather_stats()
 
1978
        expected_body = ('firstrev: 123456.200 3600\n'
 
1979
                         'latestrev: 654321.400 0\n'
 
1980
                         'revisions: 2\n')
 
1981
        self.assertEqual(smart_req.SmartServerResponse(('ok', ), expected_body),
 
1982
                         request.execute('',
 
1983
                                         rev_id_utf8, 'no'))
 
1984
 
 
1985
    def test_not_empty_repository_with_committers(self):
 
1986
        """For a revid and requesting committers we get the whole thing."""
 
1987
        backing = self.get_transport()
 
1988
        rev_id_utf8 = u'\xc8abc'.encode('utf-8')
 
1989
        request = smart_repo.SmartServerRepositoryGatherStats(backing)
 
1990
        tree = self.make_branch_and_memory_tree('.')
 
1991
        tree.lock_write()
 
1992
        tree.add('')
 
1993
        # Let's build a predictable result
 
1994
        tree.commit('a commit', timestamp=123456.2, timezone=3600,
 
1995
                    committer='foo')
 
1996
        tree.commit('a commit', timestamp=654321.4, timezone=0,
 
1997
                    committer='bar', rev_id=rev_id_utf8)
 
1998
        tree.unlock()
 
1999
        stats = tree.branch.repository.gather_stats()
 
2000
 
 
2001
        expected_body = ('committers: 2\n'
 
2002
                         'firstrev: 123456.200 3600\n'
 
2003
                         'latestrev: 654321.400 0\n'
 
2004
                         'revisions: 2\n')
 
2005
        self.assertEqual(smart_req.SmartServerResponse(('ok', ), expected_body),
 
2006
                         request.execute('',
 
2007
                                         rev_id_utf8, 'yes'))
 
2008
 
 
2009
    def test_unknown_revid(self):
 
2010
        """An unknown revision id causes a 'nosuchrevision' error."""
 
2011
        backing = self.get_transport()
 
2012
        request = smart_repo.SmartServerRepositoryGatherStats(backing)
 
2013
        repository = self.make_repository('.')
 
2014
        expected_body = 'revisions: 0\n'
 
2015
        self.assertEqual(
 
2016
            smart_req.FailedSmartServerResponse(
 
2017
                ('nosuchrevision', 'mia'), None),
 
2018
            request.execute('', 'mia', 'yes'))
 
2019
 
 
2020
 
 
2021
class TestSmartServerRepositoryIsShared(tests.TestCaseWithMemoryTransport):
 
2022
 
 
2023
    def test_is_shared(self):
 
2024
        """For a shared repository, ('yes', ) is returned."""
 
2025
        backing = self.get_transport()
 
2026
        request = smart_repo.SmartServerRepositoryIsShared(backing)
 
2027
        self.make_repository('.', shared=True)
 
2028
        self.assertEqual(smart_req.SmartServerResponse(('yes', )),
 
2029
            request.execute('', ))
 
2030
 
 
2031
    def test_is_not_shared(self):
 
2032
        """For a shared repository, ('no', ) is returned."""
 
2033
        backing = self.get_transport()
 
2034
        request = smart_repo.SmartServerRepositoryIsShared(backing)
 
2035
        self.make_repository('.', shared=False)
 
2036
        self.assertEqual(smart_req.SmartServerResponse(('no', )),
 
2037
            request.execute('', ))
 
2038
 
 
2039
 
 
2040
class TestSmartServerRepositoryGetRevisionSignatureText(
 
2041
        tests.TestCaseWithMemoryTransport):
 
2042
 
 
2043
    def test_get_signature(self):
 
2044
        backing = self.get_transport()
 
2045
        request = smart_repo.SmartServerRepositoryGetRevisionSignatureText(
 
2046
            backing)
 
2047
        bb = self.make_branch_builder('.')
 
2048
        bb.build_commit(rev_id='A')
 
2049
        repo = bb.get_branch().repository
 
2050
        strategy = gpg.LoopbackGPGStrategy(None)
 
2051
        self.addCleanup(repo.lock_write().unlock)
 
2052
        repo.start_write_group()
 
2053
        repo.sign_revision('A', strategy)
 
2054
        repo.commit_write_group()
 
2055
        expected_body = (
 
2056
            '-----BEGIN PSEUDO-SIGNED CONTENT-----\n' +
 
2057
            Testament.from_revision(repo, 'A').as_short_text() +
 
2058
            '-----END PSEUDO-SIGNED CONTENT-----\n')
 
2059
        self.assertEqual(
 
2060
            smart_req.SmartServerResponse(('ok', ), expected_body),
 
2061
            request.execute('', 'A'))
 
2062
 
 
2063
 
 
2064
class TestSmartServerRepositoryMakeWorkingTrees(
 
2065
        tests.TestCaseWithMemoryTransport):
 
2066
 
 
2067
    def test_make_working_trees(self):
 
2068
        """For a repository with working trees, ('yes', ) is returned."""
 
2069
        backing = self.get_transport()
 
2070
        request = smart_repo.SmartServerRepositoryMakeWorkingTrees(backing)
 
2071
        r = self.make_repository('.')
 
2072
        r.set_make_working_trees(True)
 
2073
        self.assertEqual(smart_req.SmartServerResponse(('yes', )),
 
2074
            request.execute('', ))
 
2075
 
 
2076
    def test_is_not_shared(self):
 
2077
        """For a repository with working trees, ('no', ) is returned."""
 
2078
        backing = self.get_transport()
 
2079
        request = smart_repo.SmartServerRepositoryMakeWorkingTrees(backing)
 
2080
        r = self.make_repository('.')
 
2081
        r.set_make_working_trees(False)
 
2082
        self.assertEqual(smart_req.SmartServerResponse(('no', )),
 
2083
            request.execute('', ))
 
2084
 
 
2085
 
 
2086
class TestSmartServerRepositoryLockWrite(tests.TestCaseWithMemoryTransport):
 
2087
 
 
2088
    def test_lock_write_on_unlocked_repo(self):
 
2089
        backing = self.get_transport()
 
2090
        request = smart_repo.SmartServerRepositoryLockWrite(backing)
 
2091
        repository = self.make_repository('.', format='knit')
 
2092
        response = request.execute('')
 
2093
        nonce = repository.control_files._lock.peek().get('nonce')
 
2094
        self.assertEqual(smart_req.SmartServerResponse(('ok', nonce)), response)
 
2095
        # The repository is now locked.  Verify that with a new repository
 
2096
        # object.
 
2097
        new_repo = repository.bzrdir.open_repository()
 
2098
        self.assertRaises(errors.LockContention, new_repo.lock_write)
 
2099
        # Cleanup
 
2100
        request = smart_repo.SmartServerRepositoryUnlock(backing)
 
2101
        response = request.execute('', nonce)
 
2102
 
 
2103
    def test_lock_write_on_locked_repo(self):
 
2104
        backing = self.get_transport()
 
2105
        request = smart_repo.SmartServerRepositoryLockWrite(backing)
 
2106
        repository = self.make_repository('.', format='knit')
 
2107
        repo_token = repository.lock_write().repository_token
 
2108
        repository.leave_lock_in_place()
 
2109
        repository.unlock()
 
2110
        response = request.execute('')
 
2111
        self.assertEqual(
 
2112
            smart_req.SmartServerResponse(('LockContention',)), response)
 
2113
        # Cleanup
 
2114
        repository.lock_write(repo_token)
 
2115
        repository.dont_leave_lock_in_place()
 
2116
        repository.unlock()
 
2117
 
 
2118
    def test_lock_write_on_readonly_transport(self):
 
2119
        backing = self.get_readonly_transport()
 
2120
        request = smart_repo.SmartServerRepositoryLockWrite(backing)
 
2121
        repository = self.make_repository('.', format='knit')
 
2122
        response = request.execute('')
 
2123
        self.assertFalse(response.is_successful())
 
2124
        self.assertEqual('LockFailed', response.args[0])
 
2125
 
 
2126
 
 
2127
class TestInsertStreamBase(tests.TestCaseWithMemoryTransport):
 
2128
 
 
2129
    def make_empty_byte_stream(self, repo):
 
2130
        byte_stream = smart_repo._stream_to_byte_stream([], repo._format)
 
2131
        return ''.join(byte_stream)
 
2132
 
 
2133
 
 
2134
class TestSmartServerRepositoryInsertStream(TestInsertStreamBase):
 
2135
 
 
2136
    def test_insert_stream_empty(self):
 
2137
        backing = self.get_transport()
 
2138
        request = smart_repo.SmartServerRepositoryInsertStream(backing)
 
2139
        repository = self.make_repository('.')
 
2140
        response = request.execute('', '')
 
2141
        self.assertEqual(None, response)
 
2142
        response = request.do_chunk(self.make_empty_byte_stream(repository))
 
2143
        self.assertEqual(None, response)
 
2144
        response = request.do_end()
 
2145
        self.assertEqual(smart_req.SmartServerResponse(('ok', )), response)
 
2146
 
 
2147
 
 
2148
class TestSmartServerRepositoryInsertStreamLocked(TestInsertStreamBase):
 
2149
 
 
2150
    def test_insert_stream_empty(self):
 
2151
        backing = self.get_transport()
 
2152
        request = smart_repo.SmartServerRepositoryInsertStreamLocked(
 
2153
            backing)
 
2154
        repository = self.make_repository('.', format='knit')
 
2155
        lock_token = repository.lock_write().repository_token
 
2156
        response = request.execute('', '', lock_token)
 
2157
        self.assertEqual(None, response)
 
2158
        response = request.do_chunk(self.make_empty_byte_stream(repository))
 
2159
        self.assertEqual(None, response)
 
2160
        response = request.do_end()
 
2161
        self.assertEqual(smart_req.SmartServerResponse(('ok', )), response)
 
2162
        repository.unlock()
 
2163
 
 
2164
    def test_insert_stream_with_wrong_lock_token(self):
 
2165
        backing = self.get_transport()
 
2166
        request = smart_repo.SmartServerRepositoryInsertStreamLocked(
 
2167
            backing)
 
2168
        repository = self.make_repository('.', format='knit')
 
2169
        lock_token = repository.lock_write().repository_token
 
2170
        self.assertRaises(
 
2171
            errors.TokenMismatch, request.execute, '', '', 'wrong-token')
 
2172
        repository.unlock()
 
2173
 
 
2174
 
 
2175
class TestSmartServerRepositoryUnlock(tests.TestCaseWithMemoryTransport):
 
2176
 
 
2177
    def setUp(self):
 
2178
        tests.TestCaseWithMemoryTransport.setUp(self)
 
2179
 
 
2180
    def test_unlock_on_locked_repo(self):
 
2181
        backing = self.get_transport()
 
2182
        request = smart_repo.SmartServerRepositoryUnlock(backing)
 
2183
        repository = self.make_repository('.', format='knit')
 
2184
        token = repository.lock_write().repository_token
 
2185
        repository.leave_lock_in_place()
 
2186
        repository.unlock()
 
2187
        response = request.execute('', token)
 
2188
        self.assertEqual(
 
2189
            smart_req.SmartServerResponse(('ok',)), response)
 
2190
        # The repository is now unlocked.  Verify that with a new repository
 
2191
        # object.
 
2192
        new_repo = repository.bzrdir.open_repository()
 
2193
        new_repo.lock_write()
 
2194
        new_repo.unlock()
 
2195
 
 
2196
    def test_unlock_on_unlocked_repo(self):
 
2197
        backing = self.get_transport()
 
2198
        request = smart_repo.SmartServerRepositoryUnlock(backing)
 
2199
        repository = self.make_repository('.', format='knit')
 
2200
        response = request.execute('', 'some token')
 
2201
        self.assertEqual(
 
2202
            smart_req.SmartServerResponse(('TokenMismatch',)), response)
 
2203
 
 
2204
 
 
2205
class TestSmartServerRepositoryGetPhysicalLockStatus(
 
2206
    tests.TestCaseWithTransport):
 
2207
 
 
2208
    def test_with_write_lock(self):
 
2209
        backing = self.get_transport()
 
2210
        repo = self.make_repository('.')
 
2211
        self.addCleanup(repo.lock_write().unlock)
 
2212
        # lock_write() doesn't necessarily actually take a physical
 
2213
        # lock out.
 
2214
        if repo.get_physical_lock_status():
 
2215
            expected = 'yes'
 
2216
        else:
 
2217
            expected = 'no'
 
2218
        request_class = smart_repo.SmartServerRepositoryGetPhysicalLockStatus
 
2219
        request = request_class(backing)
 
2220
        self.assertEqual(smart_req.SuccessfulSmartServerResponse((expected,)),
 
2221
            request.execute('', ))
 
2222
 
 
2223
    def test_without_write_lock(self):
 
2224
        backing = self.get_transport()
 
2225
        repo = self.make_repository('.')
 
2226
        self.assertEquals(False, repo.get_physical_lock_status())
 
2227
        request_class = smart_repo.SmartServerRepositoryGetPhysicalLockStatus
 
2228
        request = request_class(backing)
 
2229
        self.assertEqual(smart_req.SuccessfulSmartServerResponse(('no',)),
 
2230
            request.execute('', ))
 
2231
 
 
2232
 
 
2233
class TestSmartServerRepositoryReconcile(tests.TestCaseWithTransport):
 
2234
 
 
2235
    def test_reconcile(self):
 
2236
        backing = self.get_transport()
 
2237
        repo = self.make_repository('.')
 
2238
        token = repo.lock_write().repository_token
 
2239
        self.addCleanup(repo.unlock)
 
2240
        request_class = smart_repo.SmartServerRepositoryReconcile
 
2241
        request = request_class(backing)
 
2242
        self.assertEqual(smart_req.SuccessfulSmartServerResponse(
 
2243
            ('ok', ),
 
2244
             'garbage_inventories: 0\n'
 
2245
             'inconsistent_parents: 0\n'),
 
2246
            request.execute('', token))
 
2247
 
 
2248
 
 
2249
class TestSmartServerIsReadonly(tests.TestCaseWithMemoryTransport):
 
2250
 
 
2251
    def test_is_readonly_no(self):
 
2252
        backing = self.get_transport()
 
2253
        request = smart_req.SmartServerIsReadonly(backing)
 
2254
        response = request.execute()
 
2255
        self.assertEqual(
 
2256
            smart_req.SmartServerResponse(('no',)), response)
 
2257
 
 
2258
    def test_is_readonly_yes(self):
 
2259
        backing = self.get_readonly_transport()
 
2260
        request = smart_req.SmartServerIsReadonly(backing)
 
2261
        response = request.execute()
 
2262
        self.assertEqual(
 
2263
            smart_req.SmartServerResponse(('yes',)), response)
 
2264
 
 
2265
 
 
2266
class TestSmartServerRepositorySetMakeWorkingTrees(
 
2267
    tests.TestCaseWithMemoryTransport):
 
2268
 
 
2269
    def test_set_false(self):
 
2270
        backing = self.get_transport()
 
2271
        repo = self.make_repository('.', shared=True)
 
2272
        repo.set_make_working_trees(True)
 
2273
        request_class = smart_repo.SmartServerRepositorySetMakeWorkingTrees
 
2274
        request = request_class(backing)
 
2275
        self.assertEqual(smart_req.SuccessfulSmartServerResponse(('ok',)),
 
2276
            request.execute('', 'False'))
 
2277
        repo = repo.bzrdir.open_repository()
 
2278
        self.assertFalse(repo.make_working_trees())
 
2279
 
 
2280
    def test_set_true(self):
 
2281
        backing = self.get_transport()
 
2282
        repo = self.make_repository('.', shared=True)
 
2283
        repo.set_make_working_trees(False)
 
2284
        request_class = smart_repo.SmartServerRepositorySetMakeWorkingTrees
 
2285
        request = request_class(backing)
 
2286
        self.assertEqual(smart_req.SuccessfulSmartServerResponse(('ok',)),
 
2287
            request.execute('', 'True'))
 
2288
        repo = repo.bzrdir.open_repository()
 
2289
        self.assertTrue(repo.make_working_trees())
 
2290
 
 
2291
 
 
2292
class TestSmartServerRepositoryGetSerializerFormat(
 
2293
    tests.TestCaseWithMemoryTransport):
 
2294
 
 
2295
    def test_get_serializer_format(self):
 
2296
        backing = self.get_transport()
 
2297
        repo = self.make_repository('.', format='2a')
 
2298
        request_class = smart_repo.SmartServerRepositoryGetSerializerFormat
 
2299
        request = request_class(backing)
 
2300
        self.assertEqual(
 
2301
            smart_req.SuccessfulSmartServerResponse(('ok', '10')),
 
2302
            request.execute(''))
 
2303
 
 
2304
 
 
2305
class TestSmartServerRepositoryWriteGroup(
 
2306
    tests.TestCaseWithMemoryTransport):
 
2307
 
 
2308
    def test_start_write_group(self):
 
2309
        backing = self.get_transport()
 
2310
        repo = self.make_repository('.')
 
2311
        lock_token = repo.lock_write().repository_token
 
2312
        self.addCleanup(repo.unlock)
 
2313
        request_class = smart_repo.SmartServerRepositoryStartWriteGroup
 
2314
        request = request_class(backing)
 
2315
        self.assertEqual(smart_req.SuccessfulSmartServerResponse(('ok', [])),
 
2316
            request.execute('', lock_token))
 
2317
 
 
2318
    def test_start_write_group_unsuspendable(self):
 
2319
        backing = self.get_transport()
 
2320
        repo = self.make_repository('.', format='knit')
 
2321
        lock_token = repo.lock_write().repository_token
 
2322
        self.addCleanup(repo.unlock)
 
2323
        request_class = smart_repo.SmartServerRepositoryStartWriteGroup
 
2324
        request = request_class(backing)
 
2325
        self.assertEqual(
 
2326
            smart_req.FailedSmartServerResponse(('UnsuspendableWriteGroup',)),
 
2327
            request.execute('', lock_token))
 
2328
 
 
2329
    def test_commit_write_group(self):
 
2330
        backing = self.get_transport()
 
2331
        repo = self.make_repository('.')
 
2332
        lock_token = repo.lock_write().repository_token
 
2333
        self.addCleanup(repo.unlock)
 
2334
        repo.start_write_group()
 
2335
        tokens = repo.suspend_write_group()
 
2336
        request_class = smart_repo.SmartServerRepositoryCommitWriteGroup
 
2337
        request = request_class(backing)
 
2338
        self.assertEqual(smart_req.SuccessfulSmartServerResponse(('ok',)),
 
2339
            request.execute('', lock_token, tokens))
 
2340
 
 
2341
    def test_abort_write_group(self):
 
2342
        backing = self.get_transport()
 
2343
        repo = self.make_repository('.')
 
2344
        lock_token = repo.lock_write().repository_token
 
2345
        repo.start_write_group()
 
2346
        tokens = repo.suspend_write_group()
 
2347
        self.addCleanup(repo.unlock)
 
2348
        request_class = smart_repo.SmartServerRepositoryAbortWriteGroup
 
2349
        request = request_class(backing)
 
2350
        self.assertEqual(smart_req.SuccessfulSmartServerResponse(('ok',)),
 
2351
            request.execute('', lock_token, tokens))
 
2352
 
 
2353
    def test_check_write_group(self):
 
2354
        backing = self.get_transport()
 
2355
        repo = self.make_repository('.')
 
2356
        lock_token = repo.lock_write().repository_token
 
2357
        repo.start_write_group()
 
2358
        tokens = repo.suspend_write_group()
 
2359
        self.addCleanup(repo.unlock)
 
2360
        request_class = smart_repo.SmartServerRepositoryCheckWriteGroup
 
2361
        request = request_class(backing)
 
2362
        self.assertEqual(smart_req.SuccessfulSmartServerResponse(('ok',)),
 
2363
            request.execute('', lock_token, tokens))
 
2364
 
 
2365
    def test_check_write_group_invalid(self):
 
2366
        backing = self.get_transport()
 
2367
        repo = self.make_repository('.')
 
2368
        lock_token = repo.lock_write().repository_token
 
2369
        self.addCleanup(repo.unlock)
 
2370
        request_class = smart_repo.SmartServerRepositoryCheckWriteGroup
 
2371
        request = request_class(backing)
 
2372
        self.assertEqual(smart_req.FailedSmartServerResponse(
 
2373
            ('UnresumableWriteGroup', ['random'],
 
2374
                'Malformed write group token')),
 
2375
            request.execute('', lock_token, ["random"]))
 
2376
 
 
2377
 
 
2378
class TestSmartServerPackRepositoryAutopack(tests.TestCaseWithTransport):
 
2379
 
 
2380
    def make_repo_needing_autopacking(self, path='.'):
 
2381
        # Make a repo in need of autopacking.
 
2382
        tree = self.make_branch_and_tree('.', format='pack-0.92')
 
2383
        repo = tree.branch.repository
 
2384
        # monkey-patch the pack collection to disable autopacking
 
2385
        repo._pack_collection._max_pack_count = lambda count: count
 
2386
        for x in range(10):
 
2387
            tree.commit('commit %s' % x)
 
2388
        self.assertEqual(10, len(repo._pack_collection.names()))
 
2389
        del repo._pack_collection._max_pack_count
 
2390
        return repo
 
2391
 
 
2392
    def test_autopack_needed(self):
 
2393
        repo = self.make_repo_needing_autopacking()
 
2394
        repo.lock_write()
 
2395
        self.addCleanup(repo.unlock)
 
2396
        backing = self.get_transport()
 
2397
        request = smart_packrepo.SmartServerPackRepositoryAutopack(
 
2398
            backing)
 
2399
        response = request.execute('')
 
2400
        self.assertEqual(smart_req.SmartServerResponse(('ok',)), response)
 
2401
        repo._pack_collection.reload_pack_names()
 
2402
        self.assertEqual(1, len(repo._pack_collection.names()))
 
2403
 
 
2404
    def test_autopack_not_needed(self):
 
2405
        tree = self.make_branch_and_tree('.', format='pack-0.92')
 
2406
        repo = tree.branch.repository
 
2407
        repo.lock_write()
 
2408
        self.addCleanup(repo.unlock)
 
2409
        for x in range(9):
 
2410
            tree.commit('commit %s' % x)
 
2411
        backing = self.get_transport()
 
2412
        request = smart_packrepo.SmartServerPackRepositoryAutopack(
 
2413
            backing)
 
2414
        response = request.execute('')
 
2415
        self.assertEqual(smart_req.SmartServerResponse(('ok',)), response)
 
2416
        repo._pack_collection.reload_pack_names()
 
2417
        self.assertEqual(9, len(repo._pack_collection.names()))
 
2418
 
 
2419
    def test_autopack_on_nonpack_format(self):
 
2420
        """A request to autopack a non-pack repo is a no-op."""
 
2421
        repo = self.make_repository('.', format='knit')
 
2422
        backing = self.get_transport()
 
2423
        request = smart_packrepo.SmartServerPackRepositoryAutopack(
 
2424
            backing)
 
2425
        response = request.execute('')
 
2426
        self.assertEqual(smart_req.SmartServerResponse(('ok',)), response)
 
2427
 
 
2428
 
 
2429
class TestSmartServerVfsGet(tests.TestCaseWithMemoryTransport):
 
2430
 
 
2431
    def test_unicode_path(self):
 
2432
        """VFS requests expect unicode paths to be escaped."""
 
2433
        filename = u'foo\N{INTERROBANG}'
 
2434
        filename_escaped = urlutils.escape(filename)
 
2435
        backing = self.get_transport()
 
2436
        request = vfs.GetRequest(backing)
 
2437
        backing.put_bytes_non_atomic(filename_escaped, 'contents')
 
2438
        self.assertEqual(smart_req.SmartServerResponse(('ok', ), 'contents'),
 
2439
            request.execute(filename_escaped))
 
2440
 
 
2441
 
 
2442
class TestHandlers(tests.TestCase):
 
2443
    """Tests for the request.request_handlers object."""
 
2444
 
 
2445
    def test_all_registrations_exist(self):
 
2446
        """All registered request_handlers can be found."""
 
2447
        # If there's a typo in a register_lazy call, this loop will fail with
 
2448
        # an AttributeError.
 
2449
        for key in smart_req.request_handlers.keys():
 
2450
            try:
 
2451
                item = smart_req.request_handlers.get(key)
 
2452
            except AttributeError, e:
 
2453
                raise AttributeError('failed to get %s: %s' % (key, e))
 
2454
 
 
2455
    def assertHandlerEqual(self, verb, handler):
 
2456
        self.assertEqual(smart_req.request_handlers.get(verb), handler)
 
2457
 
 
2458
    def test_registered_methods(self):
 
2459
        """Test that known methods are registered to the correct object."""
 
2460
        self.assertHandlerEqual('Branch.break_lock',
 
2461
            smart_branch.SmartServerBranchBreakLock)
 
2462
        self.assertHandlerEqual('Branch.get_config_file',
 
2463
            smart_branch.SmartServerBranchGetConfigFile)
 
2464
        self.assertHandlerEqual('Branch.put_config_file',
 
2465
            smart_branch.SmartServerBranchPutConfigFile)
 
2466
        self.assertHandlerEqual('Branch.get_parent',
 
2467
            smart_branch.SmartServerBranchGetParent)
 
2468
        self.assertHandlerEqual('Branch.get_physical_lock_status',
 
2469
            smart_branch.SmartServerBranchRequestGetPhysicalLockStatus)
 
2470
        self.assertHandlerEqual('Branch.get_tags_bytes',
 
2471
            smart_branch.SmartServerBranchGetTagsBytes)
 
2472
        self.assertHandlerEqual('Branch.lock_write',
 
2473
            smart_branch.SmartServerBranchRequestLockWrite)
 
2474
        self.assertHandlerEqual('Branch.last_revision_info',
 
2475
            smart_branch.SmartServerBranchRequestLastRevisionInfo)
 
2476
        self.assertHandlerEqual('Branch.revision_history',
 
2477
            smart_branch.SmartServerRequestRevisionHistory)
 
2478
        self.assertHandlerEqual('Branch.revision_id_to_revno',
 
2479
            smart_branch.SmartServerBranchRequestRevisionIdToRevno)
 
2480
        self.assertHandlerEqual('Branch.set_config_option',
 
2481
            smart_branch.SmartServerBranchRequestSetConfigOption)
 
2482
        self.assertHandlerEqual('Branch.set_last_revision',
 
2483
            smart_branch.SmartServerBranchRequestSetLastRevision)
 
2484
        self.assertHandlerEqual('Branch.set_last_revision_info',
 
2485
            smart_branch.SmartServerBranchRequestSetLastRevisionInfo)
 
2486
        self.assertHandlerEqual('Branch.set_last_revision_ex',
 
2487
            smart_branch.SmartServerBranchRequestSetLastRevisionEx)
 
2488
        self.assertHandlerEqual('Branch.set_parent_location',
 
2489
            smart_branch.SmartServerBranchRequestSetParentLocation)
 
2490
        self.assertHandlerEqual('Branch.unlock',
 
2491
            smart_branch.SmartServerBranchRequestUnlock)
 
2492
        self.assertHandlerEqual('BzrDir.destroy_branch',
 
2493
            smart_dir.SmartServerBzrDirRequestDestroyBranch)
 
2494
        self.assertHandlerEqual('BzrDir.find_repository',
 
2495
            smart_dir.SmartServerRequestFindRepositoryV1)
 
2496
        self.assertHandlerEqual('BzrDir.find_repositoryV2',
 
2497
            smart_dir.SmartServerRequestFindRepositoryV2)
 
2498
        self.assertHandlerEqual('BzrDirFormat.initialize',
 
2499
            smart_dir.SmartServerRequestInitializeBzrDir)
 
2500
        self.assertHandlerEqual('BzrDirFormat.initialize_ex_1.16',
 
2501
            smart_dir.SmartServerRequestBzrDirInitializeEx)
 
2502
        self.assertHandlerEqual('BzrDir.checkout_metadir',
 
2503
            smart_dir.SmartServerBzrDirRequestCheckoutMetaDir)
 
2504
        self.assertHandlerEqual('BzrDir.cloning_metadir',
 
2505
            smart_dir.SmartServerBzrDirRequestCloningMetaDir)
 
2506
        self.assertHandlerEqual('BzrDir.get_config_file',
 
2507
            smart_dir.SmartServerBzrDirRequestConfigFile)
 
2508
        self.assertHandlerEqual('BzrDir.open_branch',
 
2509
            smart_dir.SmartServerRequestOpenBranch)
 
2510
        self.assertHandlerEqual('BzrDir.open_branchV2',
 
2511
            smart_dir.SmartServerRequestOpenBranchV2)
 
2512
        self.assertHandlerEqual('BzrDir.open_branchV3',
 
2513
            smart_dir.SmartServerRequestOpenBranchV3)
 
2514
        self.assertHandlerEqual('PackRepository.autopack',
 
2515
            smart_packrepo.SmartServerPackRepositoryAutopack)
 
2516
        self.assertHandlerEqual('Repository.add_signature_text',
 
2517
            smart_repo.SmartServerRepositoryAddSignatureText)
 
2518
        self.assertHandlerEqual('Repository.all_revision_ids',
 
2519
            smart_repo.SmartServerRepositoryAllRevisionIds)
 
2520
        self.assertHandlerEqual('Repository.break_lock',
 
2521
            smart_repo.SmartServerRepositoryBreakLock)
 
2522
        self.assertHandlerEqual('Repository.gather_stats',
 
2523
            smart_repo.SmartServerRepositoryGatherStats)
 
2524
        self.assertHandlerEqual('Repository.get_parent_map',
 
2525
            smart_repo.SmartServerRepositoryGetParentMap)
 
2526
        self.assertHandlerEqual('Repository.get_physical_lock_status',
 
2527
            smart_repo.SmartServerRepositoryGetPhysicalLockStatus)
 
2528
        self.assertHandlerEqual('Repository.get_rev_id_for_revno',
 
2529
            smart_repo.SmartServerRepositoryGetRevIdForRevno)
 
2530
        self.assertHandlerEqual('Repository.get_revision_graph',
 
2531
            smart_repo.SmartServerRepositoryGetRevisionGraph)
 
2532
        self.assertHandlerEqual('Repository.get_revision_signature_text',
 
2533
            smart_repo.SmartServerRepositoryGetRevisionSignatureText)
 
2534
        self.assertHandlerEqual('Repository.get_stream',
 
2535
            smart_repo.SmartServerRepositoryGetStream)
 
2536
        self.assertHandlerEqual('Repository.get_stream_1.19',
 
2537
            smart_repo.SmartServerRepositoryGetStream_1_19)
 
2538
        self.assertHandlerEqual('Repository.iter_revisions',
 
2539
            smart_repo.SmartServerRepositoryIterRevisions)
 
2540
        self.assertHandlerEqual('Repository.has_revision',
 
2541
            smart_repo.SmartServerRequestHasRevision)
 
2542
        self.assertHandlerEqual('Repository.insert_stream',
 
2543
            smart_repo.SmartServerRepositoryInsertStream)
 
2544
        self.assertHandlerEqual('Repository.insert_stream_locked',
 
2545
            smart_repo.SmartServerRepositoryInsertStreamLocked)
 
2546
        self.assertHandlerEqual('Repository.is_shared',
 
2547
            smart_repo.SmartServerRepositoryIsShared)
 
2548
        self.assertHandlerEqual('Repository.iter_files_bytes',
 
2549
            smart_repo.SmartServerRepositoryIterFilesBytes)
 
2550
        self.assertHandlerEqual('Repository.lock_write',
 
2551
            smart_repo.SmartServerRepositoryLockWrite)
 
2552
        self.assertHandlerEqual('Repository.make_working_trees',
 
2553
            smart_repo.SmartServerRepositoryMakeWorkingTrees)
 
2554
        self.assertHandlerEqual('Repository.pack',
 
2555
            smart_repo.SmartServerRepositoryPack)
 
2556
        self.assertHandlerEqual('Repository.reconcile',
 
2557
            smart_repo.SmartServerRepositoryReconcile)
 
2558
        self.assertHandlerEqual('Repository.tarball',
 
2559
            smart_repo.SmartServerRepositoryTarball)
 
2560
        self.assertHandlerEqual('Repository.unlock',
 
2561
            smart_repo.SmartServerRepositoryUnlock)
 
2562
        self.assertHandlerEqual('Repository.start_write_group',
 
2563
            smart_repo.SmartServerRepositoryStartWriteGroup)
 
2564
        self.assertHandlerEqual('Repository.check_write_group',
 
2565
            smart_repo.SmartServerRepositoryCheckWriteGroup)
 
2566
        self.assertHandlerEqual('Repository.commit_write_group',
 
2567
            smart_repo.SmartServerRepositoryCommitWriteGroup)
 
2568
        self.assertHandlerEqual('Repository.abort_write_group',
 
2569
            smart_repo.SmartServerRepositoryAbortWriteGroup)
 
2570
        self.assertHandlerEqual('VersionedFileRepository.get_serializer_format',
 
2571
            smart_repo.SmartServerRepositoryGetSerializerFormat)
 
2572
        self.assertHandlerEqual('VersionedFileRepository.get_inventories',
 
2573
            smart_repo.SmartServerRepositoryGetInventories)
 
2574
        self.assertHandlerEqual('Transport.is_readonly',
 
2575
            smart_req.SmartServerIsReadonly)
 
2576
 
 
2577
 
 
2578
class SmartTCPServerHookTests(tests.TestCaseWithMemoryTransport):
 
2579
    """Tests for SmartTCPServer hooks."""
 
2580
 
 
2581
    def setUp(self):
 
2582
        super(SmartTCPServerHookTests, self).setUp()
 
2583
        self.server = server.SmartTCPServer(self.get_transport())
 
2584
 
 
2585
    def test_run_server_started_hooks(self):
 
2586
        """Test the server started hooks get fired properly."""
 
2587
        started_calls = []
 
2588
        server.SmartTCPServer.hooks.install_named_hook('server_started',
 
2589
            lambda backing_urls, url: started_calls.append((backing_urls, url)),
 
2590
            None)
 
2591
        started_ex_calls = []
 
2592
        server.SmartTCPServer.hooks.install_named_hook('server_started_ex',
 
2593
            lambda backing_urls, url: started_ex_calls.append((backing_urls, url)),
 
2594
            None)
 
2595
        self.server._sockname = ('example.com', 42)
 
2596
        self.server.run_server_started_hooks()
 
2597
        self.assertEquals(started_calls,
 
2598
            [([self.get_transport().base], 'bzr://example.com:42/')])
 
2599
        self.assertEquals(started_ex_calls,
 
2600
            [([self.get_transport().base], self.server)])
 
2601
 
 
2602
    def test_run_server_started_hooks_ipv6(self):
 
2603
        """Test that socknames can contain 4-tuples."""
 
2604
        self.server._sockname = ('::', 42, 0, 0)
 
2605
        started_calls = []
 
2606
        server.SmartTCPServer.hooks.install_named_hook('server_started',
 
2607
            lambda backing_urls, url: started_calls.append((backing_urls, url)),
 
2608
            None)
 
2609
        self.server.run_server_started_hooks()
 
2610
        self.assertEquals(started_calls,
 
2611
                [([self.get_transport().base], 'bzr://:::42/')])
 
2612
 
 
2613
    def test_run_server_stopped_hooks(self):
 
2614
        """Test the server stopped hooks."""
 
2615
        self.server._sockname = ('example.com', 42)
 
2616
        stopped_calls = []
 
2617
        server.SmartTCPServer.hooks.install_named_hook('server_stopped',
 
2618
            lambda backing_urls, url: stopped_calls.append((backing_urls, url)),
 
2619
            None)
 
2620
        self.server.run_server_stopped_hooks()
 
2621
        self.assertEquals(stopped_calls,
 
2622
            [([self.get_transport().base], 'bzr://example.com:42/')])
 
2623
 
 
2624
 
 
2625
class TestSmartServerRepositoryPack(tests.TestCaseWithMemoryTransport):
 
2626
 
 
2627
    def test_pack(self):
 
2628
        backing = self.get_transport()
 
2629
        request = smart_repo.SmartServerRepositoryPack(backing)
 
2630
        tree = self.make_branch_and_memory_tree('.')
 
2631
        repo_token = tree.branch.repository.lock_write().repository_token
 
2632
 
 
2633
        self.assertIs(None, request.execute('', repo_token, False))
 
2634
 
 
2635
        self.assertEqual(
 
2636
            smart_req.SuccessfulSmartServerResponse(('ok', ), ),
 
2637
            request.do_body(''))
 
2638
 
 
2639
 
 
2640
class TestSmartServerRepositoryGetInventories(tests.TestCaseWithTransport):
 
2641
 
 
2642
    def _get_serialized_inventory_delta(self, repository, base_revid, revid):
 
2643
        base_inv = repository.revision_tree(base_revid).inventory
 
2644
        inv = repository.revision_tree(revid).inventory
 
2645
        inv_delta = inv._make_delta(base_inv)
 
2646
        serializer = inventory_delta.InventoryDeltaSerializer(True, False)
 
2647
        return "".join(serializer.delta_to_lines(base_revid, revid, inv_delta))
 
2648
 
 
2649
    def test_single(self):
 
2650
        backing = self.get_transport()
 
2651
        request = smart_repo.SmartServerRepositoryGetInventories(backing)
 
2652
        t = self.make_branch_and_tree('.', format='2a')
 
2653
        self.addCleanup(t.lock_write().unlock)
 
2654
        self.build_tree_contents([("file", "somecontents")])
 
2655
        t.add(["file"], ["thefileid"])
 
2656
        t.commit(rev_id='somerev', message="add file")
 
2657
        self.assertIs(None, request.execute('', 'unordered'))
 
2658
        response = request.do_body("somerev\n")
 
2659
        self.assertTrue(response.is_successful())
 
2660
        self.assertEquals(response.args, ("ok", ))
 
2661
        stream = [('inventory-deltas', [
 
2662
            versionedfile.FulltextContentFactory('somerev', None, None,
 
2663
                self._get_serialized_inventory_delta(
 
2664
                    t.branch.repository, 'null:', 'somerev'))])]
 
2665
        fmt = bzrdir.format_registry.get('2a')().repository_format
 
2666
        self.assertEquals(
 
2667
            "".join(response.body_stream),
 
2668
            "".join(smart_repo._stream_to_byte_stream(stream, fmt)))
 
2669
 
 
2670
    def test_empty(self):
 
2671
        backing = self.get_transport()
 
2672
        request = smart_repo.SmartServerRepositoryGetInventories(backing)
 
2673
        t = self.make_branch_and_tree('.', format='2a')
 
2674
        self.addCleanup(t.lock_write().unlock)
 
2675
        self.build_tree_contents([("file", "somecontents")])
 
2676
        t.add(["file"], ["thefileid"])
 
2677
        t.commit(rev_id='somerev', message="add file")
 
2678
        self.assertIs(None, request.execute('', 'unordered'))
 
2679
        response = request.do_body("")
 
2680
        self.assertTrue(response.is_successful())
 
2681
        self.assertEquals(response.args, ("ok", ))
 
2682
        self.assertEquals("".join(response.body_stream),
 
2683
            "Bazaar pack format 1 (introduced in 0.18)\nB54\n\nBazaar repository format 2a (needs bzr 1.16 or later)\nE")