~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_smart.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-08-17 07:52:09 UTC
  • mfrom: (1910.3.4 trivial)
  • Revision ID: pqm@pqm.ubuntu.com-20060817075209-e85a1f9e05ff8b87
(andrew) Trivial fixes to NotImplemented errors.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007 Canonical Ltd
2
 
#
3
 
# This program is free software; you can redistribute it and/or modify
4
 
# it under the terms of the GNU General Public License as published by
5
 
# the Free Software Foundation; either version 2 of the License, or
6
 
# (at your option) any later version.
7
 
#
8
 
# This program is distributed in the hope that it will be useful,
9
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 
# GNU General Public License for more details.
12
 
#
13
 
# You should have received a copy of the GNU General Public License
14
 
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
 
 
17
 
"""Tests for the smart wire/domain protocol."""
18
 
 
19
 
from StringIO import StringIO
20
 
import tempfile
21
 
import tarfile
22
 
 
23
 
from bzrlib import bzrdir, errors, pack, 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
 
from bzrlib.util import bencode
29
 
 
30
 
 
31
 
class TestCaseWithSmartMedium(tests.TestCaseWithTransport):
32
 
 
33
 
    def setUp(self):
34
 
        super(TestCaseWithSmartMedium, self).setUp()
35
 
        # We're allowed to set  the transport class here, so that we don't use
36
 
        # the default or a parameterized class, but rather use the
37
 
        # TestCaseWithTransport infrastructure to set up a smart server and
38
 
        # transport.
39
 
        self.transport_server = smart.server.SmartTCPServer_for_testing
40
 
 
41
 
    def get_smart_medium(self):
42
 
        """Get a smart medium to use in tests."""
43
 
        return self.get_transport().get_smart_medium()
44
 
 
45
 
 
46
 
class TestSmartServerResponse(tests.TestCase):
47
 
 
48
 
    def test__eq__(self):
49
 
        self.assertEqual(SmartServerResponse(('ok', )),
50
 
            SmartServerResponse(('ok', )))
51
 
        self.assertEqual(SmartServerResponse(('ok', ), 'body'),
52
 
            SmartServerResponse(('ok', ), 'body'))
53
 
        self.assertNotEqual(SmartServerResponse(('ok', )),
54
 
            SmartServerResponse(('notok', )))
55
 
        self.assertNotEqual(SmartServerResponse(('ok', ), 'body'),
56
 
            SmartServerResponse(('ok', )))
57
 
        self.assertNotEqual(None,
58
 
            SmartServerResponse(('ok', )))
59
 
 
60
 
 
61
 
class TestSmartServerRequestFindRepository(tests.TestCaseWithTransport):
62
 
    """Tests for BzrDir.find_repository."""
63
 
 
64
 
    def test_no_repository(self):
65
 
        """When there is no repository to be found, ('norepository', ) is returned."""
66
 
        backing = self.get_transport()
67
 
        request = smart.bzrdir.SmartServerRequestFindRepository(backing)
68
 
        self.make_bzrdir('.')
69
 
        self.assertEqual(SmartServerResponse(('norepository', )),
70
 
            request.execute(backing.local_abspath('')))
71
 
 
72
 
    def test_nonshared_repository(self):
73
 
        # nonshared repositorys only allow 'find' to return a handle when the 
74
 
        # path the repository is being searched on is the same as that that 
75
 
        # the repository is at.
76
 
        backing = self.get_transport()
77
 
        request = smart.bzrdir.SmartServerRequestFindRepository(backing)
78
 
        result = self._make_repository_and_result()
79
 
        self.assertEqual(result, request.execute(backing.local_abspath('')))
80
 
        self.make_bzrdir('subdir')
81
 
        self.assertEqual(SmartServerResponse(('norepository', )),
82
 
            request.execute(backing.local_abspath('subdir')))
83
 
 
84
 
    def _make_repository_and_result(self, shared=False, format=None):
85
 
        """Convenience function to setup a repository.
86
 
 
87
 
        :result: The SmartServerResponse to expect when opening it.
88
 
        """
89
 
        repo = self.make_repository('.', shared=shared, format=format)
90
 
        if repo.supports_rich_root():
91
 
            rich_root = 'yes'
92
 
        else:
93
 
            rich_root = 'no'
94
 
        if repo._format.supports_tree_reference:
95
 
            subtrees = 'yes'
96
 
        else:
97
 
            subtrees = 'no'
98
 
        return SmartServerResponse(('ok', '', rich_root, subtrees))
99
 
 
100
 
    def test_shared_repository(self):
101
 
        """When there is a shared repository, we get 'ok', 'relpath-to-repo'."""
102
 
        backing = self.get_transport()
103
 
        request = smart.bzrdir.SmartServerRequestFindRepository(backing)
104
 
        result = self._make_repository_and_result(shared=True)
105
 
        self.assertEqual(result, request.execute(backing.local_abspath('')))
106
 
        self.make_bzrdir('subdir')
107
 
        result2 = SmartServerResponse(result.args[0:1] + ('..', ) + result.args[2:])
108
 
        self.assertEqual(result2,
109
 
            request.execute(backing.local_abspath('subdir')))
110
 
        self.make_bzrdir('subdir/deeper')
111
 
        result3 = SmartServerResponse(result.args[0:1] + ('../..', ) + result.args[2:])
112
 
        self.assertEqual(result3,
113
 
            request.execute(backing.local_abspath('subdir/deeper')))
114
 
 
115
 
    def test_rich_root_and_subtree_encoding(self):
116
 
        """Test for the format attributes for rich root and subtree support."""
