~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_smart.py

  • Committer: Martin Pool
  • Date: 2009-03-03 01:45:32 UTC
  • mto: (4070.4.5 gnu-changelog)
  • mto: This revision was merged to the branch mainline in revision 4081.
  • Revision ID: mbp@sourcefrog.net-20090303014532-d223fxy97cb1og8f
Recommend setting timestamp in BranchBuilder

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2006, 2007 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
"""Tests for the smart wire/domain protocol.
 
18
 
 
19
This module contains tests for the domain-level smart requests and responses,
 
20
such as the 'Branch.lock_write' request. Many of these use specific disk
 
21
formats to exercise calls that only make sense for formats with specific
 
22
properties.
 
23
 
 
24
Tests for low-level protocol encoding are found in test_smart_transport.
 
25
"""
 
26
 
 
27
import bz2
 
28
from cStringIO import StringIO
 
29
import tarfile
 
30
 
 
31
from bzrlib import (
 
32
    bzrdir,
 
33
    errors,
 
34
    pack,
 
35
    smart,
 
36
    tests,
 
37
    urlutils,
 
38
    )
 
39
from bzrlib.branch import Branch, BranchReferenceFormat
 
40
import bzrlib.smart.branch
 
41
import bzrlib.smart.bzrdir
 
42
import bzrlib.smart.repository
 
43
from bzrlib.smart.request import (
 
44
    FailedSmartServerResponse,
 
45
    SmartServerRequest,
 
46
    SmartServerResponse,
 
47
    SuccessfulSmartServerResponse,
 
48
    )
 
49
from bzrlib.tests import (
 
50
    iter_suite_tests,
 
51
    split_suite_by_re,
 
52
    TestScenarioApplier,
 
53
    )
 
54
from bzrlib.transport import chroot, get_transport
 
55
from bzrlib.util import bencode
 
56
 
 
57
 
 
58
def load_tests(standard_tests, module, loader):
 
59
    """Multiply tests version and protocol consistency."""
 
60
    # FindRepository tests.
 
61
    bzrdir_mod = bzrlib.smart.bzrdir
 
62
    applier = TestScenarioApplier()
 
63
    applier.scenarios = [
 
64
        ("find_repository", {
 
65
            "_request_class":bzrdir_mod.SmartServerRequestFindRepositoryV1}),
 
66
        ("find_repositoryV2", {
 
67
            "_request_class":bzrdir_mod.SmartServerRequestFindRepositoryV2}),
 
68
        ("find_repositoryV3", {
 
69
            "_request_class":bzrdir_mod.SmartServerRequestFindRepositoryV3}),
 
70
        ]
 
71
    to_adapt, result = split_suite_by_re(standard_tests,
 
72
        "TestSmartServerRequestFindRepository")
 
73
    v2_only, v1_and_2 = split_suite_by_re(to_adapt,
 
74
        "_v2")
 
75
    for test in iter_suite_tests(v1_and_2):
 
76
        result.addTests(applier.adapt(test))
 
77
    del applier.scenarios[0]
 
78
    for test in iter_suite_tests(v2_only):
 
79
        result.addTests(applier.adapt(test))
 
80
    return result
 
81
 
 
82
 
 
83
class TestCaseWithChrootedTransport(tests.TestCaseWithTransport):
 
84
 
 
85
    def setUp(self):
 
86
        tests.TestCaseWithTransport.setUp(self)
 
87
        self._chroot_server = None
 
88
 
 
89
    def get_transport(self, relpath=None):
 
90
        if self._chroot_server is None:
 
91
            backing_transport = tests.TestCaseWithTransport.get_transport(self)
 
92
            self._chroot_server = chroot.ChrootServer(backing_transport)
 
93
            self._chroot_server.setUp()
 
94
            self.addCleanup(self._chroot_server.tearDown)
 
95
        t = get_transport(self._chroot_server.get_url())
 
96
        if relpath is not None:
 
97
            t = t.clone(relpath)
 
98
        return t
 
99
 
 
100
 
 
101
class TestCaseWithSmartMedium(tests.TestCaseWithTransport):
 
102
 
 
103
    def setUp(self):
 
104
        super(TestCaseWithSmartMedium, self).setUp()
 
105
        # We're allowed to set  the transport class here, so that we don't use
 
106
        # the default or a parameterized class, but rather use the
 
107
        # TestCaseWithTransport infrastructure to set up a smart server and
 
108
        # transport.
 
109
        self.transport_server = self.make_transport_server
 
110
 
 
111
    def make_transport_server(self):
 
112
        return smart.server.SmartTCPServer_for_testing('-' + self.id())
 
113
 
 
114
    def get_smart_medium(self):
 
115
        """Get a smart medium to use in tests."""
 
116
        return self.get_transport().get_smart_medium()
 
117
 
 
118
 
 
119
class TestSmartServerResponse(tests.TestCase):
 
120
 
 
121
    def test__eq__(self):
 
122
        self.assertEqual(SmartServerResponse(('ok', )),
 
123
            SmartServerResponse(('ok', )))
 
124
        self.assertEqual(SmartServerResponse(('ok', ), 'body'),
 
125
            SmartServerResponse(('ok', ), 'body'))
 
126
        self.assertNotEqual(SmartServerResponse(('ok', )),
 
127
            SmartServerResponse(('notok', )))
 
128
        self.assertNotEqual(SmartServerResponse(('ok', ), 'body'),
 
129
            SmartServerResponse(('ok', )))
 
130
        self.assertNotEqual(None,
 
131
            SmartServerResponse(('ok', )))
 
132
 
 
133
    def test__str__(self):
 
134
        """SmartServerResponses can be stringified."""
 
135
        self.assertEqual(
 
136
            "<SuccessfulSmartServerResponse args=('args',) body='body'>",
 
137
            str(SuccessfulSmartServerResponse(('args',), 'body')))
 
138
        self.assertEqual(
 
139
            "<FailedSmartServerResponse args=('args',) body='body'>",
 
140
            str(FailedSmartServerResponse(('args',), 'body')))
 
141
 
 
142
 
 
143
class TestSmartServerRequest(tests.TestCaseWithMemoryTransport):
 
144
 
 
145
    def test_translate_client_path(self):
 
146
        transport = self.get_transport()
 
147
        request = SmartServerRequest(transport, 'foo/')
 
148
        self.assertEqual('./', request.translate_client_path('foo/'))
 
149
        self.assertRaises(
 
150
            errors.InvalidURLJoin, request.translate_client_path, 'foo/..')
 
151
        self.assertRaises(
 
152
            errors.PathNotChild, request.translate_client_path, '/')
 
153
        self.assertRaises(
 
154
            errors.PathNotChild, request.translate_client_path, 'bar/')
 
155
        self.assertEqual('./baz', request.translate_client_path('foo/baz'))
 
156
 
 
157
    def test_transport_from_client_path(self):
 
158
        transport = self.get_transport()
 
159
        request = SmartServerRequest(transport, 'foo/')
 
160
        self.assertEqual(
 
161
            transport.base,
 
162
            request.transport_from_client_path('foo/').base)
 
163
 
 
164
 
 
165
class TestSmartServerRequestCreateRepository(tests.TestCaseWithMemoryTransport):
 
166
    """Tests for BzrDir.create_repository."""
 
167
 
 
168
    def test_makes_repository(self):
 
169
        """When there is a bzrdir present, the call succeeds."""
 
170
        backing = self.get_transport()
 
