~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_smart.py

(parthm) Better regex compile errors (Parth Malwankar)

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