117
 
        backing = self.get_transport()
118
 
        request = smart.bzrdir.SmartServerRequestFindRepository(backing)
119
 
        result = self._make_repository_and_result(format='dirstate-with-subtree')
120
 
        # check the test will be valid
121
 
        self.assertEqual('yes', result.args[2])
122
 
        self.assertEqual('yes', result.args[3])
123
 
        self.assertEqual(result, request.execute(backing.local_abspath('')))
124
 
 
125
 
 
126
 
class TestSmartServerRequestInitializeBzrDir(tests.TestCaseWithTransport):
127
 
 
128
 
    def test_empty_dir(self):
129
 
        """Initializing an empty dir should succeed and do it."""
130
 
        backing = self.get_transport()
131
 
        request = smart.bzrdir.SmartServerRequestInitializeBzrDir(backing)
132
 
        self.assertEqual(SmartServerResponse(('ok', )),
133
 
            request.execute(backing.local_abspath('.')))
134
 
        made_dir = bzrdir.BzrDir.open_from_transport(backing)
135
 
        # no branch, tree or repository is expected with the current 
136
 
        # default formart.
137
 
        self.assertRaises(errors.NoWorkingTree, made_dir.open_workingtree)
138
 
        self.assertRaises(errors.NotBranchError, made_dir.open_branch)
139
 
        self.assertRaises(errors.NoRepositoryPresent, made_dir.open_repository)
140
 
 
141
 
    def test_missing_dir(self):
142
 
        """Initializing a missing directory should fail like the bzrdir api."""
143
 
        backing = self.get_transport()
144
 
        request = smart.bzrdir.SmartServerRequestInitializeBzrDir(backing)
145
 
        self.assertRaises(errors.NoSuchFile,
146
 
            request.execute, backing.local_abspath('subdir'))
147
 
 
148
 
    def test_initialized_dir(self):
149
 
        """Initializing an extant bzrdir should fail like the bzrdir api."""
150
 
        backing = self.get_transport()
151
 
        request = smart.bzrdir.SmartServerRequestInitializeBzrDir(backing)
152
 
        self.make_bzrdir('subdir')
153
 
        self.assertRaises(errors.FileExists,
154
 
            request.execute, backing.local_abspath('subdir'))
155
 
 
156
 
 
157
 
class TestSmartServerRequestOpenBranch(tests.TestCaseWithTransport):
158
 
 
159
 
    def test_no_branch(self):
160
 
        """When there is no branch, ('nobranch', ) is returned."""
161
 
        backing = self.get_transport()
162
 
        request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
163
 
        self.make_bzrdir('.')
164
 
        self.assertEqual(SmartServerResponse(('nobranch', )),
165
 
            request.execute(backing.local_abspath('')))
166
 
 
167
 
    def test_branch(self):
168
 
        """When there is a branch, 'ok' is returned."""
169
 
        backing = self.get_transport()
170
 
        request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
171
 
        self.make_branch('.')
172
 
        self.assertEqual(SmartServerResponse(('ok', '')),
173
 
            request.execute(backing.local_abspath('')))
174
 
 
175
 
    def test_branch_reference(self):
176
 
        """When there is a branch reference, the reference URL is returned."""
177
 
        backing = self.get_transport()
178
 
        request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
179
 
        branch = self.make_branch('branch')
180
 
        checkout = branch.create_checkout('reference',lightweight=True)
181
 
        # TODO: once we have an API to probe for references of any sort, we
182
 
        # can use it here.
183
 
        reference_url = backing.abspath('branch') + '/'
184
 
        self.assertFileEqual(reference_url, 'reference/.bzr/branch/location')
185
 
        self.assertEqual(SmartServerResponse(('ok', reference_url)),
186
 
            request.execute(backing.local_abspath('reference')))
187
 
 
188
 
 
189
 
class TestSmartServerRequestRevisionHistory(tests.TestCaseWithTransport):
190
 
 
191
 
    def test_empty(self):
192
 
        """For an empty branch, the body is empty."""
193
 
        backing = self.get_transport()
194
 
        request = smart.branch.SmartServerRequestRevisionHistory(backing)
195
 
        self.make_branch('.')
196
 
        self.assertEqual(SmartServerResponse(('ok', ), ''),
197
 
            request.execute(backing.local_abspath('')))
198
 
 
199
 
    def test_not_empty(self):
200
 
        """For a non-empty branch, the body is empty."""
201
 
        backing = self.get_transport()
202
 
        request = smart.branch.SmartServerRequestRevisionHistory(backing)
203
 
        tree = self.make_branch_and_memory_tree('.')
204
 
        tree.lock_write()
205
 
        tree.add('')
206
 
        r1 = tree.commit('1st commit')
207
 
        r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
208
 
        tree.unlock()
209
 
        self.assertEqual(
210
 
            SmartServerResponse(('ok', ), ('\x00'.join([r1, r2]))),
211
 
            request.execute(backing.local_abspath('')))
212
 
 
213
 
 
214
 
class TestSmartServerBranchRequest(tests.TestCaseWithTransport):
215
 
 
216
 
    def test_no_branch(self):
217
 
        """When there is a bzrdir and no branch, NotBranchError is raised."""
218
 
        backing = self.get_transport()
219
 
        request = smart.branch.SmartServerBranchRequest(backing)
220
 
        self.make_bzrdir('.')
221
 
        self.assertRaises(errors.NotBranchError,
222
 
            request.execute, backing.local_abspath(''))
223
 
 
224
 
    def test_branch_reference(self):