171
        self.make_bzrdir('.')
 
172
        request_class = bzrlib.smart.bzrdir.SmartServerRequestCreateRepository
 
173
        request = request_class(backing)
 
174
        reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
175
        reference_format = reference_bzrdir_format.repository_format
 
176
        network_name = reference_format.network_name()
 
177
        expected = SuccessfulSmartServerResponse(
 
178
            ('ok', 'no', 'no', 'no', network_name))
 
179
        self.assertEqual(expected, request.execute('', network_name, 'True'))
 
180
 
 
181
 
 
182
class TestSmartServerRequestFindRepository(tests.TestCaseWithMemoryTransport):
 
183
    """Tests for BzrDir.find_repository."""
 
184
 
 
185
    def test_no_repository(self):
 
186
        """When there is no repository to be found, ('norepository', ) is returned."""
 
187
        backing = self.get_transport()
 
188
        request = self._request_class(backing)
 
189
        self.make_bzrdir('.')
 
190
        self.assertEqual(SmartServerResponse(('norepository', )),
 
191
            request.execute(''))
 
192
 
 
193
    def test_nonshared_repository(self):
 
194
        # nonshared repositorys only allow 'find' to return a handle when the
 
195
        # path the repository is being searched on is the same as that that
 
196
        # the repository is at.
 
197
        backing = self.get_transport()
 
198
        request = self._request_class(backing)
 
199
        result = self._make_repository_and_result()
 
200
        self.assertEqual(result, request.execute(''))
 
201
        self.make_bzrdir('subdir')
 
202
        self.assertEqual(SmartServerResponse(('norepository', )),
 
203
            request.execute('subdir'))
 
204
 
 
205
    def _make_repository_and_result(self, shared=False, format=None):
 
206
        """Convenience function to setup a repository.
 
207
 
 
208
        :result: The SmartServerResponse to expect when opening it.
 
209
        """
 
210
        repo = self.make_repository('.', shared=shared, format=format)
 
211
        if repo.supports_rich_root():
 
212
            rich_root = 'yes'
 
213
        else:
 
214
            rich_root = 'no'
 
215
        if repo._format.supports_tree_reference:
 
216
            subtrees = 'yes'
 
217
        else:
 
218
            subtrees = 'no'
 
219
        if (smart.bzrdir.SmartServerRequestFindRepositoryV3 ==
 
220
            self._request_class):
 
221
            return SuccessfulSmartServerResponse(
 
222
                ('ok', '', rich_root, subtrees, 'no',
 
223
                 repo._format.network_name()))
 
224
        elif (smart.bzrdir.SmartServerRequestFindRepositoryV2 ==
 
225
            self._request_class):
 
226
            # All tests so far are on formats, and for non-external
 
227
            # repositories.
 
228
            return SuccessfulSmartServerResponse(
 
229
                ('ok', '', rich_root, subtrees, 'no'))
 
230
        else:
 
231
            return SuccessfulSmartServerResponse(('ok', '', rich_root, subtrees))
 
232
 
 
233
    def test_shared_repository(self):
 
234
        """When there is a shared repository, we get 'ok', 'relpath-to-repo'."""
 
235
        backing = self.get_transport()
 
236
        request = self._request_class(backing)
 
237
        result = self._make_repository_and_result(shared=True)
 
238
        self.assertEqual(result, request.execute(''))
 
239
        self.make_bzrdir('subdir')
 
240
        result2 = SmartServerResponse(result.args[0:1] + ('..', ) + result.args[2:])
 
241
        self.assertEqual(result2,
 
242
            request.execute('subdir'))
 
243
        self.make_bzrdir('subdir/deeper')
 
244
        result3 = SmartServerResponse(result.args[0:1] + ('../..', ) + result.args[2:])
 
245
        self.assertEqual(result3,
 
246
            request.execute('subdir/deeper'))
 
247
 
 
248
    def test_rich_root_and_subtree_encoding(self):
 
249
        """Test for the format attributes for rich root and subtree support."""
 
250
        backing = self.get_transport()
 
251
        request = self._request_class(backing)
 
252
        result = self._make_repository_and_result(format='dirstate-with-subtree')
 
253
        # check the test will be valid
 
254
        self.assertEqual('yes', result.args[2])
 
255
        self.assertEqual('yes', result.args[3])
 
256
        self.assertEqual(result, request.execute(''))
 
257
 
 
258
    def test_supports_external_lookups_no_v2(self):
 
259
        """Test for the supports_external_lookups attribute."""
 
260
        backing = self.get_transport()
 
261
        request = self._request_class(backing)
 
262
        result = self._make_repository_and_result(format='dirstate-with-subtree')
 
263
        # check the test will be valid
 
264
        self.assertEqual('no', result.args[4])
 
265
        self.assertEqual(result, request.execute(''))
 
266
 
 
267
 
 
268
class TestSmartServerRequestInitializeBzrDir(tests.TestCaseWithMemoryTransport):
 
269
 
 
270
    def test_empty_dir(self):
 
271
        """Initializing an empty dir should succeed and do it."""
 
272
        backing = self.get_transport()
 
273
        request = smart.bzrdir.SmartServerRequestInitializeBzrDir(backing)
 
274
        self.assertEqual(SmartServerResponse(('ok', )),
 
275
            request.execute(''))
 
276
        made_dir = bzrdir.BzrDir.open_from_transport(backing)
 
277
        # no branch, tree or repository is expected with the current
 
278
        # default formart.
 
279
        self.assertRaises(errors.NoWorkingTree, made_dir.open_workingtree)
 
280
        self.assertRaises(errors.NotBranchError, made_dir.open_branch)
 
281
        self.assertRaises(errors.NoRepositoryPresent, made_dir.open_repository)
 
282
 
 
283
    def test_missing_dir(self):
 
284
        """Initializing a missing directory should fail like the bzrdir api."""
 
285
        backing = self.get_transport()
 
286
        request = smart.bzrdir.SmartServerRequestInitializeBzrDir(backing)
 
287
        self.assertRaises(errors.NoSuchFile,
 
288
            request.execute, 'subdir')
 
289
 
 
290
    def test_initialized_dir(self):
 
291
        """Initializing an extant bzrdir should fail like the bzrdir api."""
 
292
        backing = self.get_transport()
 
293
        request = smart.bzrdir.SmartServerRequestInitializeBzrDir(backing)
 
294
        self.make_bzrdir('subdir')
 
295
        self.assertRaises(errors.FileExists,
 
296
            request.execute, 'subdir')
 
297
 
 
298
 
 
299
class TestSmartServerRequestOpenBranch(TestCaseWithChrootedTransport):
 
300
 
 
301
    def test_no_branch(self):
 
302
        """When there is no branch, ('nobranch', ) is returned."""
 
303
        backing = self.get_transport()
 
304
        request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
 
305
        self.make_bzrdir('.')
 
306
        self.assertEqual(SmartServerResponse(('nobranch', )),
 
307
            request.execute(''))
 
308
 
 
309
    def test_branch(self):
 
310
        """When there is a branch, 'ok' is returned."""
 
311
        backing = self.get_transport()
 
312
        request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
 
313
        self.make_branch('.')
 
314
        self.assertEqual(SmartServerResponse(('ok', '')),
 
315
            request.execute(''))
 
316
 
 
317
    def test_branch_reference(self):
 
318
        """When there is a branch reference, the reference URL is returned."""
 
319
        backing = self.get_transport()
 
