~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_smart.py

  • Committer: Robert Collins
  • Date: 2010-04-08 04:34:03 UTC
  • mfrom: (5138 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5139.
  • Revision ID: robertc@robertcollins.net-20100408043403-56z0d07vdqrx7f3t
Update bugfix for 528114 to trunk.

Show diffs side-by-side

added added

removed removed

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