225
 
        """When there is a branch reference, NotBranchError is raised."""
226
 
        backing = self.get_transport()
227
 
        request = smart.branch.SmartServerBranchRequest(backing)
228
 
        branch = self.make_branch('branch')
229
 
        checkout = branch.create_checkout('reference',lightweight=True)
230
 
        self.assertRaises(errors.NotBranchError,
231
 
            request.execute, backing.local_abspath('checkout'))
232
 
 
233
 
 
234
 
class TestSmartServerBranchRequestLastRevisionInfo(tests.TestCaseWithTransport):
235
 
 
236
 
    def test_empty(self):
237
 
        """For an empty branch, the result is ('ok', '0', 'null:')."""
238
 
        backing = self.get_transport()
239
 
        request = smart.branch.SmartServerBranchRequestLastRevisionInfo(backing)
240
 
        self.make_branch('.')
241
 
        self.assertEqual(SmartServerResponse(('ok', '0', 'null:')),
242
 
            request.execute(backing.local_abspath('')))
243
 
 
244
 
    def test_not_empty(self):
245
 
        """For a non-empty branch, the result is ('ok', 'revno', 'revid')."""
246
 
        backing = self.get_transport()
247
 
        request = smart.branch.SmartServerBranchRequestLastRevisionInfo(backing)
248
 
        tree = self.make_branch_and_memory_tree('.')
249
 
        tree.lock_write()
250
 
        tree.add('')
251
 
        rev_id_utf8 = u'\xc8'.encode('utf-8')
252
 
        r1 = tree.commit('1st commit')
253
 
        r2 = tree.commit('2nd commit', rev_id=rev_id_utf8)
254
 
        tree.unlock()
255
 
        self.assertEqual(
256
 
            SmartServerResponse(('ok', '2', rev_id_utf8)),
257
 
            request.execute(backing.local_abspath('')))
258
 
 
259
 
 
260
 
class TestSmartServerBranchRequestGetConfigFile(tests.TestCaseWithTransport):
261
 
 
262
 
    def test_default(self):
263
 
        """With no file, we get empty content."""
264
 
        backing = self.get_transport()
265
 
        request = smart.branch.SmartServerBranchGetConfigFile(backing)
266
 
        branch = self.make_branch('.')
267
 
        # there should be no file by default
268
 
        content = ''
269
 
        self.assertEqual(SmartServerResponse(('ok', ), content),
270
 
            request.execute(backing.local_abspath('')))
271
 
 
272
 
    def test_with_content(self):
273
 
        # SmartServerBranchGetConfigFile should return the content from
274
 
        # branch.control_files.get('branch.conf') for now - in the future it may
275
 
        # perform more complex processing. 
276
 
        backing = self.get_transport()
277
 
        request = smart.branch.SmartServerBranchGetConfigFile(backing)
278
 
        branch = self.make_branch('.')
279
 
        branch.control_files.put_utf8('branch.conf', 'foo bar baz')
280
 
        self.assertEqual(SmartServerResponse(('ok', ), 'foo bar baz'),
281
 
            request.execute(backing.local_abspath('')))
282
 
 
283
 
 
284
 
class TestSmartServerBranchRequestSetLastRevision(tests.TestCaseWithTransport):
285
 
 
286
 
    def test_empty(self):
287
 
        backing = self.get_transport()
288
 
        request = smart.branch.SmartServerBranchRequestSetLastRevision(backing)
289
 
        b = self.make_branch('.')
290
 
        branch_token = b.lock_write()
291
 
        repo_token = b.repository.lock_write()
292
 
        b.repository.unlock()
293
 
        try:
294
 
            self.assertEqual(SmartServerResponse(('ok',)),
295
 
                request.execute(
296
 
                    backing.local_abspath(''), branch_token, repo_token,
297
 
                    'null:'))
298
 
        finally:
299
 
            b.unlock()
300
 
 
301
 
    def test_not_present_revision_id(self):
302
 
        backing = self.get_transport()
303
 
        request = smart.branch.SmartServerBranchRequestSetLastRevision(backing)
304
 
        b = self.make_branch('.')
305
 
        branch_token = b.lock_write()
306
 
        repo_token = b.repository.lock_write()
307
 
        b.repository.unlock()
308
 
        try:
309
 
            revision_id = 'non-existent revision'
310
 
            self.assertEqual(
311
 
                SmartServerResponse(('NoSuchRevision', revision_id)),
312
 
                request.execute(
313
 
                    backing.local_abspath(''), branch_token, repo_token,
314
 
                    revision_id))
315
 
        finally:
316
 
            b.unlock()
317
 
 
318
 
    def test_revision_id_present(self):
319
 
        backing = self.get_transport()
320
 
        request = smart.branch.SmartServerBranchRequestSetLastRevision(backing)
321
 
        tree = self.make_branch_and_memory_tree('.')
322
 
        tree.lock_write()
323
 
        tree.add('')
324
 
        rev_id_utf8 = u'\xc8'.encode('utf-8')
325
 
        r1 = tree.commit('1st commit', rev_id=rev_id_utf8)
326
 
        r2 = tree.commit('2nd commit')
327
 
        tree.unlock()
328
 
        branch_token = tree.branch.lock_write()
329
 
        repo_token = tree.branch.repository.lock_write()
330
 
        tree.branch.repository.unlock()
331
 
        try:
332
 
            self.assertEqual(
333
 
                SmartServerResponse(('ok',)),
334
 
                request.execute(
335
 
                    backing.local_abspath(''), branch_token, repo_token,
336
 
                    rev_id_utf8))