320
        request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
 
321
        branch = self.make_branch('branch')
 
322
        checkout = branch.create_checkout('reference',lightweight=True)
 
323
        reference_url = BranchReferenceFormat().get_reference(checkout.bzrdir)
 
324
        self.assertFileEqual(reference_url, 'reference/.bzr/branch/location')
 
325
        self.assertEqual(SmartServerResponse(('ok', reference_url)),
 
326
            request.execute('reference'))
 
327
 
 
328
 
 
329
class TestSmartServerRequestRevisionHistory(tests.TestCaseWithMemoryTransport):
 
330
 
 
331
    def test_empty(self):
 
332
        """For an empty branch, the body is empty."""
 
333
        backing = self.get_transport()
 
334
        request = smart.branch.SmartServerRequestRevisionHistory(backing)
 
335
        self.make_branch('.')
 
336
        self.assertEqual(SmartServerResponse(('ok', ), ''),
 
337
            request.execute(''))
 
338
 
 
339
    def test_not_empty(self):
 
340
        """For a non-empty branch, the body is empty."""
 
341
        backing = self.get_transport()
 
342
        request = smart.branch.SmartServerRequestRevisionHistory(backing)
 
343
        tree = self.make_branch_and_memory_tree('.')
 
344
        tree.lock_write()
 
345
        tree.add('')
 
346
        r1 = tree.commit('1st commit')
 
347
        r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
 
348
        tree.unlock()
 
349
        self.assertEqual(
 
350
            SmartServerResponse(('ok', ), ('\x00'.join([r1, r2]))),
 
351
            request.execute(''))
 
352
 
 
353
 
 
354
class TestSmartServerBranchRequest(tests.TestCaseWithMemoryTransport):
 
355
 
 
356
    def test_no_branch(self):
 
357
        """When there is a bzrdir and no branch, NotBranchError is raised."""
 
358
        backing = self.get_transport()
 
359
        request = smart.branch.SmartServerBranchRequest(backing)
 
360
        self.make_bzrdir('.')
 
361
        self.assertRaises(errors.NotBranchError,
 
362
            request.execute, '')
 
363
 
 
364
    def test_branch_reference(self):
 
365
        """When there is a branch reference, NotBranchError is raised."""
 
366
        backing = self.get_transport()
 
367
        request = smart.branch.SmartServerBranchRequest(backing)
 
368
        branch = self.make_branch('branch')
 
369
        checkout = branch.create_checkout('reference',lightweight=True)
 
370
        self.assertRaises(errors.NotBranchError,
 
371
            request.execute, 'checkout')
 
372
 
 
373
 
 
374
class TestSmartServerBranchRequestLastRevisionInfo(tests.TestCaseWithMemoryTransport):
 
375
 
 
376
    def test_empty(self):
 
377
        """For an empty branch, the result is ('ok', '0', 'null:')."""
 
378
        backing = self.get_transport()
 
379
        request = smart.branch.SmartServerBranchRequestLastRevisionInfo(backing)
 
380
        self.make_branch('.')
 
381
        self.assertEqual(SmartServerResponse(('ok', '0', 'null:')),
 
382
            request.execute(''))
 
383
 
 
384
    def test_not_empty(self):
 
385
        """For a non-empty branch, the result is ('ok', 'revno', 'revid')."""
 
386
        backing = self.get_transport()
 
387
        request = smart.branch.SmartServerBranchRequestLastRevisionInfo(backing)
 
388
        tree = self.make_branch_and_memory_tree('.')
 
389
        tree.lock_write()
 
390
        tree.add('')
 
391
        rev_id_utf8 = u'\xc8'.encode('utf-8')
 
392
        r1 = tree.commit('1st commit')
 
393
        r2 = tree.commit('2nd commit', rev_id=rev_id_utf8)
 
394
        tree.unlock()
 
395
        self.assertEqual(
 
396
            SmartServerResponse(('ok', '2', rev_id_utf8)),
 
397
            request.execute(''))
 
398
 
 
399
 
 
400
class TestSmartServerBranchRequestGetConfigFile(tests.TestCaseWithMemoryTransport):
 
401
 
 
402
    def test_default(self):
 
403
        """With no file, we get empty content."""
 
404
        backing = self.get_transport()
 
405
        request = smart.branch.SmartServerBranchGetConfigFile(backing)
 
406
        branch = self.make_branch('.')
 
407
        # there should be no file by default
 
408
        content = ''
 
409
        self.assertEqual(SmartServerResponse(('ok', ), content),
 
410
            request.execute(''))
 
411
 
 
412
    def test_with_content(self):
 
413
        # SmartServerBranchGetConfigFile should return the content from
 
414
        # branch.control_files.get('branch.conf') for now - in the future it may
 
415
        # perform more complex processing.
 
416
        backing = self.get_transport()
 
417
        request = smart.branch.SmartServerBranchGetConfigFile(backing)
 
418
        branch = self.make_branch('.')
 
419
        branch._transport.put_bytes('branch.conf', 'foo bar baz')
 
420
        self.assertEqual(SmartServerResponse(('ok', ), 'foo bar baz'),
 
421
            request.execute(''))
 
422
 
 
423
 
 
424
class SetLastRevisionTestBase(tests.TestCaseWithMemoryTransport):
 
425
    """Base test case for verbs that implement set_last_revision."""
 
426
 
 
427
    def setUp(self):
 
428
        tests.TestCaseWithMemoryTransport.setUp(self)
 
429
        backing_transport = self.get_transport()
 
430
        self.request = self.request_class(backing_transport)
 
431
        self.tree = self.make_branch_and_memory_tree('.')
 
432
 
 
433
    def lock_branch(self):
 
434
        b = self.tree.branch
 
435
        branch_token = b.lock_write()
 
436
        repo_token = b.repository.lock_write()
 
437
        b.repository.unlock()
 
438
        return branch_token, repo_token
 
439
 
 
440
    def unlock_branch(self):
 
441
        self.tree.branch.unlock()
 
442
 
 
443
    def set_last_revision(self, revision_id, revno):
 
444
        branch_token, repo_token = self.lock_branch()
 
445
        response = self._set_last_revision(
 
446
            revision_id, revno, branch_token, repo_token)
 
447
        self.unlock_branch()
 
448
        return response
 
449
 
 
450
    def assertRequestSucceeds(self, revision_id, revno):
 
451
        response = self.set_last_revision(revision_id, revno)
 
452
        self.assertEqual(SuccessfulSmartServerResponse(('ok',)), response)
 
453
 
 
454
 
 
455
class TestSetLastRevisionVerbMixin(object):
 
456
    """Mixin test case for verbs that implement set_last_revision."""
 
457
 
 
458
    def test_set_null_to_null(self):
 
459
        """An empty branch can have its last revision set to 'null:'."""
 
460
        self.assertRequestSucceeds('null:', 0)
 
461
 
 
462
    def test_NoSuchRevision(self):
 
463
        """If the revision_id is not present, the verb returns NoSuchRevision.
 
464
        """
 
465
        revision_id = 'non-existent revision'
 
466
        self.assertEqual(
 
467
            FailedSmartServerResponse(('NoSuchRevision', revision_id)),
 
468
            self.set_last_revision(revision_id, 1))
 
469
 
 
470
    def make_tree_with_two_commits(self):
 
471
        self.tree.lock_write()
 
472
        self.tree.add('')
 
473
        rev_id_utf8 = u'\xc8'.encode('utf-8')
 
