~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_smart.py

  • Committer: Ian Clatworthy
  • Date: 2007-08-13 14:16:53 UTC
  • mto: (2733.1.1 ianc-integration)
  • mto: This revision was merged to the branch mainline in revision 2734.
  • Revision ID: ian.clatworthy@internode.on.net-20070813141653-3cbrp00xowq58zv1
Added mini tutorial

Show diffs side-by-side

added added

removed removed

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