337
 
            self.assertEqual([rev_id_utf8], tree.branch.revision_history())
338
 
        finally:
339
 
            tree.branch.unlock()
340
 
 
341
 
    def test_revision_id_present2(self):
342
 
        backing = self.get_transport()
343
 
        request = smart.branch.SmartServerBranchRequestSetLastRevision(backing)
344
 
        tree = self.make_branch_and_memory_tree('.')
345
 
        tree.lock_write()
346
 
        tree.add('')
347
 
        rev_id_utf8 = u'\xc8'.encode('utf-8')
348
 
        r1 = tree.commit('1st commit', rev_id=rev_id_utf8)
349
 
        r2 = tree.commit('2nd commit')
350
 
        tree.unlock()
351
 
        tree.branch.set_revision_history([])
352
 
        branch_token = tree.branch.lock_write()
353
 
        repo_token = tree.branch.repository.lock_write()
354
 
        tree.branch.repository.unlock()
355
 
        try:
356
 
            self.assertEqual(
357
 
                SmartServerResponse(('ok',)),
358
 
                request.execute(
359
 
                    backing.local_abspath(''), branch_token, repo_token,
360
 
                    rev_id_utf8))
361
 
            self.assertEqual([rev_id_utf8], tree.branch.revision_history())
362
 
        finally:
363
 
            tree.branch.unlock()
364
 
 
365
 
 
366
 
class TestSmartServerBranchRequestLockWrite(tests.TestCaseWithTransport):
367
 
 
368
 
    def setUp(self):
369
 
        tests.TestCaseWithTransport.setUp(self)
370
 
        self.reduceLockdirTimeout()
371
 
 
372
 
    def test_lock_write_on_unlocked_branch(self):
373
 
        backing = self.get_transport()
374
 
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
375
 
        branch = self.make_branch('.')
376
 
        repository = branch.repository
377
 
        response = request.execute(backing.local_abspath(''))
378
 
        branch_nonce = branch.control_files._lock.peek().get('nonce')
379
 
        repository_nonce = repository.control_files._lock.peek().get('nonce')
380
 
        self.assertEqual(
381
 
            SmartServerResponse(('ok', branch_nonce, repository_nonce)),
382
 
            response)
383
 
        # The branch (and associated repository) is now locked.  Verify that
384
 
        # with a new branch object.
385
 
        new_branch = repository.bzrdir.open_branch()
386
 
        self.assertRaises(errors.LockContention, new_branch.lock_write)
387
 
 
388
 
    def test_lock_write_on_locked_branch(self):
389
 
        backing = self.get_transport()
390
 
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
391
 
        branch = self.make_branch('.')
392
 
        branch.lock_write()
393
 
        branch.leave_lock_in_place()
394
 
        branch.unlock()
395
 
        response = request.execute(backing.local_abspath(''))
396
 
        self.assertEqual(
397
 
            SmartServerResponse(('LockContention',)), response)
398
 
 
399
 
    def test_lock_write_with_tokens_on_locked_branch(self):
400
 
        backing = self.get_transport()
401
 
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
402
 
        branch = self.make_branch('.')
403
 
        branch_token = branch.lock_write()
404
 
        repo_token = branch.repository.lock_write()
405
 
        branch.repository.unlock()
406
 
        branch.leave_lock_in_place()
407
 
        branch.repository.leave_lock_in_place()
408
 
        branch.unlock()
409
 
        response = request.execute(backing.local_abspath(''),
410
 
                                   branch_token, repo_token)
411
 
        self.assertEqual(
412
 
            SmartServerResponse(('ok', branch_token, repo_token)), response)
413
 
 
414
 
    def test_lock_write_with_mismatched_tokens_on_locked_branch(self):
415
 
        backing = self.get_transport()
416
 
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
417
 
        branch = self.make_branch('.')
418
 
        branch_token = branch.lock_write()
419
 
        repo_token = branch.repository.lock_write()
420
 
        branch.repository.unlock()
421
 
        branch.leave_lock_in_place()
422
 
        branch.repository.leave_lock_in_place()
423
 
        branch.unlock()
424
 
        response = request.execute(backing.local_abspath(''),
425
 
                                   branch_token+'xxx', repo_token)
426
 
        self.assertEqual(
427
 
            SmartServerResponse(('TokenMismatch',)), response)
428
 
 
429
 
    def test_lock_write_on_locked_repo(self):
430
 
        backing = self.get_transport()
431
 
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
432
 
        branch = self.make_branch('.')
433
 
        branch.repository.lock_write()
434
 
        branch.repository.leave_lock_in_place()
435
 
        branch.repository.unlock()
436
 
        response = request.execute(backing.local_abspath(''))
437
 
        self.assertEqual(
438
 
            SmartServerResponse(('LockContention',)), response)
439
 
 
440
 
    def test_lock_write_on_readonly_transport(self):
441
 
        backing = self.get_readonly_transport()
442
 
        request = smart.branch.SmartServerBranchRequestLockWrite(backing)
443
 
        branch = self.make_branch('.')
444
 
        response = request.execute('')
445
 
        error_name, lock_str, why_str = response.args
446
 
        self.assertFalse(response.is_successful())
447
 
        self.assertEqual('LockFailed', error_name)
448
 
 
449
 
 
450
 
class TestSmartServerBranchRequestUnlock(tests.TestCaseWithTransport):
451
 
 
452
 
    def setUp(self):
453
 
        tests.TestCaseWithTransport.setUp(self)
454
 
        self.reduceLockdirTimeout()
455
 
 
456
 
    def test_unlock_on_locked_branch_and_repo(self):