474
        r1 = self.tree.commit('1st commit', rev_id=rev_id_utf8)
 
475
        r2 = self.tree.commit('2nd commit', rev_id='rev-2')
 
476
        self.tree.unlock()
 
477
 
 
478
    def test_branch_last_revision_info_is_updated(self):
 
479
        """A branch's tip can be set to a revision that is present in its
 
480
        repository.
 
481
        """
 
482
        # Make a branch with an empty revision history, but two revisions in
 
483
        # its repository.
 
484
        self.make_tree_with_two_commits()
 
485
        rev_id_utf8 = u'\xc8'.encode('utf-8')
 
486
        self.tree.branch.set_revision_history([])
 
487
        self.assertEqual(
 
488
            (0, 'null:'), self.tree.branch.last_revision_info())
 
489
        # We can update the branch to a revision that is present in the
 
490
        # repository.
 
491
        self.assertRequestSucceeds(rev_id_utf8, 1)
 
492
        self.assertEqual(
 
493
            (1, rev_id_utf8), self.tree.branch.last_revision_info())
 
494
 
 
495
    def test_branch_last_revision_info_rewind(self):
 
496
        """A branch's tip can be set to a revision that is an ancestor of the
 
497
        current tip.
 
498
        """
 
499
        self.make_tree_with_two_commits()
 
500
        rev_id_utf8 = u'\xc8'.encode('utf-8')
 
501
        self.assertEqual(
 
502
            (2, 'rev-2'), self.tree.branch.last_revision_info())
 
503
        self.assertRequestSucceeds(rev_id_utf8, 1)
 
504
        self.assertEqual(
 
505
            (1, rev_id_utf8), self.tree.branch.last_revision_info())
 
506
 
 
507
    def test_TipChangeRejected(self):
 
508
        """If a pre_change_branch_tip hook raises TipChangeRejected, the verb
 
509
        returns TipChangeRejected.
 
510
        """
 
511
        rejection_message = u'rejection message\N{INTERROBANG}'
 
512
        def hook_that_rejects(params):
 
513
            raise errors.TipChangeRejected(rejection_message)
 
514
        Branch.hooks.install_named_hook(
 
515
            'pre_change_branch_tip', hook_that_rejects, None)
 
516
        self.assertEqual(
 
517
            FailedSmartServerResponse(
 
518
                ('TipChangeRejected', rejection_message.encode('utf-8'))),
 
519
            self.set_last_revision('null:', 0))
 
