1
# Copyright (C) 2005, 2006 Canonical Ltd
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.
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.
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
17
"""Tests for the BzrDir facility and any format specific tests.
19
For interface contract tests, see tests/bzr_dir_implementations.
22
from StringIO import StringIO
25
import bzrlib.bzrdir as bzrdir
26
import bzrlib.errors as errors
27
from bzrlib.errors import (NotBranchError,
29
UnsupportedFormatError,
31
import bzrlib.repository as repository
32
from bzrlib.tests import TestCase, TestCaseWithTransport
33
from bzrlib.transport import get_transport
34
from bzrlib.transport.http import HttpServer
35
from bzrlib.transport.memory import MemoryServer
36
import bzrlib.workingtree as workingtree
39
class TestDefaultFormat(TestCase):
41
def test_get_set_default_format(self):
42
old_format = bzrdir.BzrDirFormat.get_default_format()
43
# default is BzrDirFormat6
44
self.failUnless(isinstance(old_format, bzrdir.BzrDirMetaFormat1))
45
bzrdir.BzrDirFormat.set_default_format(SampleBzrDirFormat())
46
# creating a bzr dir should now create an instrumented dir.
48
result = bzrdir.BzrDir.create('memory:///')
49
self.failUnless(isinstance(result, SampleBzrDir))
51
bzrdir.BzrDirFormat.set_default_format(old_format)
52
self.assertEqual(old_format, bzrdir.BzrDirFormat.get_default_format())
55
class SampleBranch(bzrlib.branch.Branch):
56
"""A dummy branch for guess what, dummy use."""
58
def __init__(self, dir):
62
class SampleBzrDir(bzrdir.BzrDir):
63
"""A sample BzrDir implementation to allow testing static methods."""
65
def create_repository(self, shared=False):
66
"""See BzrDir.create_repository."""
69
def open_repository(self):
70
"""See BzrDir.open_repository."""
73
def create_branch(self):
74
"""See BzrDir.create_branch."""
75
return SampleBranch(self)
77
def create_workingtree(self):
78
"""See BzrDir.create_workingtree."""
82
class SampleBzrDirFormat(bzrdir.BzrDirFormat):
85
this format is initializable, unsupported to aid in testing the
86
open and open_downlevel routines.
89
def get_format_string(self):
90
"""See BzrDirFormat.get_format_string()."""
91
return "Sample .bzr dir format."
93
def initialize(self, url):
94
"""Create a bzr dir."""
95
t = get_transport(url)
97
t.put('.bzr/branch-format', StringIO(self.get_format_string()))
98
return SampleBzrDir(t, self)
100
def is_supported(self):
103
def open(self, transport, _found=None):
104
return "opened branch."
107
class TestBzrDirFormat(TestCaseWithTransport):
108
"""Tests for the BzrDirFormat facility."""
110
def test_find_format(self):
111
# is the right format object found for a branch?
112
# create a branch with a few known format objects.
113
# this is not quite the same as
114
t = get_transport(self.get_url())
115
self.build_tree(["foo/", "bar/"], transport=t)
116
def check_format(format, url):
117
format.initialize(url)
118
t = get_transport(url)
119
found_format = bzrdir.BzrDirFormat.find_format(t)
120
self.failUnless(isinstance(found_format, format.__class__))
121
check_format(bzrdir.BzrDirFormat5(), "foo")
122
check_format(bzrdir.BzrDirFormat6(), "bar")
124
def test_find_format_nothing_there(self):
125
self.assertRaises(NotBranchError,
126
bzrdir.BzrDirFormat.find_format,
129
def test_find_format_unknown_format(self):
130
t = get_transport(self.get_url())
132
t.put('.bzr/branch-format', StringIO())
133
self.assertRaises(UnknownFormatError,
134
bzrdir.BzrDirFormat.find_format,
137
def test_register_unregister_format(self):
138
format = SampleBzrDirFormat()
141
format.initialize(url)
142
# register a format for it.
143
bzrdir.BzrDirFormat.register_format(format)
144
# which bzrdir.Open will refuse (not supported)
145
self.assertRaises(UnsupportedFormatError, bzrdir.BzrDir.open, url)
146
# which bzrdir.open_containing will refuse (not supported)
147
self.assertRaises(UnsupportedFormatError, bzrdir.BzrDir.open_containing, url)
148
# but open_downlevel will work
149
t = get_transport(url)
150
self.assertEqual(format.open(t), bzrdir.BzrDir.open_unsupported(url))
151
# unregister the format
152
bzrdir.BzrDirFormat.unregister_format(format)
153
# now open_downlevel should fail too.
154
self.assertRaises(UnknownFormatError, bzrdir.BzrDir.open_unsupported, url)
156
def test_create_repository(self):
157
format = SampleBzrDirFormat()
158
old_format = bzrdir.BzrDirFormat.get_default_format()
159
bzrdir.BzrDirFormat.set_default_format(format)
161
repo = bzrdir.BzrDir.create_repository(self.get_url())
162
self.assertEqual('A repository', repo)
164
bzrdir.BzrDirFormat.set_default_format(old_format)
166
def test_create_repository_shared(self):
167
old_format = bzrdir.BzrDirFormat.get_default_format()
168
repo = bzrdir.BzrDir.create_repository('.', shared=True)
169
self.assertTrue(repo.is_shared())
171
def test_create_repository_nonshared(self):
172
old_format = bzrdir.BzrDirFormat.get_default_format()
173
repo = bzrdir.BzrDir.create_repository('.')
174
self.assertFalse(repo.is_shared())
176
def test_create_repository_under_shared(self):
177
# an explicit create_repository always does so.
178
# we trust the format is right from the 'create_repository test'
179
old_format = bzrdir.BzrDirFormat.get_default_format()
180
bzrdir.BzrDirFormat.set_default_format(bzrdir.BzrDirMetaFormat1())
182
self.make_repository('.', shared=True)
183
repo = bzrdir.BzrDir.create_repository(self.get_url('child'))
184
self.assertTrue(isinstance(repo, repository.Repository))
185
self.assertTrue(repo.bzrdir.root_transport.base.endswith('child/'))
187
bzrdir.BzrDirFormat.set_default_format(old_format)
189
def test_create_branch_and_repo_uses_default(self):
190
format = SampleBzrDirFormat()
191
old_format = bzrdir.BzrDirFormat.get_default_format()
192
bzrdir.BzrDirFormat.set_default_format(format)
194
branch = bzrdir.BzrDir.create_branch_and_repo(self.get_url())
195
self.assertTrue(isinstance(branch, SampleBranch))
197
bzrdir.BzrDirFormat.set_default_format(old_format)
199
def test_create_branch_and_repo_under_shared(self):
200
# creating a branch and repo in a shared repo uses the
202
old_format = bzrdir.BzrDirFormat.get_default_format()
203
bzrdir.BzrDirFormat.set_default_format(bzrdir.BzrDirMetaFormat1())
205
self.make_repository('.', shared=True)
206
branch = bzrdir.BzrDir.create_branch_and_repo(self.get_url('child'))
207
self.assertRaises(errors.NoRepositoryPresent,
208
branch.bzrdir.open_repository)
210
bzrdir.BzrDirFormat.set_default_format(old_format)
212
def test_create_branch_and_repo_under_shared_force_new(self):
213
# creating a branch and repo in a shared repo can be forced to
215
old_format = bzrdir.BzrDirFormat.get_default_format()
216
bzrdir.BzrDirFormat.set_default_format(bzrdir.BzrDirMetaFormat1())
218
self.make_repository('.', shared=True)
219
branch = bzrdir.BzrDir.create_branch_and_repo(self.get_url('child'),
221
branch.bzrdir.open_repository()
223
bzrdir.BzrDirFormat.set_default_format(old_format)
225
def test_create_standalone_working_tree(self):
226
format = SampleBzrDirFormat()
227
old_format = bzrdir.BzrDirFormat.get_default_format()
228
bzrdir.BzrDirFormat.set_default_format(format)
230
# note this is deliberately readonly, as this failure should
231
# occur before any writes.
232
self.assertRaises(errors.NotLocalUrl,
233
bzrdir.BzrDir.create_standalone_workingtree,
234
self.get_readonly_url())
235
tree = bzrdir.BzrDir.create_standalone_workingtree('.')
236
self.assertEqual('A tree', tree)
238
bzrdir.BzrDirFormat.set_default_format(old_format)
240
def test_create_standalone_working_tree_under_shared_repo(self):
241
# create standalone working tree always makes a repo.
242
old_format = bzrdir.BzrDirFormat.get_default_format()
243
bzrdir.BzrDirFormat.set_default_format(bzrdir.BzrDirMetaFormat1())
245
self.make_repository('.', shared=True)
246
# note this is deliberately readonly, as this failure should
247
# occur before any writes.
248
self.assertRaises(errors.NotLocalUrl,
249
bzrdir.BzrDir.create_standalone_workingtree,
250
self.get_readonly_url('child'))
251
tree = bzrdir.BzrDir.create_standalone_workingtree('child')
252
tree.bzrdir.open_repository()
254
bzrdir.BzrDirFormat.set_default_format(old_format)
256
def test_create_branch_convenience(self):
257
# outside a repo the default convenience output is a repo+branch_tree
258
old_format = bzrdir.BzrDirFormat.get_default_format()
259
bzrdir.BzrDirFormat.set_default_format(bzrdir.BzrDirMetaFormat1())
261
branch = bzrdir.BzrDir.create_branch_convenience('.')
262
branch.bzrdir.open_workingtree()
263
branch.bzrdir.open_repository()
265
bzrdir.BzrDirFormat.set_default_format(old_format)
267
def test_create_branch_convenience_root(self):
268
"""Creating a branch at the root of a fs should work."""
269
self.transport_server = MemoryServer
270
# outside a repo the default convenience output is a repo+branch_tree
271
old_format = bzrdir.BzrDirFormat.get_default_format()
272
bzrdir.BzrDirFormat.set_default_format(bzrdir.BzrDirMetaFormat1())
274
branch = bzrdir.BzrDir.create_branch_convenience(self.get_url())
275
self.assertRaises(errors.NoWorkingTree,
276
branch.bzrdir.open_workingtree)
277
branch.bzrdir.open_repository()
279
bzrdir.BzrDirFormat.set_default_format(old_format)
281
def test_create_branch_convenience_under_shared_repo(self):
282
# inside a repo the default convenience output is a branch+ follow the
284
old_format = bzrdir.BzrDirFormat.get_default_format()
285
bzrdir.BzrDirFormat.set_default_format(bzrdir.BzrDirMetaFormat1())
287
self.make_repository('.', shared=True)
288
branch = bzrdir.BzrDir.create_branch_convenience('child')
289
branch.bzrdir.open_workingtree()
290
self.assertRaises(errors.NoRepositoryPresent,
291
branch.bzrdir.open_repository)
293
bzrdir.BzrDirFormat.set_default_format(old_format)
295
def test_create_branch_convenience_under_shared_repo_force_no_tree(self):
296
# inside a repo the default convenience output is a branch+ follow the
297
# repo tree policy but we can override that
298
old_format = bzrdir.BzrDirFormat.get_default_format()
299
bzrdir.BzrDirFormat.set_default_format(bzrdir.BzrDirMetaFormat1())
301
self.make_repository('.', shared=True)
302
branch = bzrdir.BzrDir.create_branch_convenience('child',
303
force_new_tree=False)
304
self.assertRaises(errors.NoWorkingTree,
305
branch.bzrdir.open_workingtree)
306
self.assertRaises(errors.NoRepositoryPresent,
307
branch.bzrdir.open_repository)
309
bzrdir.BzrDirFormat.set_default_format(old_format)
311
def test_create_branch_convenience_under_shared_repo_no_tree_policy(self):
312
# inside a repo the default convenience output is a branch+ follow the
314
old_format = bzrdir.BzrDirFormat.get_default_format()
315
bzrdir.BzrDirFormat.set_default_format(bzrdir.BzrDirMetaFormat1())
317
repo = self.make_repository('.', shared=True)
318
repo.set_make_working_trees(False)
319
branch = bzrdir.BzrDir.create_branch_convenience('child')
320
self.assertRaises(errors.NoWorkingTree,
321
branch.bzrdir.open_workingtree)
322
self.assertRaises(errors.NoRepositoryPresent,
323
branch.bzrdir.open_repository)
325
bzrdir.BzrDirFormat.set_default_format(old_format)
327
def test_create_branch_convenience_under_shared_repo_no_tree_policy_force_tree(self):
328
# inside a repo the default convenience output is a branch+ follow the
329
# repo tree policy but we can override that
330
old_format = bzrdir.BzrDirFormat.get_default_format()
331
bzrdir.BzrDirFormat.set_default_format(bzrdir.BzrDirMetaFormat1())
333
repo = self.make_repository('.', shared=True)
334
repo.set_make_working_trees(False)
335
branch = bzrdir.BzrDir.create_branch_convenience('child',
337
branch.bzrdir.open_workingtree()
338
self.assertRaises(errors.NoRepositoryPresent,
339
branch.bzrdir.open_repository)
341
bzrdir.BzrDirFormat.set_default_format(old_format)
343
def test_create_branch_convenience_under_shared_repo_force_new_repo(self):
344
# inside a repo the default convenience output is overridable to give
346
old_format = bzrdir.BzrDirFormat.get_default_format()
347
bzrdir.BzrDirFormat.set_default_format(bzrdir.BzrDirMetaFormat1())
349
self.make_repository('.', shared=True)
350
branch = bzrdir.BzrDir.create_branch_convenience('child',
352
branch.bzrdir.open_repository()
353
branch.bzrdir.open_workingtree()
355
bzrdir.BzrDirFormat.set_default_format(old_format)
358
class ChrootedTests(TestCaseWithTransport):
359
"""A support class that provides readonly urls outside the local namespace.
361
This is done by checking if self.transport_server is a MemoryServer. if it
362
is then we are chrooted already, if it is not then an HttpServer is used
367
super(ChrootedTests, self).setUp()
368
if not self.transport_server == MemoryServer:
369
self.transport_readonly_server = HttpServer
371
def test_open_containing(self):
372
self.assertRaises(NotBranchError, bzrdir.BzrDir.open_containing,
373
self.get_readonly_url(''))
374
self.assertRaises(NotBranchError, bzrdir.BzrDir.open_containing,
375
self.get_readonly_url('g/p/q'))
376
control = bzrdir.BzrDir.create(self.get_url())
377
branch, relpath = bzrdir.BzrDir.open_containing(self.get_readonly_url(''))
378
self.assertEqual('', relpath)
379
branch, relpath = bzrdir.BzrDir.open_containing(self.get_readonly_url('g/p/q'))
380
self.assertEqual('g/p/q', relpath)
382
def test_open_containing_from_transport(self):
383
self.assertRaises(NotBranchError, bzrdir.BzrDir.open_containing_from_transport,
384
get_transport(self.get_readonly_url('')))
385
self.assertRaises(NotBranchError, bzrdir.BzrDir.open_containing_from_transport,
386
get_transport(self.get_readonly_url('g/p/q')))
387
control = bzrdir.BzrDir.create(self.get_url())
388
branch, relpath = bzrdir.BzrDir.open_containing_from_transport(
389
get_transport(self.get_readonly_url('')))
390
self.assertEqual('', relpath)
391
branch, relpath = bzrdir.BzrDir.open_containing_from_transport(
392
get_transport(self.get_readonly_url('g/p/q')))
393
self.assertEqual('g/p/q', relpath)
396
class TestMeta1DirFormat(TestCaseWithTransport):
397
"""Tests specific to the meta1 dir format."""
399
def test_right_base_dirs(self):
400
dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
402
branch_base = t.clone('branch').base
403
self.assertEqual(branch_base, dir.get_branch_transport(None).base)
404
self.assertEqual(branch_base,
405
dir.get_branch_transport(bzrlib.branch.BzrBranchFormat5()).base)
406
repository_base = t.clone('repository').base
407
self.assertEqual(repository_base, dir.get_repository_transport(None).base)
408
self.assertEqual(repository_base,
409
dir.get_repository_transport(repository.RepositoryFormat7()).base)
410
checkout_base = t.clone('checkout').base
411
self.assertEqual(checkout_base, dir.get_workingtree_transport(None).base)
412
self.assertEqual(checkout_base,
413
dir.get_workingtree_transport(workingtree.WorkingTreeFormat3()).base)
415
def test_meta1dir_uses_lockdir(self):
416
"""Meta1 format uses a LockDir to guard the whole directory, not a file."""
417
dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
419
self.assertIsDirectory('branch-lock', t)
422
class TestFormat5(TestCaseWithTransport):
423
"""Tests specific to the version 5 bzrdir format."""
425
def test_same_lockfiles_between_tree_repo_branch(self):
426
# this checks that only a single lockfiles instance is created
427
# for format 5 objects
428
dir = bzrdir.BzrDirFormat5().initialize(self.get_url())
429
def check_dir_components_use_same_lock(dir):
430
ctrl_1 = dir.open_repository().control_files
431
ctrl_2 = dir.open_branch().control_files
432
ctrl_3 = dir.open_workingtree()._control_files
433
self.assertTrue(ctrl_1 is ctrl_2)
434
self.assertTrue(ctrl_2 is ctrl_3)
435
check_dir_components_use_same_lock(dir)
436
# and if we open it normally.
437
dir = bzrdir.BzrDir.open(self.get_url())
438
check_dir_components_use_same_lock(dir)
440
def test_can_convert(self):
441
# format 5 dirs are convertable
442
dir = bzrdir.BzrDirFormat5().initialize(self.get_url())
443
self.assertTrue(dir.can_convert_format())
445
def test_needs_conversion(self):
446
# format 5 dirs need a conversion if they are not the default.
447
# and they start of not the default.
448
old_format = bzrdir.BzrDirFormat.get_default_format()
449
bzrdir.BzrDirFormat.set_default_format(bzrdir.BzrDirFormat5())
451
dir = bzrdir.BzrDirFormat5().initialize(self.get_url())
452
self.assertFalse(dir.needs_format_conversion())
454
bzrdir.BzrDirFormat.set_default_format(old_format)
455
self.assertTrue(dir.needs_format_conversion())
458
class TestFormat6(TestCaseWithTransport):
459
"""Tests specific to the version 6 bzrdir format."""
461
def test_same_lockfiles_between_tree_repo_branch(self):
462
# this checks that only a single lockfiles instance is created
463
# for format 6 objects
464
dir = bzrdir.BzrDirFormat6().initialize(self.get_url())
465
def check_dir_components_use_same_lock(dir):
466
ctrl_1 = dir.open_repository().control_files
467
ctrl_2 = dir.open_branch().control_files
468
ctrl_3 = dir.open_workingtree()._control_files
469
self.assertTrue(ctrl_1 is ctrl_2)
470
self.assertTrue(ctrl_2 is ctrl_3)
471
check_dir_components_use_same_lock(dir)
472
# and if we open it normally.
473
dir = bzrdir.BzrDir.open(self.get_url())
474
check_dir_components_use_same_lock(dir)
476
def test_can_convert(self):
477
# format 6 dirs are convertable
478
dir = bzrdir.BzrDirFormat6().initialize(self.get_url())
479
self.assertTrue(dir.can_convert_format())
481
def test_needs_conversion(self):
482
# format 6 dirs need an conversion if they are not the default.
483
old_format = bzrdir.BzrDirFormat.get_default_format()
484
bzrdir.BzrDirFormat.set_default_format(bzrdir.BzrDirMetaFormat1())
486
dir = bzrdir.BzrDirFormat6().initialize(self.get_url())
487
self.assertTrue(dir.needs_format_conversion())
489
bzrdir.BzrDirFormat.set_default_format(old_format)
492
class NotBzrDir(bzrlib.bzrdir.BzrDir):
493
"""A non .bzr based control directory."""
495
def __init__(self, transport, format):
496
self._format = format
497
self.root_transport = transport
498
self.transport = transport.clone('.not')
501
class NotBzrDirFormat(bzrlib.bzrdir.BzrDirFormat):
502
"""A test class representing any non-.bzr based disk format."""
504
def initialize_on_transport(self, transport):
505
"""Initialize a new .not dir in the base directory of a Transport."""
506
transport.mkdir('.not')
507
return self.open(transport)
509
def open(self, transport):
510
"""Open this directory."""
511
return NotBzrDir(transport, self)
514
def _known_formats(self):
515
return set([NotBzrDirFormat()])
518
def probe_transport(self, transport):
519
"""Our format is present if the transport ends in '.not/'."""
520
if transport.has('.not'):
521
return NotBzrDirFormat()
524
class TestNotBzrDir(TestCaseWithTransport):
525
"""Tests for using the bzrdir api with a non .bzr based disk format.
527
If/when one of these is in the core, we can let the implementation tests
531
def test_create_and_find_format(self):
532
# create a .notbzr dir
533
format = NotBzrDirFormat()
534
dir = format.initialize(self.get_url())
535
self.assertIsInstance(dir, NotBzrDir)
537
bzrlib.bzrdir.BzrDirFormat.register_control_format(format)
539
found = bzrlib.bzrdir.BzrDirFormat.find_format(
540
get_transport(self.get_url()))
541
self.assertIsInstance(found, NotBzrDirFormat)
543
bzrlib.bzrdir.BzrDirFormat.unregister_control_format(format)
545
def test_included_in_known_formats(self):
546
bzrlib.bzrdir.BzrDirFormat.register_control_format(NotBzrDirFormat)
548
formats = bzrlib.bzrdir.BzrDirFormat.known_formats()
549
for format in formats:
550
if isinstance(format, NotBzrDirFormat):
552
self.fail("No NotBzrDirFormat in %s" % formats)
554
bzrlib.bzrdir.BzrDirFormat.unregister_control_format(NotBzrDirFormat)
557
class NonLocalTests(TestCaseWithTransport):
558
"""Tests for bzrdir static behaviour on non local paths."""
561
super(NonLocalTests, self).setUp()
562
self.transport_server = MemoryServer
564
def test_create_branch_convenience(self):
565
# outside a repo the default convenience output is a repo+branch_tree
566
old_format = bzrdir.BzrDirFormat.get_default_format()
567
bzrdir.BzrDirFormat.set_default_format(bzrdir.BzrDirMetaFormat1())
569
branch = bzrdir.BzrDir.create_branch_convenience(self.get_url('foo'))
570
self.assertRaises(errors.NoWorkingTree,
571
branch.bzrdir.open_workingtree)
572
branch.bzrdir.open_repository()
574
bzrdir.BzrDirFormat.set_default_format(old_format)
576
def test_create_branch_convenience_force_tree_not_local_fails(self):
577
# outside a repo the default convenience output is a repo+branch_tree
578
old_format = bzrdir.BzrDirFormat.get_default_format()
579
bzrdir.BzrDirFormat.set_default_format(bzrdir.BzrDirMetaFormat1())
581
self.assertRaises(errors.NotLocalUrl,
582
bzrdir.BzrDir.create_branch_convenience,
585
t = get_transport(self.get_url('.'))
586
self.assertFalse(t.has('foo'))
588
bzrdir.BzrDirFormat.set_default_format(old_format)
590
def test_clone(self):
591
# clone into a nonlocal path works
592
old_format = bzrdir.BzrDirFormat.get_default_format()
593
bzrdir.BzrDirFormat.set_default_format(bzrdir.BzrDirMetaFormat1())
595
branch = bzrdir.BzrDir.create_branch_convenience('local')
597
bzrdir.BzrDirFormat.set_default_format(old_format)
598
branch.bzrdir.open_workingtree()
599
result = branch.bzrdir.clone(self.get_url('remote'))
600
self.assertRaises(errors.NoWorkingTree,
601
result.open_workingtree)
603
result.open_repository()