457
 
        backing = self.get_transport()
458
 
        request = smart.branch.SmartServerBranchRequestUnlock(backing)
459
 
        branch = self.make_branch('.')
460
 
        # Lock the branch
461
 
        branch_token = branch.lock_write()
462
 
        repo_token = branch.repository.lock_write()
463
 
        branch.repository.unlock()
464
 
        # Unlock the branch (and repo) object, leaving the physical locks
465
 
        # in place.
466
 
        branch.leave_lock_in_place()
467
 
        branch.repository.leave_lock_in_place()
468
 
        branch.unlock()
469
 
        response = request.execute(backing.local_abspath(''),
470
 
                                   branch_token, repo_token)
471
 
        self.assertEqual(
472
 
            SmartServerResponse(('ok',)), response)
473
 
        # The branch is now unlocked.  Verify that with a new branch
474
 
        # object.
475
 
        new_branch = branch.bzrdir.open_branch()
476
 
        new_branch.lock_write()
477
 
        new_branch.unlock()
478
 
 
479
 
    def test_unlock_on_unlocked_branch_unlocked_repo(self):
480
 
        backing = self.get_transport()
481
 
        request = smart.branch.SmartServerBranchRequestUnlock(backing)
482
 
        branch = self.make_branch('.')
483
 
        response = request.execute(
484
 
            backing.local_abspath(''), 'branch token', 'repo token')
485
 
        self.assertEqual(
486
 
            SmartServerResponse(('TokenMismatch',)), response)
487
 
 
488
 
    def test_unlock_on_unlocked_branch_locked_repo(self):
489
 
        backing = self.get_transport()
490
 
        request = smart.branch.SmartServerBranchRequestUnlock(backing)
491
 
        branch = self.make_branch('.')
492
 
        # Lock the repository.
493
 
        repo_token = branch.repository.lock_write()
494
 
        branch.repository.leave_lock_in_place()
495
 
        branch.repository.unlock()
496
 
        # Issue branch lock_write request on the unlocked branch (with locked
497
 
        # repo).
498
 
        response = request.execute(
499
 
            backing.local_abspath(''), 'branch token', repo_token)
500
 
        self.assertEqual(
501
 
            SmartServerResponse(('TokenMismatch',)), response)
502
 
 
503
 
 
504
 
class TestSmartServerRepositoryRequest(tests.TestCaseWithTransport):
505
 
 
506
 
    def test_no_repository(self):
507
 
        """Raise NoRepositoryPresent when there is a bzrdir and no repo."""
508
 
        # we test this using a shared repository above the named path,
509
 
        # thus checking the right search logic is used - that is, that
510
 
        # its the exact path being looked at and the server is not
511
 
        # searching.
512
 
        backing = self.get_transport()
513
 
        request = smart.repository.SmartServerRepositoryRequest(backing)
514
 
        self.make_repository('.', shared=True)
515
 
        self.make_bzrdir('subdir')
516
 
        self.assertRaises(errors.NoRepositoryPresent,
517
 
            request.execute, backing.local_abspath('subdir'))
518
 
 
519
 
 
520
 
class TestSmartServerRepositoryGetRevisionGraph(tests.TestCaseWithTransport):
521
 
 
522
 
    def test_none_argument(self):
523
 
        backing = self.get_transport()
524
 
        request = smart.repository.SmartServerRepositoryGetRevisionGraph(backing)
525
 
        tree = self.make_branch_and_memory_tree('.')
526
 
        tree.lock_write()
527
 
        tree.add('')
528
 
        r1 = tree.commit('1st commit')
529
 
        r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
530
 
        tree.unlock()
531
 
 
532
 
        # the lines of revision_id->revision_parent_list has no guaranteed
533
 
        # order coming out of a dict, so sort both our test and response
534
 
        lines = sorted([' '.join([r2, r1]), r1])
535
 
        response = request.execute(backing.local_abspath(''), '')
536
 
        response.body = '\n'.join(sorted(response.body.split('\n')))
537
 
 
538
 
        self.assertEqual(
539
 
            SmartServerResponse(('ok', ), '\n'.join(lines)), response)
540
 
 
541
 
    def test_specific_revision_argument(self):
542
 
        backing = self.get_transport()
543
 
        request = smart.repository.SmartServerRepositoryGetRevisionGraph(backing)
544
 
        tree = self.make_branch_and_memory_tree('.')
545
 
        tree.lock_write()
546
 
        tree.add('')
547
 
        rev_id_utf8 = u'\xc9'.encode('utf-8')
548
 
        r1 = tree.commit('1st commit', rev_id=rev_id_utf8)
549
 
        r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
550
 
        tree.unlock()
551
 
 
552
 
        self.assertEqual(SmartServerResponse(('ok', ), rev_id_utf8),
553
 
            request.execute(backing.local_abspath(''), rev_id_utf8))
554
 
    
555
 
    def test_no_such_revision(self):
556
 
        backing = self.get_transport()
557
 
        request = smart.repository.SmartServerRepositoryGetRevisionGraph(backing)
558
 
        tree = self.make_branch_and_memory_tree('.')
559
 
        tree.lock_write()
560
 
        tree.add('')
561
 
        r1 = tree.commit('1st commit')
562
 
        tree.unlock()
563
 
 
564
 
        # Note that it still returns body (of zero bytes).
565
 
        self.assertEqual(
566
 
            SmartServerResponse(('nosuchrevision', 'missingrevision', ), ''),
567
 
            request.execute(backing.local_abspath(''), 'missingrevision'))
568
 
 
569
 
 
570
 