520
 
 
521
 
 
522
class TestSmartServerBranchRequestSetLastRevision(
 
523
        SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
 
524
    """Tests for Branch.set_last_revision verb."""
 
525
 
 
526
    request_class = smart.branch.SmartServerBranchRequestSetLastRevision
 
527
 
 
528
    def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
 
529
        return self.request.execute(
 
530
            '', branch_token, repo_token, revision_id)
 
531
 
 
532
 
 
533
class TestSmartServerBranchRequestSetLastRevisionInfo(
 
534
        SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
 
535
    """Tests for Branch.set_last_revision_info verb."""
 
536
 
 
537
    request_class = smart.branch.SmartServerBranchRequestSetLastRevisionInfo
 
538
 
 
539
    def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
 
540
        return self.request.execute(
 
541
            '', branch_token, repo_token, revno, revision_id)
 
542
 
 
543
    def test_NoSuchRevision(self):
 
544
        """Branch.set_last_revision_info does not have to return
 
545
        NoSuchRevision if the revision_id is absent.
 
546
        """
 
547
        raise tests.TestNotApplicable()
 
548
 
 
549
 
 
550
class TestSmartServerBranchRequestSetLastRevisionEx(
 
551
        SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
 
552
    """Tests for Branch.set_last_revision_ex verb."""
 
553
 
 
554
    request_class = smart.branch.SmartServerBranchRequestSetLastRevisionEx
 
555
 
 
556
    def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
 
557
        return self.request.execute(
 
558
            '', branch_token, repo_token, revision_id, 0, 0)
 
559
 
 
560
    def assertRequestSucceeds(self, revision_id, revno):
 
561
        response = self.set_last_revision(revision_id, revno)
 
562
        self.assertEqual(
 
563
            SuccessfulSmartServerResponse(('ok', revno, revision_id)),
 
564
            response)
 
565
 
 
566
    def test_branch_last_revision_info_rewind(self):
 
567
        """A branch's tip can be set to a revision that is an ancestor of the
 
568
        current tip, but only if allow_overwrite_descendant is passed.
 
569
        """
 
570
        self.make_tree_with_two_commits()
 
571
        rev_id_utf8 = u'\xc8'.encode('utf-8')
 
572
        self.assertEqual(
 
573
            (2, 'rev-2'), self.tree.branch.last_revision_info())
 
574
        # If allow_overwrite_descendant flag is 0, then trying to set the tip
 
575
        # to an older revision ID has no effect.
 
576
        branch_token, repo_token = self.lock_branch()
 
577
        response = self.request.execute(
 
578
            '', branch_token, repo_token, rev_id_utf8, 0, 0)
 
579
        self.assertEqual(
 
580
            SuccessfulSmartServerResponse(('ok', 2, 'rev-2')),
 
581
            response)
 
582
        self.assertEqual(
 
583
            (2, 'rev-2'), self.tree.branch.last_revision_info())
 
584
 
 
585
        # If allow_overwrite_descendant flag is 1, then setting the tip to an
 
586
        # ancestor works.
 
587
        response = self.request.execute(
 
588
            '', branch_token, repo_token, rev_id_utf8, 0, 1)
 
589
        self.assertEqual(
 
590
            SuccessfulSmartServerResponse(('ok', 1, rev_id_utf8)),
 
591
            response)
 
592
        self.unlock_branch()
 
593
        self.assertEqual(
 
594
            (1, rev_id_utf8), self.tree.branch.last_revision_info())
 
595
 
 
596
    def make_branch_with_divergent_history(self):
 
597
        """Make a branch with divergent history in its repo.
 
598
 
 
599
        The branch's tip will be 'child-2', and the repo will also contain
 
600
        'child-1', which diverges from a common base revision.
 
601
        """
 
602
        self.tree.lock_write()
 
603
        self.tree.add('')
 
604
        r1 = self.tree.commit('1st commit')
 
605
        revno_1, revid_1 = self.tree.branch.last_revision_info()
 
606
        r2 = self.tree.commit('2nd commit', rev_id='child-1')
 
607
        # Undo the second commit
 
608
        self.tree.branch.set_last_revision_info(revno_1, revid_1)
 
609
        self.tree.set_parent_ids([revid_1])
 
610
        # Make a new second commit, child-2.  child-2 has diverged from
 
611
        # child-1.
 
612
        new_r2 = self.tree.commit('2nd commit', rev_id='child-2')
 
613
        self.tree.unlock()
 
614
 
 
615
    def test_not_allow_diverged(self):
 
616
        """If allow_diverged is not passed, then setting a divergent history
 
617
        returns a Diverged error.
 
618
        """
 
619
        self.make_branch_with_divergent_history()
 
620
        self.assertEqual(
 
621
            FailedSmartServerResponse(('Diverged',)),
 
622
            self.set_last_revision('child-1', 2))
 
623
        # The branch tip was not changed.
 
624
        self.assertEqual('child-2', self.tree.branch.last_revision())
 
625
 
 
626
    def test_allow_diverged(self):
 
627
        """If allow_diverged is passed, then setting a divergent history
 
628
        succeeds.
 
629
        """
 
630
        self.make_branch_with_divergent_history()
 
631
        branch_token, repo_token = self.lock_branch()
 
632
        response = self.request.execute(
 
633
            '', branch_token, repo_token, 'child-1', 1, 0)
 
634
        self.assertEqual(
 
635
            SuccessfulSmartServerResponse(('ok', 2, 'child-1')),
 
636
            response)
 
637
        self.unlock_branch()
 
638
        # The branch tip was changed.
 
639
        self.assertEqual('child-1', self.tree.branch.last_revision())
 
640
 
 
641
 
 
642
class TestSmartServerBranchRequestGetStackedOnURL(tests.TestCaseWithMemoryTransport):
 
643
 
 
644
    def test_get_stacked_on_url(self):
 
645
        base_branch = self.make_branch('base', format='1.6')
 
646
        stacked_branch = self.make_branch('stacked', format='1.6')
 
647
        # typically should be relative
 
648
        stacked_branch.set_stacked_on_url('../base')
 
649
        request = smart.branch.SmartServerBranchRequestGetStackedOnURL(
 
650
            self.get_transport())
 
651
        response = request.execute('stacked')
 
652
        self.assertEquals(
 
653
            SmartServerResponse(('ok', '../base')),
 
654
            response)
 
655
 
 
656
 
 
657
class TestSmartServerBranchRequestLockWrite(tests.TestCaseWithMemoryTransport):
 
658
 
 
659
    def setUp(self):
 
660
        tests.TestCaseWithMemoryTransport.setUp(self)
 
661
 
 
662
    def test_lock_write_on_unlocked_branch(self):
 
663
        backing = self.get_transport()
 
664
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
 
665
        branch = self.make_branch('.', format='knit')
 
666
        repository = branch.repository
 
667
        response = request.execute('')
 
668
        branch_nonce = branch.control_files._lock.peek().get('nonce')
 
669
        repository_nonce = repository.control_files._lock.peek().get('nonce')
 
670
        self.assertEqual(
 
671
            SmartServerResponse(('ok', branch_nonce, repository_nonce)),
 
672
            response)
 
673
        # The branch (and associated repository) is now locked.  Verify that
 
674
        # with a new branch object.
 
675
        new_branch = repository.bzrdir.open_branch()
 
676
        self.assertRaises(errors.LockContention, new_branch.lock_write)
 
677
 
 
678
    def test_lock_write_on_locked_branch(self):
 
679
        backing = self.get_transport()
 
680
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
 
681
        branch = self.make_branch('.')
 
682
        branch.lock_write()
 
683
        branch.leave_lock_in_place()
 
684
        branch.unlock()
 
685
        response = request.execute('')
 
686
        self.assertEqual(
 
687
            SmartServerResponse(('LockContention',)), response)
 
688
 
 
689
    def test_lock_write_with_tokens_on_locked_branch(self):
 
690
        backing = self.get_transport()
 
691
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
 
692
        branch = self.make_branch('.', format='knit')
 
693
        branch_token = branch.lock_write()
 
694
        repo_token = branch.repository.lock_write()
 
695
        branch.repository.unlock()
 
696
        branch.leave_lock_in_place()
 
697
        branch.repository.leave_lock_in_place()
 
698
        branch.unlock()
 
699
        response = request.execute('',
 
700
                                   branch_token, repo_token)
 
701
        self.assertEqual(
 
702
            SmartServerResponse(('ok', branch_token, repo_token)), response)
 
703
 
 
704
    def test_lock_write_with_mismatched_tokens_on_locked_branch(self):
 
705
        backing = self.get_transport()
 
706
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
 
707
        branch = self.make_branch('.', format='knit')
 
708
        branch_token = branch.lock_write()
 
709
        repo_token = branch.repository.lock_write()
 
710
        branch.repository.unlock()
 
711
        branch.leave_lock_in_place()
 
712
        branch.repository.leave_lock_in_place()
 
713
        branch.unlock()
 
714
        response = request.execute('',
 
715
                                   branch_token+'xxx', repo_token)
 
716
        self.assertEqual(
 
717
            SmartServerResponse(('TokenMismatch',)), response)
 
718
 
 
719
    def test_lock_write_on_locked_repo(self):
 
720
        backing = self.get_transport()
 
721
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
 
722
        branch = self.make_branch('.', format='knit')
 
723
        branch.repository.lock_write()
 
724
        branch.repository.leave_lock_in_place()
 
725
        branch.repository.unlock()
 
726
        response = request.execute('')
 
727
        self.assertEqual(
 
728
            SmartServerResponse(('LockContention',)), response)
 
729
 
 
730
    def test_lock_write_on_readonly_transport(self):
 
731
        backing = self.get_readonly_transport()
 
732
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
 
733
        branch = self.make_branch('.')
 
734
        root = self.get_transport().clone('/')
 
735
        path = urlutils.relative_url(root.base, self.get_transport().base)
 
736
        response = request.execute(path)
 
737
        error_name, lock_str, why_str = response.args
 
738
        self.assertFalse(response.is_successful())
 
739
        self.assertEqual('LockFailed', error_name)
 
740
 
 
741
 
 
742
class TestSmartServerBranchRequestUnlock(tests.TestCaseWithMemoryTransport):
 
743
 
 
744
    def setUp(self):
 
745
        tests.TestCaseWithMemoryTransport.setUp(self)
 
746
 
 
747
    def test_unlock_on_locked_branch_and_repo(self):
 
748
        backing = self.get_transport()
 
749
        request = smart.branch.SmartServerBranchRequestUnlock(backing)
 
750
        branch = self.make_branch('.', format='knit')
 
751
        # Lock the branch
 
752
        branch_token = branch.lock_write()
 
753
        repo_token = branch.repository.lock_write()
 
754
        branch.repository.unlock()
 
755
        # Unlock the branch (and repo) object, leaving the physical locks
 
756
        # in place.
 
757
        branch.leave_lock_in_place()
 
758
        branch.repository.leave_lock_in_place()
 
759
        branch.unlock()
 
760
        response = request.execute('',
 
761
                                   branch_token, repo_token)
 
762
        self.assertEqual(
 
763
            SmartServerResponse(('ok',)), response)
 
764
        # The branch is now unlocked.  Verify that with a new branch
 
765
        # object.
 
766
        new_branch = branch.bzrdir.open_branch()
 
767
        new_branch.lock_write()
 
768
        new_branch.unlock()
 
769
 
 
770
    def test_unlock_on_unlocked_branch_unlocked_repo(self):
 
771
        backing = self.get_transport()
 
772
        request = smart.branch.SmartServerBranchRequestUnlock(backing)
 
773
        branch = self.make_branch('.', format='knit')
 
774
        response = request.execute(
 
775
            '', 'branch token', 'repo token')
 
776
        self.assertEqual(
 
777
            SmartServerResponse(('TokenMismatch',)), response)
 
778
 
 
779
    def test_unlock_on_unlocked_branch_locked_repo(self):
 
780
        backing = self.get_transport()
 
781
        request = smart.branch.SmartServerBranchRequestUnlock(backing)
 
782
        branch = self.make_branch('.', format='knit')
 
783
        # Lock the repository.
 
784
        repo_token = branch.repository.lock_write()
 
785
        branch.repository.leave_lock_in_place()
 
786
        branch.repository.unlock()
 
787
        # Issue branch lock_write request on the unlocked branch (with locked
 
788
        # repo).
 
789
        response = request.execute(
 
790
            '', 'branch token', repo_token)
 
791
        self.assertEqual(
 
792
            SmartServerResponse(('TokenMismatch',)), response)
 
793
 
 
794
 
 
795
class TestSmartServerRepositoryRequest(tests.TestCaseWithMemoryTransport):
 
796
 
 
797
    def test_no_repository(self):
 
798
        """Raise NoRepositoryPresent when there is a bzrdir and no repo."""
 
799
        # we test this using a shared repository above the named path,
 
800
        # thus checking the right search logic is used - that is, that
 
801
        # its the exact path being looked at and the server is not
 
802
        # searching.
 
803
        backing = self.get_transport()
 
804
        request = smart.repository.SmartServerRepositoryRequest(backing)
 
805
        self.make_repository('.', shared=True)
 
806
        self.make_bzrdir('subdir')
 
807
        self.assertRaises(errors.NoRepositoryPresent,
 
808
            request.execute, 'subdir')
 
809
 
 
810
 
 
811
class TestSmartServerRepositoryGetParentMap(tests.TestCaseWithMemoryTransport):
 
812
 
 
813
    def test_trivial_bzipped(self):
 
814
        # This tests that the wire encoding is actually bzipped
 
815
        backing = self.get_transport()
 
816
        request = smart.repository.SmartServerRepositoryGetParentMap(backing)
 
817
        tree = self.make_branch_and_memory_tree('.')
 
818
 
 
819
        self.assertEqual(None,
 
820
            request.execute('', 'missing-id'))
 
821
        # Note that it returns a body (of '' bzipped).
 
822
        self.assertEqual(
 
823
            SuccessfulSmartServerResponse(('ok', ), bz2.compress('')),
 
824
            request.do_body('\n\n0\n'))
 
825
 
 
826
 
 
827
class TestSmartServerRepositoryGetRevisionGraph(tests.TestCaseWithMemoryTransport):
 
828
 
 
829
    def test_none_argument(self):
 
830
        backing = self.get_transport()
 
831
        request = smart.repository.SmartServerRepositoryGetRevisionGraph(backing)
 
832
        tree = self.make_branch_and_memory_tree('.')
 
833
        tree.lock_write()
 
834
        tree.add('')
 
835
        r1 = tree.commit('1st commit')
 
836
        r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
 
837
        tree.unlock()
 
838
 
 
839
        # the lines of revision_id->revision_parent_list has no guaranteed
 
840
        # order coming out of a dict, so sort both our test and response
 
841
        lines = sorted([' '.join([r2, r1]), r1])
 
842
        response = request.execute('', '')
 
843
        response.body = '\n'.join(sorted(response.body.split('\n')))
 
844
 
 
845
        self.assertEqual(
 
846
            SmartServerResponse(('ok', ), '\n'.join(lines)), response)
 
847
 
 
848
    def test_specific_revision_argument(self):
 
849
        backing = self.get_transport()
 
850
        request = smart.repository.SmartServerRepositoryGetRevisionGraph(backing)
 
851
        tree = self.make_branch_and_memory_tree('.')
 
852
        tree.lock_write()
 
853
        tree.add('')
 
854
        rev_id_utf8 = u'\xc9'.encode('utf-8')
 
855
        r1 = tree.commit('1st commit', rev_id=rev_id_utf8)
 
856
        r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
 
857
        tree.unlock()
 
858
 
 
859
        self.assertEqual(SmartServerResponse(('ok', ), rev_id_utf8),
 
860
            request.execute('', rev_id_utf8))
 
861
 
 
862
    def test_no_such_revision(self):
 
863
        backing = self.get_transport()
 
864
        request = smart.repository.SmartServerRepositoryGetRevisionGraph(backing)
 
865
        tree = self.make_branch_and_memory_tree('.')
 
866
        tree.lock_write()
 
867
        tree.add('')
 
868
        r1 = tree.commit('1st commit')
 
869
        tree.unlock()
 
870
 
 
871
        # Note that it still returns body (of zero bytes).
 
872
        self.assertEqual(
 
873
            SmartServerResponse(('nosuchrevision', 'missingrevision', ), ''),
 
874
            request.execute('', 'missingrevision'))
 
875
 
 
876
 
 
877
class TestSmartServerRequestHasRevision(tests.TestCaseWithMemoryTransport):
 
878
 
 
879
    def test_missing_revision(self):
 
880
        """For a missing revision, ('no', ) is returned."""
 
881
        backing = self.get_transport()
 
882
        request = smart.repository.SmartServerRequestHasRevision(backing)
 
883
        self.make_repository('.')
 
884
        self.assertEqual(SmartServerResponse(('no', )),
 
885
            request.execute('', 'revid'))
 
886
 
 
887
    def test_present_revision(self):
 
888
        """For a present revision, ('yes', ) is returned."""
 
889
        backing = self.get_transport()
 
890
        request = smart.repository.SmartServerRequestHasRevision(backing)
 
891
        tree = self.make_branch_and_memory_tree('.')
 
892
        tree.lock_write()
 
893
        tree.add('')
 
894
        rev_id_utf8 = u'\xc8abc'.encode('utf-8')
 
895
        r1 = tree.commit('a commit', rev_id=rev_id_utf8)
 
896
        tree.unlock()
 
897
        self.assertTrue(tree.branch.repository.has_revision(rev_id_utf8))
 
898
        self.assertEqual(SmartServerResponse(('yes', )),
 
899
            request.execute('', rev_id_utf8))
 
900
 
 
901
 
 
902
class TestSmartServerRepositoryGatherStats(tests.TestCaseWithMemoryTransport):
 
903
 
 
904
    def test_empty_revid(self):
 
905
        """With an empty revid, we get only size an number and revisions"""
 
906
        backing = self.get_transport()
 
907
        request = smart.repository.SmartServerRepositoryGatherStats(backing)
 
908
        repository = self.make_repository('.')
 
909
        stats = repository.gather_stats()
 
910
        expected_body = 'revisions: 0\n'
 
911
        self.assertEqual(SmartServerResponse(('ok', ), expected_body),
 
912
                         request.execute('', '', 'no'))
 
913
 
 
914
    def test_revid_with_committers(self):
 
915
        """For a revid we get more infos."""
 
916
        backing = self.get_transport()
 
917
        rev_id_utf8 = u'\xc8abc'.encode('utf-8')
 
918
        request = smart.repository.SmartServerRepositoryGatherStats(backing)
 
919
        tree = self.make_branch_and_memory_tree('.')
 
920
        tree.lock_write()
 
921
        tree.add('')
 
922
        # Let's build a predictable result
 
923
        tree.commit('a commit', timestamp=123456.2, timezone=3600)
 
924
        tree.commit('a commit', timestamp=654321.4, timezone=0,
 
925
                    rev_id=rev_id_utf8)
 
926
        tree.unlock()
 
927
 
 
928
        stats = tree.branch.repository.gather_stats()
 
929
        expected_body = ('firstrev: 123456.200 3600\n'
 
930
                         'latestrev: 654321.400 0\n'
 
931
                         'revisions: 2\n')
 
932
        self.assertEqual(SmartServerResponse(('ok', ), expected_body),
 
933
                         request.execute('',
 
934
                                         rev_id_utf8, 'no'))
 
935
 
 
936
    def test_not_empty_repository_with_committers(self):
 
937
        """For a revid and requesting committers we get the whole thing."""
 
938
        backing = self.get_transport()
 
939
        rev_id_utf8 = u'\xc8abc'.encode('utf-8')
 
940
        request = smart.repository.SmartServerRepositoryGatherStats(backing)
 
941
        tree = self.make_branch_and_memory_tree('.')
 
942
        tree.lock_write()
 
943
        tree.add('')
 
944
        # Let's build a predictable result
 
945
        tree.commit('a commit', timestamp=123456.2, timezone=3600,
 
946
                    committer='foo')
 
947
        tree.commit('a commit', timestamp=654321.4, timezone=0,
 
948
                    committer='bar', rev_id=rev_id_utf8)
 
949
        tree.unlock()
 
950
        stats = tree.branch.repository.gather_stats()
 
951
 
 
952
        expected_body = ('committers: 2\n'
 
953
                         'firstrev: 123456.200 3600\n'
 
954
                         'latestrev: 654321.400 0\n'
 
955
                         'revisions: 2\n')
 
956
        self.assertEqual(SmartServerResponse(('ok', ), expected_body),
 
957
                         request.execute('',
 
958
                                         rev_id_utf8, 'yes'))
 
959
 
 
960
 
 
961
class TestSmartServerRepositoryIsShared(tests.TestCaseWithMemoryTransport):
 
962
 
 
963
    def test_is_shared(self):
 
964
        """For a shared repository, ('yes', ) is returned."""
 
965
        backing = self.get_transport()
 
966
        request = smart.repository.SmartServerRepositoryIsShared(backing)
 
967
        self.make_repository('.', shared=True)
 
968
        self.assertEqual(SmartServerResponse(('yes', )),
 
969
            request.execute('', ))
 
970
 
 
971
    def test_is_not_shared(self):
 
972
        """For a shared repository, ('no', ) is returned."""
 
973
        backing = self.get_transport()
 
974
        request = smart.repository.SmartServerRepositoryIsShared(backing)
 
975
        self.make_repository('.', shared=False)
 
976
        self.assertEqual(SmartServerResponse(('no', )),
 
977
            request.execute('', ))
 
978
 
 
979
 
 
980
class TestSmartServerRepositoryLockWrite(tests.TestCaseWithMemoryTransport):
 
981
 
 
982
    def setUp(self):
 
983
        tests.TestCaseWithMemoryTransport.setUp(self)
 
984
 
 
985
    def test_lock_write_on_unlocked_repo(self):
 
986
        backing = self.get_transport()
 
987
        request = smart.repository.SmartServerRepositoryLockWrite(backing)
 
988
        repository = self.make_repository('.', format='knit')
 
989
        response = request.execute('')
 
990
        nonce = repository.control_files._lock.peek().get('nonce')
 
991
        self.assertEqual(SmartServerResponse(('ok', nonce)), response)
 
992
        # The repository is now locked.  Verify that with a new repository
 
993
        # object.
 
994
        new_repo = repository.bzrdir.open_repository()
 
995
        self.assertRaises(errors.LockContention, new_repo.lock_write)
 
996
 
 
997
    def test_lock_write_on_locked_repo(self):
 
998
        backing = self.get_transport()
 
999
        request = smart.repository.SmartServerRepositoryLockWrite(backing)
 
1000
        repository = self.make_repository('.', format='knit')
 
1001
        repository.lock_write()
 
1002
        repository.leave_lock_in_place()
 
1003
        repository.unlock()
 
1004
        response = request.execute('')
 
1005
        self.assertEqual(
 
1006
            SmartServerResponse(('LockContention',)), response)
 
1007
 
 
1008
    def test_lock_write_on_readonly_transport(self):
 
1009
        backing = self.get_readonly_transport()
 
1010
        request = smart.repository.SmartServerRepositoryLockWrite(backing)
 
1011
        repository = self.make_repository('.', format='knit')
 
1012
        response = request.execute('')
 
1013
        self.assertFalse(response.is_successful())
 
1014
        self.assertEqual('LockFailed', response.args[0])
 
1015
 
 
1016
 
 
1017
class TestSmartServerRepositoryUnlock(tests.TestCaseWithMemoryTransport):
 
1018
 
 
1019
    def setUp(self):
 
1020
        tests.TestCaseWithMemoryTransport.setUp(self)
 
1021
 
 
1022
    def test_unlock_on_locked_repo(self):
 
1023
        backing = self.get_transport()
 
1024
        request = smart.repository.SmartServerRepositoryUnlock(backing)
 
1025
        repository = self.make_repository('.', format='knit')
 
1026
        token = repository.lock_write()
 
1027
        repository.leave_lock_in_place()
 
1028
        repository.unlock()
 
1029
        response = request.execute('', token)
 
1030
        self.assertEqual(
 
1031
            SmartServerResponse(('ok',)), response)
 
1032
        # The repository is now unlocked.  Verify that with a new repository
 
1033
        # object.
 
1034
        new_repo = repository.bzrdir.open_repository()
 
1035
        new_repo.lock_write()
 
1036
        new_repo.unlock()
 
1037
 
 
1038
    def test_unlock_on_unlocked_repo(self):
 
1039
        backing = self.get_transport()
 
1040
        request = smart.repository.SmartServerRepositoryUnlock(backing)
 
1041
        repository = self.make_repository('.', format='knit')
 
1042
        response = request.execute('', 'some token')
 
1043
        self.assertEqual(
 
1044
            SmartServerResponse(('TokenMismatch',)), response)
 
1045
 
 
1046
 
 
1047
class TestSmartServerIsReadonly(tests.TestCaseWithMemoryTransport):
 
1048
 
 
1049
    def test_is_readonly_no(self):
 
1050
        backing = self.get_transport()
 
1051
        request = smart.request.SmartServerIsReadonly(backing)
 
1052
        response = request.execute()
 
1053
        self.assertEqual(
 
1054
            SmartServerResponse(('no',)), response)
 
1055
 
 
1056
    def test_is_readonly_yes(self):
 
1057
        backing = self.get_readonly_transport()
 
1058
        request = smart.request.SmartServerIsReadonly(backing)
 
1059
        response = request.execute()
 
1060
        self.assertEqual(
 
1061
            SmartServerResponse(('yes',)), response)
 
1062
 
 
1063
 
 
1064
class TestSmartServerRepositorySetMakeWorkingTrees(tests.TestCaseWithMemoryTransport):
 
1065
 
 
1066
    def test_set_false(self):
 
1067
        backing = self.get_transport()
 
1068
        repo = self.make_repository('.', shared=True)
 
1069
        repo.set_make_working_trees(True)
 
1070
        request_class = smart.repository.SmartServerRepositorySetMakeWorkingTrees
 
1071
        request = request_class(backing)
 
1072
        self.assertEqual(SuccessfulSmartServerResponse(('ok',)),
 
1073
            request.execute('', 'False'))
 
1074
        repo = repo.bzrdir.open_repository()
 
1075
        self.assertFalse(repo.make_working_trees())
 
1076
 
 
1077
    def test_set_true(self):
 
1078
        backing = self.get_transport()
 
1079
        repo = self.make_repository('.', shared=True)
 
1080
        repo.set_make_working_trees(False)
 
1081
        request_class = smart.repository.SmartServerRepositorySetMakeWorkingTrees
 
1082
        request = request_class(backing)
 
1083
        self.assertEqual(SuccessfulSmartServerResponse(('ok',)),
 
1084
            request.execute('', 'True'))
 
1085
        repo = repo.bzrdir.open_repository()
 
1086
        self.assertTrue(repo.make_working_trees())
 
1087
 
 
1088
 
 
1089
class TestSmartServerPackRepositoryAutopack(tests.TestCaseWithTransport):
 
1090
 
 
1091
    def make_repo_needing_autopacking(self, path='.'):
 
1092
        # Make a repo in need of autopacking.
 
1093
        tree = self.make_branch_and_tree('.', format='pack-0.92')
 
1094
        repo = tree.branch.repository
 
1095
        # monkey-patch the pack collection to disable autopacking
 
1096
        repo._pack_collection._max_pack_count = lambda count: count
 
1097
        for x in range(10):
 
1098
            tree.commit('commit %s' % x)
 
1099
        self.assertEqual(10, len(repo._pack_collection.names()))
 
1100
        del repo._pack_collection._max_pack_count
 
1101
        return repo
 
1102
 
 
1103
    def test_autopack_needed(self):
 
1104
        repo = self.make_repo_needing_autopacking()
 
1105
        backing = self.get_transport()
 
1106
        request = smart.packrepository.SmartServerPackRepositoryAutopack(
 
1107
            backing)
 
1108
        response = request.execute('')
 
1109
        self.assertEqual(SmartServerResponse(('ok',)), response)
 
1110
        repo._pack_collection.reload_pack_names()
 
1111
        self.assertEqual(1, len(repo._pack_collection.names()))
 
1112
 
 
1113
    def test_autopack_not_needed(self):
 
1114
        tree = self.make_branch_and_tree('.', format='pack-0.92')
 
1115
        repo = tree.branch.repository
 
1116
        for x in range(9):
 
1117
            tree.commit('commit %s' % x)
 
1118
        backing = self.get_transport()
 
1119
        request = smart.packrepository.SmartServerPackRepositoryAutopack(
 
1120
            backing)
 
1121
        response = request.execute('')
 
1122
        self.assertEqual(SmartServerResponse(('ok',)), response)
 
1123
        repo._pack_collection.reload_pack_names()
 
1124
        self.assertEqual(9, len(repo._pack_collection.names()))
 
1125
 
 
1126
    def test_autopack_on_nonpack_format(self):
 
1127
        """A request to autopack a non-pack repo is a no-op."""
 
1128
        repo = self.make_repository('.', format='knit')
 
1129
        backing = self.get_transport()
 
1130
        request = smart.packrepository.SmartServerPackRepositoryAutopack(
 
1131
            backing)
 
1132
        response = request.execute('')
 
1133
        self.assertEqual(SmartServerResponse(('ok',)), response)
 
1134
 
 
1135
 
 
1136
class TestHandlers(tests.TestCase):
 
1137
    """Tests for the request.request_handlers object."""
 
1138
 
 
1139
    def test_all_registrations_exist(self):
 
1140
        """All registered request_handlers can be found."""
 
1141
        # If there's a typo in a register_lazy call, this loop will fail with
 
1142
        # an AttributeError.
 
1143
        for key, item in smart.request.request_handlers.iteritems():
 
1144
            pass
 
1145
 
 
1146
    def test_registered_methods(self):
 
1147
        """Test that known methods are registered to the correct object."""
 
1148
        self.assertEqual(
 
1149
            smart.request.request_handlers.get('Branch.get_config_file'),
 
1150
            smart.branch.SmartServerBranchGetConfigFile)
 
1151
        self.assertEqual(
 
1152
            smart.request.request_handlers.get('Branch.lock_write'),
 
1153
            smart.branch.SmartServerBranchRequestLockWrite)
 
1154
        self.assertEqual(
 
1155
            smart.request.request_handlers.get('Branch.last_revision_info'),
 
1156
            smart.branch.SmartServerBranchRequestLastRevisionInfo)
 
1157
        self.assertEqual(
 
1158
            smart.request.request_handlers.get('Branch.revision_history'),
 
1159
            smart.branch.SmartServerRequestRevisionHistory)
 
1160
        self.assertEqual(
 
1161
            smart.request.request_handlers.get('Branch.set_last_revision'),
 
1162
            smart.branch.SmartServerBranchRequestSetLastRevision)
 
1163
        self.assertEqual(
 
1164
            smart.request.request_handlers.get('Branch.set_last_revision_info'),
 
1165
            smart.branch.SmartServerBranchRequestSetLastRevisionInfo)
 
1166
        self.assertEqual(
 
1167
            smart.request.request_handlers.get('Branch.unlock'),
 
1168
            smart.branch.SmartServerBranchRequestUnlock)
 
1169
        self.assertEqual(
 
1170
            smart.request.request_handlers.get('BzrDir.find_repository'),
 
1171
            smart.bzrdir.SmartServerRequestFindRepositoryV1)
 
1172
        self.assertEqual(
 
1173
            smart.request.request_handlers.get('BzrDir.find_repositoryV2'),
 
1174
            smart.bzrdir.SmartServerRequestFindRepositoryV2)
 
1175
        self.assertEqual(
 
1176
            smart.request.request_handlers.get('BzrDirFormat.initialize'),
 
1177
            smart.bzrdir.SmartServerRequestInitializeBzrDir)
 
1178
        self.assertEqual(
 
1179
            smart.request.request_handlers.get('BzrDir.open_branch'),
 
1180
            smart.bzrdir.SmartServerRequestOpenBranch)
 
1181
        self.assertEqual(
 
1182
            smart.request.request_handlers.get('PackRepository.autopack'),
 
1183
            smart.packrepository.SmartServerPackRepositoryAutopack)
 
1184
        self.assertEqual(
 
1185
            smart.request.request_handlers.get('Repository.gather_stats'),
 
1186
            smart.repository.SmartServerRepositoryGatherStats)
 
1187
        self.assertEqual(
 
1188
            smart.request.request_handlers.get('Repository.get_parent_map'),
 
1189
            smart.repository.SmartServerRepositoryGetParentMap)
 
1190
        self.assertEqual(
 
1191
            smart.request.request_handlers.get(
 
1192
                'Repository.get_revision_graph'),
 
1193
            smart.repository.SmartServerRepositoryGetRevisionGraph)
 
1194
        self.assertEqual(
 
1195
            smart.request.request_handlers.get('Repository.has_revision'),
 
1196
            smart.repository.SmartServerRequestHasRevision)
 
1197
        self.assertEqual(
 
1198
            smart.request.request_handlers.get('Repository.is_shared'),
 
1199
            smart.repository.SmartServerRepositoryIsShared)
 
1200
        self.assertEqual(
 
1201
            smart.request.request_handlers.get('Repository.lock_write'),
 
1202
            smart.repository.SmartServerRepositoryLockWrite)
 
1203
        self.assertEqual(
 
1204
            smart.request.request_handlers.get('Repository.get_stream'),
 
1205
            smart.repository.SmartServerRepositoryGetStream)
 
1206
        self.assertEqual(
 
1207
            smart.request.request_handlers.get('Repository.tarball'),
 
1208
            smart.repository.SmartServerRepositoryTarball)
 
1209
        self.assertEqual(
 
1210
            smart.request.request_handlers.get('Repository.unlock'),
 
1211
            smart.repository.SmartServerRepositoryUnlock)
 
1212
        self.assertEqual(
 
1213
            smart.request.request_handlers.get('Transport.is_readonly'),
 
1214
            smart.request.SmartServerIsReadonly)