class TestSmartServerRequestHasRevision(tests.TestCaseWithTransport):
571
 
 
572
 
    def test_missing_revision(self):
573
 
        """For a missing revision, ('no', ) is returned."""
574
 
        backing = self.get_transport()
575
 
        request = smart.repository.SmartServerRequestHasRevision(backing)
576
 
        self.make_repository('.')
577
 
        self.assertEqual(SmartServerResponse(('no', )),
578
 
            request.execute(backing.local_abspath(''), 'revid'))
579
 
 
580
 
    def test_present_revision(self):
581
 
        """For a present revision, ('yes', ) is returned."""
582
 
        backing = self.get_transport()
583
 
        request = smart.repository.SmartServerRequestHasRevision(backing)
584
 
        tree = self.make_branch_and_memory_tree('.')
585
 
        tree.lock_write()
586
 
        tree.add('')
587
 
        rev_id_utf8 = u'\xc8abc'.encode('utf-8')
588
 
        r1 = tree.commit('a commit', rev_id=rev_id_utf8)
589
 
        tree.unlock()
590
 
        self.assertTrue(tree.branch.repository.has_revision(rev_id_utf8))
591
 
        self.assertEqual(SmartServerResponse(('yes', )),
592
 
            request.execute(backing.local_abspath(''), rev_id_utf8))
593
 
 
594
 
 
595
 
class TestSmartServerRepositoryGatherStats(tests.TestCaseWithTransport):
596
 
 
597
 
    def test_empty_revid(self):
598
 
        """With an empty revid, we get only size an number and revisions"""
599
 
        backing = self.get_transport()
600
 
        request = smart.repository.SmartServerRepositoryGatherStats(backing)
601
 
        repository = self.make_repository('.')
602
 
        stats = repository.gather_stats()
603
 
        size = stats['size']
604
 
        expected_body = 'revisions: 0\nsize: %d\n' % size
605
 
        self.assertEqual(SmartServerResponse(('ok', ), expected_body),
606
 
                         request.execute(backing.local_abspath(''), '', 'no'))
607
 
 
608
 
    def test_revid_with_committers(self):
609
 
        """For a revid we get more infos."""
610
 
        backing = self.get_transport()
611
 
        rev_id_utf8 = u'\xc8abc'.encode('utf-8')
612
 
        request = smart.repository.SmartServerRepositoryGatherStats(backing)
613
 
        tree = self.make_branch_and_memory_tree('.')
614
 
        tree.lock_write()
615
 
        tree.add('')
616
 
        # Let's build a predictable result
617
 
        tree.commit('a commit', timestamp=123456.2, timezone=3600)
618
 
        tree.commit('a commit', timestamp=654321.4, timezone=0,
619
 
                    rev_id=rev_id_utf8)
620
 
        tree.unlock()
621
 
 
622
 
        stats = tree.branch.repository.gather_stats()
623
 
        size = stats['size']
624
 
        expected_body = ('firstrev: 123456.200 3600\n'
625
 
                         'latestrev: 654321.400 0\n'
626
 
                         'revisions: 2\n'
627
 
                         'size: %d\n' % size)
628
 
        self.assertEqual(SmartServerResponse(('ok', ), expected_body),
629
 
                         request.execute(backing.local_abspath(''),
630
 
                                         rev_id_utf8, 'no'))
631
 
 
632
 
    def test_not_empty_repository_with_committers(self):
633
 
        """For a revid and requesting committers we get the whole thing."""
634
 
        backing = self.get_transport()
635
 
        rev_id_utf8 = u'\xc8abc'.encode('utf-8')
636
 
        request = smart.repository.SmartServerRepositoryGatherStats(backing)
637
 
        tree = self.make_branch_and_memory_tree('.')
638
 
        tree.lock_write()
639
 
        tree.add('')
640
 
        # Let's build a predictable result
641
 
        tree.commit('a commit', timestamp=123456.2, timezone=3600,
642
 
                    committer='foo')
643
 
        tree.commit('a commit', timestamp=654321.4, timezone=0,
644
 
                    committer='bar', rev_id=rev_id_utf8)
645
 
        tree.unlock()
646
 
        stats = tree.branch.repository.gather_stats()
647
 
 
648
 
        size = stats['size']
649
 
        expected_body = ('committers: 2\n'
650
 
                         'firstrev: 123456.200 3600\n'
651
 
                         'latestrev: 654321.400 0\n'
652
 
                         'revisions: 2\n'
653
 
                         'size: %d\n' % size)
654
 
        self.assertEqual(SmartServerResponse(('ok', ), expected_body),
655
 
                         request.execute(backing.local_abspath(''),
656
 
                                         rev_id_utf8, 'yes'))
657
 
 
658
 
 
659
 
class TestSmartServerRepositoryIsShared(tests.TestCaseWithTransport):
660
 
 
661
 
    def test_is_shared(self):
662
 
        """For a shared repository, ('yes', ) is returned."""
663
 
        backing = self.get_transport()
664
 
        request = smart.repository.SmartServerRepositoryIsShared(backing)
665
 
        self.make_repository('.', shared=True)
666
 
        self.assertEqual(SmartServerResponse(('yes', )),
667
 
            request.execute(backing.local_abspath(''), ))
668
 
 
669
 
    def test_is_not_shared(self):
670
 
        """For a shared repository, ('no', ) is returned."""
671
 
        backing = self.get_transport()
672
 
        request = smart.repository.SmartServerRepositoryIsShared(backing)
673
 
        self.make_repository('.', shared=False)
674
 
        self.assertEqual(SmartServerResponse(('no', )),
675
 
            request.execute(backing.local_abspath(''), ))
676
 
 
677
 
 
678
 
class TestSmartServerRepositoryLockWrite(tests.TestCaseWithTransport):
679
 
 
680
 
    def setUp(self):
681
 
        tests.TestCaseWithTransport.setUp(self)
682
 
        self.reduceLockdirTimeout()
683
 
 
684
 
    def test_lock_write_on_unlocked_repo(self):
685
 
        backing = self.get_transport()
686
 
        request = smart.repository.SmartServerRepositoryLockWrite(backing)
687
 
        repository = self.make_repository('.')
688
 
        response = request.execute(backing.local_abspath(''))
689
 
        nonce = repository.control_files._lock.peek().get('nonce')
690
 
        self.assertEqual(SmartServerResponse(('ok', nonce)), response)
691
 
        # The repository is now locked.  Verify that with a new repository
692
 
        # object.
693
 
        new_repo = repository.bzrdir.open_repository()
694
 
        self.assertRaises(errors.LockContention, new_repo.lock_write)
695
 
 
696
 
    def test_lock_write_on_locked_repo(self):
697
 
        backing = self.get_transport()
698
 
        request = smart.repository.SmartServerRepositoryLockWrite(backing)
699
 
        repository = self.make_repository('.')
700
 
        repository.lock_write()
701
 
        repository.leave_lock_in_place()
702
 
        repository.unlock()
703
 
        response = request.execute(backing.local_abspath(''))
704
 
        self.assertEqual(
705
 
            SmartServerResponse(('LockContention',)), response)
706
 
 
707
 
    def test_lock_write_on_readonly_transport(self):
708
 
        backing = self.get_readonly_transport()
709
 
        request = smart.repository.SmartServerRepositoryLockWrite(backing)
710
 
        repository = self.make_repository('.')
711
 
        response = request.execute('')
712
 
        self.assertFalse(response.is_successful())
713
 
        self.assertEqual('LockFailed', response.args[0])
714
 
 
715
 
 
716
 
class TestSmartServerRepositoryUnlock(tests.TestCaseWithTransport):
717
 
 
718
 
    def setUp(self):
719
 
        tests.TestCaseWithTransport.setUp(self)
720
 
        self.reduceLockdirTimeout()
721
 
 
722
 
    def test_unlock_on_locked_repo(self):
723
 
        backing = self.get_transport()
724
 
        request = smart.repository.SmartServerRepositoryUnlock(backing)
725
 
        repository = self.make_repository('.')
726
 
        token = repository.lock_write()
727
 
        repository.leave_lock_in_place()
728
 
        repository.unlock()
729
 
        response = request.execute(backing.local_abspath(''), token)
730
 
        self.assertEqual(
731
 
            SmartServerResponse(('ok',)), response)
732
 
        # The repository is now unlocked.  Verify that with a new repository
733
 
        # object.
734
 
        new_repo = repository.bzrdir.open_repository()
735
 
        new_repo.lock_write()
736
 
        new_repo.unlock()
737
 
 
738
 
    def test_unlock_on_unlocked_repo(self):
739
 
        backing = self.get_transport()
740
 
        request = smart.repository.SmartServerRepositoryUnlock(backing)
741
 
        repository = self.make_repository('.')
742
 
        response = request.execute(backing.local_abspath(''), 'some token')
743
 
        self.assertEqual(
744
 
            SmartServerResponse(('TokenMismatch',)), response)
745
 
 
746
 
 
747
 
class TestSmartServerRepositoryTarball(tests.TestCaseWithTransport):
748
 
 
749
 
    def test_repository_tarball(self):
750
 
        backing = self.get_transport()
751
 
        request = smart.repository.SmartServerRepositoryTarball(backing)
752
 
        repository = self.make_repository('.')
753
 
        # make some extraneous junk in the repository directory which should
754
 
        # not be copied
755
 
        self.build_tree(['.bzr/repository/extra-junk'])
756
 
        response = request.execute(backing.local_abspath(''), 'bz2')
757
 
        self.assertEqual(('ok',), response.args)
758
 
        # body should be a tbz2
759
 
        body_file = StringIO(response.body)
760
 
        body_tar = tarfile.open('body_tar.tbz2', fileobj=body_file,
761
 
            mode='r|bz2')
762
 
        # let's make sure there are some key repository components inside it.
763
 
        # the tarfile returns directories with trailing slashes...
764
 
        names = set([n.rstrip('/') for n in body_tar.getnames()])
765
 
        self.assertTrue('.bzr/repository/lock' in names)
766
 
        self.assertTrue('.bzr/repository/format' in names)
767
 
        self.assertTrue('.bzr/repository/extra-junk' not in names,
768
 
            "extraneous file present in tar file")
769
 
 
770
 
 
771
 
class TestSmartServerRepositoryStreamKnitData(tests.TestCaseWithTransport):
772
 
 
773
 
    def test_fetch_revisions(self):
774
 
        backing = self.get_transport()
775
 
        request = smart.repository.SmartServerRepositoryStreamKnitDataForRevisions(backing)
776
 
        tree = self.make_branch_and_memory_tree('.')
777
 
        tree.lock_write()
778
 
        tree.add('')
779
 
        rev_id1_utf8 = u'\xc8'.encode('utf-8')
780
 
        rev_id2_utf8 = u'\xc9'.encode('utf-8')
781
 
        r1 = tree.commit('1st commit', rev_id=rev_id1_utf8)
782
 
        r1 = tree.commit('2nd commit', rev_id=rev_id2_utf8)
783
 
        tree.unlock()
784
 
 
785
 
        response = request.execute(backing.local_abspath(''), rev_id2_utf8)
786
 
        self.assertEqual(('ok',), response.args)
787
 
        from cStringIO import StringIO
788
 
        unpacker = pack.ContainerReader(StringIO(response.body))
789
 
        names = []
790
 
        for [name], read_bytes in unpacker.iter_records():
791
 
            names.append(name)
792
 
            bytes = read_bytes(None)
793
 
            # The bytes should be a valid bencoded string.
794
 
            bencode.bdecode(bytes)
795
 
            # XXX: assert that the bencoded knit records have the right
796
 
            # contents?
797
 
        
798
 
    def test_no_such_revision_error(self):
799
 
        backing = self.get_transport()
800
 
        request = smart.repository.SmartServerRepositoryStreamKnitDataForRevisions(backing)
801
 
        repo = self.make_repository('.')
802
 
        rev_id1_utf8 = u'\xc8'.encode('utf-8')
803
 
        response = request.execute(backing.local_abspath(''), rev_id1_utf8)
804
 
        self.assertEqual(
805
 
            SmartServerResponse(('NoSuchRevision', rev_id1_utf8)),
806
 
            response)
807
 
 
808
 
 
809
 
class TestSmartServerIsReadonly(tests.TestCaseWithTransport):
810
 
 
811
 
    def test_is_readonly_no(self):
812
 
        backing = self.get_transport()
813
 
        request = smart.request.SmartServerIsReadonly(backing)
814
 
        response = request.execute()
815
 
        self.assertEqual(
816
 
            SmartServerResponse(('no',)), response)
817
 
 
818
 
    def test_is_readonly_yes(self):
819
 
        backing = self.get_readonly_transport()
820
 
        request = smart.request.SmartServerIsReadonly(backing)
821
 
        response = request.execute()
822
 
        self.assertEqual(
823
 
            SmartServerResponse(('yes',)), response)
824
 
 
825
 
 
826
 
class TestHandlers(tests.TestCase):
827
 
    """Tests for the request.request_handlers object."""
828
 
 
829
 
    def test_registered_methods(self):
830
 
        """Test that known methods are registered to the correct object."""
831
 
        self.assertEqual(
832
 
            smart.request.request_handlers.get('Branch.get_config_file'),
833
 
            smart.branch.SmartServerBranchGetConfigFile)
834
 
        self.assertEqual(
835
 
            smart.request.request_handlers.get('Branch.lock_write'),
836
 
            smart.branch.SmartServerBranchRequestLockWrite)
837
 
        self.assertEqual(
838
 
            smart.request.request_handlers.get('Branch.last_revision_info'),
839
 
            smart.branch.SmartServerBranchRequestLastRevisionInfo)
840
 
        self.assertEqual(
841
 
            smart.request.request_handlers.get('Branch.revision_history'),
842
 
            smart.branch.SmartServerRequestRevisionHistory)
843
 
        self.assertEqual(
844
 
            smart.request.request_handlers.get('Branch.set_last_revision'),
845
 
            smart.branch.SmartServerBranchRequestSetLastRevision)
846
 
        self.assertEqual(
847
 
            smart.request.request_handlers.get('Branch.unlock'),
848
 
            smart.branch.SmartServerBranchRequestUnlock)
849
 
        self.assertEqual(
850
 
            smart.request.request_handlers.get('BzrDir.find_repository'),
851
 
            smart.bzrdir.SmartServerRequestFindRepository)
852
 
        self.assertEqual(
853
 
            smart.request.request_handlers.get('BzrDirFormat.initialize'),
854
 
            smart.bzrdir.SmartServerRequestInitializeBzrDir)
855
 
        self.assertEqual(
856
 
            smart.request.request_handlers.get('BzrDir.open_branch'),
857
 
            smart.bzrdir.SmartServerRequestOpenBranch)
858
 
        self.assertEqual(
859
 
            smart.request.request_handlers.get('Repository.gather_stats'),
860
 
            smart.repository.SmartServerRepositoryGatherStats)
861
 
        self.assertEqual(
862
 
            smart.request.request_handlers.get(
863
 
                'Repository.get_revision_graph'),
864
 
            smart.repository.SmartServerRepositoryGetRevisionGraph)
865
 
        self.assertEqual(
866
 
            smart.request.request_handlers.get('Repository.has_revision'),
867
 
            smart.repository.SmartServerRequestHasRevision)
868
 
        self.assertEqual(
869
 
            smart.request.request_handlers.get('Repository.is_shared'),
870
 
            smart.repository.SmartServerRepositoryIsShared)
871
 
        self.assertEqual(
872
 
            smart.request.request_handlers.get('Repository.lock_write'),
873
 
            smart.repository.SmartServerRepositoryLockWrite)
874
 
        self.assertEqual(
875
 
            smart.request.request_handlers.get(
876
 
                'Repository.stream_knit_data_for_revisions'),
877
 
            smart.repository.SmartServerRepositoryStreamKnitDataForRevisions)
878
 
        self.assertEqual(
879
 
            smart.request.request_handlers.get('Repository.tarball'),
880
 
            smart.repository.SmartServerRepositoryTarball)
881
 
        self.assertEqual(
882
 
            smart.request.request_handlers.get('Repository.unlock'),
883
 
            smart.repository.SmartServerRepositoryUnlock)
884
 
        self.assertEqual(
885
 
            smart.request.request_handlers.get('Transport.is_readonly'),
886
 
            smart.request.SmartServerIsReadonly)