1
# Copyright (C) 2007-2011 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""Tests for the compiled dirstate helpers."""
29
from bzrlib.tests import (
32
from bzrlib.tests.test_osutils import dir_reader_scenarios
33
from bzrlib.tests.scenarios import (
34
load_tests_apply_scenarios,
39
load_tests = load_tests_apply_scenarios
42
compiled_dirstate_helpers_feature = tests.ModuleAvailableFeature(
43
'bzrlib._dirstate_helpers_pyx')
46
# FIXME: we should also parametrize against SHA1Provider !
48
ue_scenarios = [('dirstate_Python',
49
{'update_entry': dirstate.py_update_entry})]
50
if compiled_dirstate_helpers_feature.available():
51
update_entry = compiled_dirstate_helpers_feature.module.update_entry
52
ue_scenarios.append(('dirstate_Pyrex', {'update_entry': update_entry}))
54
pe_scenarios = [('dirstate_Python',
55
{'_process_entry': dirstate.ProcessEntryPython})]
56
if compiled_dirstate_helpers_feature.available():
57
process_entry = compiled_dirstate_helpers_feature.module.ProcessEntryC
58
pe_scenarios.append(('dirstate_Pyrex', {'_process_entry': process_entry}))
61
class TestBisectPathMixin(object):
62
"""Test that _bisect_path_*() returns the expected values.
64
_bisect_path_* is intended to work like bisect.bisect_*() except it
65
knows it is working on paths that are sorted by ('path', 'to', 'foo')
66
chunks rather than by raw 'path/to/foo'.
68
Test Cases should inherit from this and override ``get_bisect_path`` return
69
their implementation, and ``get_bisect`` to return the matching
70
bisect.bisect_* function.
73
def get_bisect_path(self):
74
"""Return an implementation of _bisect_path_*"""
75
raise NotImplementedError
78
"""Return a version of bisect.bisect_*.
80
Also, for the 'exists' check, return the offset to the real values.
81
For example bisect_left returns the index of an entry, while
82
bisect_right returns the index *after* an entry
84
:return: (bisect_func, offset)
86
raise NotImplementedError
88
def assertBisect(self, paths, split_paths, path, exists=True):
89
"""Assert that bisect_split works like bisect_left on the split paths.
91
:param paths: A list of path names
92
:param split_paths: A list of path names that are already split up by directory
93
('path/to/foo' => ('path', 'to', 'foo'))
94
:param path: The path we are indexing.
95
:param exists: The path should be present, so make sure the
96
final location actually points to the right value.
98
All other arguments will be passed along.
100
bisect_path = self.get_bisect_path()
101
self.assertIsInstance(paths, list)
102
bisect_path_idx = bisect_path(paths, path)
103
split_path = self.split_for_dirblocks([path])[0]
104
bisect_func, offset = self.get_bisect()
105
bisect_split_idx = bisect_func(split_paths, split_path)
106
self.assertEqual(bisect_split_idx, bisect_path_idx,
107
'%s disagreed. %s != %s'
109
% (bisect_path.__name__,
110
bisect_split_idx, bisect_path_idx, path)
113
self.assertEqual(path, paths[bisect_path_idx+offset])
115
def split_for_dirblocks(self, paths):
118
dirname, basename = os.path.split(path)
119
dir_split_paths.append((dirname.split('/'), basename))
120
dir_split_paths.sort()
121
return dir_split_paths
123
def test_simple(self):
124
"""In the simple case it works just like bisect_left"""
125
paths = ['', 'a', 'b', 'c', 'd']
126
split_paths = self.split_for_dirblocks(paths)
128
self.assertBisect(paths, split_paths, path, exists=True)
129
self.assertBisect(paths, split_paths, '_', exists=False)
130
self.assertBisect(paths, split_paths, 'aa', exists=False)
131
self.assertBisect(paths, split_paths, 'bb', exists=False)
132
self.assertBisect(paths, split_paths, 'cc', exists=False)
133
self.assertBisect(paths, split_paths, 'dd', exists=False)
134
self.assertBisect(paths, split_paths, 'a/a', exists=False)
135
self.assertBisect(paths, split_paths, 'b/b', exists=False)
136
self.assertBisect(paths, split_paths, 'c/c', exists=False)
137
self.assertBisect(paths, split_paths, 'd/d', exists=False)
139
def test_involved(self):
140
"""This is where bisect_path_* diverges slightly."""
141
# This is the list of paths and their contents
169
# This is the exact order that is stored by dirstate
170
# All children in a directory are mentioned before an children of
171
# children are mentioned.
172
# So all the root-directory paths, then all the
173
# first sub directory, etc.
174
paths = [# content of '/'
175
'', 'a', 'a-a', 'a-z', 'a=a', 'a=z',
177
'a/a', 'a/a-a', 'a/a-z',
179
'a/z', 'a/z-a', 'a/z-z',
202
split_paths = self.split_for_dirblocks(paths)
204
for dir_parts, basename in split_paths:
205
if dir_parts == ['']:
206
sorted_paths.append(basename)
208
sorted_paths.append('/'.join(dir_parts + [basename]))
210
self.assertEqual(sorted_paths, paths)
213
self.assertBisect(paths, split_paths, path, exists=True)
216
class TestBisectPathLeft(tests.TestCase, TestBisectPathMixin):
217
"""Run all Bisect Path tests against _bisect_path_left."""
219
def get_bisect_path(self):
220
from bzrlib._dirstate_helpers_py import _bisect_path_left
221
return _bisect_path_left
223
def get_bisect(self):
224
return bisect.bisect_left, 0
227
class TestCompiledBisectPathLeft(TestBisectPathLeft):
228
"""Run all Bisect Path tests against _bisect_path_lect"""
230
_test_needs_features = [compiled_dirstate_helpers_feature]
232
def get_bisect_path(self):
233
from bzrlib._dirstate_helpers_pyx import _bisect_path_left
234
return _bisect_path_left
237
class TestBisectPathRight(tests.TestCase, TestBisectPathMixin):
238
"""Run all Bisect Path tests against _bisect_path_right"""
240
def get_bisect_path(self):
241
from bzrlib._dirstate_helpers_py import _bisect_path_right
242
return _bisect_path_right
244
def get_bisect(self):
245
return bisect.bisect_right, -1
248
class TestCompiledBisectPathRight(TestBisectPathRight):
249
"""Run all Bisect Path tests against _bisect_path_right"""
251
_test_needs_features = [compiled_dirstate_helpers_feature]
253
def get_bisect_path(self):
254
from bzrlib._dirstate_helpers_pyx import _bisect_path_right
255
return _bisect_path_right
258
class TestBisectDirblock(tests.TestCase):
259
"""Test that bisect_dirblock() returns the expected values.
261
bisect_dirblock is intended to work like bisect.bisect_left() except it
262
knows it is working on dirblocks and that dirblocks are sorted by ('path',
263
'to', 'foo') chunks rather than by raw 'path/to/foo'.
265
This test is parameterized by calling get_bisect_dirblock(). Child test
266
cases can override this function to test against a different
270
def get_bisect_dirblock(self):
271
"""Return an implementation of bisect_dirblock"""
272
from bzrlib._dirstate_helpers_py import bisect_dirblock
273
return bisect_dirblock
275
def assertBisect(self, dirblocks, split_dirblocks, path, *args, **kwargs):
276
"""Assert that bisect_split works like bisect_left on the split paths.
278
:param dirblocks: A list of (path, [info]) pairs.
279
:param split_dirblocks: A list of ((split, path), [info]) pairs.
280
:param path: The path we are indexing.
282
All other arguments will be passed along.
284
bisect_dirblock = self.get_bisect_dirblock()
285
self.assertIsInstance(dirblocks, list)
286
bisect_split_idx = bisect_dirblock(dirblocks, path, *args, **kwargs)
287
split_dirblock = (path.split('/'), [])
288
bisect_left_idx = bisect.bisect_left(split_dirblocks, split_dirblock,
290
self.assertEqual(bisect_left_idx, bisect_split_idx,
291
'bisect_split disagreed. %s != %s'
293
% (bisect_left_idx, bisect_split_idx, path)
296
def paths_to_dirblocks(self, paths):
297
"""Convert a list of paths into dirblock form.
299
Also, ensure that the paths are in proper sorted order.
301
dirblocks = [(path, []) for path in paths]
302
split_dirblocks = [(path.split('/'), []) for path in paths]
303
self.assertEqual(sorted(split_dirblocks), split_dirblocks)
304
return dirblocks, split_dirblocks
306
def test_simple(self):
307
"""In the simple case it works just like bisect_left"""
308
paths = ['', 'a', 'b', 'c', 'd']
309
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
311
self.assertBisect(dirblocks, split_dirblocks, path)
312
self.assertBisect(dirblocks, split_dirblocks, '_')
313
self.assertBisect(dirblocks, split_dirblocks, 'aa')
314
self.assertBisect(dirblocks, split_dirblocks, 'bb')
315
self.assertBisect(dirblocks, split_dirblocks, 'cc')
316
self.assertBisect(dirblocks, split_dirblocks, 'dd')
317
self.assertBisect(dirblocks, split_dirblocks, 'a/a')
318
self.assertBisect(dirblocks, split_dirblocks, 'b/b')
319
self.assertBisect(dirblocks, split_dirblocks, 'c/c')
320
self.assertBisect(dirblocks, split_dirblocks, 'd/d')
322
def test_involved(self):
323
"""This is where bisect_left diverges slightly."""
325
'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
326
'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
328
'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
329
'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
332
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
334
self.assertBisect(dirblocks, split_dirblocks, path)
336
def test_involved_cached(self):
337
"""This is where bisect_left diverges slightly."""
339
'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
340
'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
342
'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
343
'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
347
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
349
self.assertBisect(dirblocks, split_dirblocks, path, cache=cache)
352
class TestCompiledBisectDirblock(TestBisectDirblock):
353
"""Test that bisect_dirblock() returns the expected values.
355
bisect_dirblock is intended to work like bisect.bisect_left() except it
356
knows it is working on dirblocks and that dirblocks are sorted by ('path',
357
'to', 'foo') chunks rather than by raw 'path/to/foo'.
359
This runs all the normal tests that TestBisectDirblock did, but uses the
363
_test_needs_features = [compiled_dirstate_helpers_feature]
365
def get_bisect_dirblock(self):
366
from bzrlib._dirstate_helpers_pyx import bisect_dirblock
367
return bisect_dirblock
370
class TestCmpByDirs(tests.TestCase):
371
"""Test an implementation of cmp_by_dirs()
373
cmp_by_dirs() compares 2 paths by their directory sections, rather than as
376
Child test cases can override ``get_cmp_by_dirs`` to test a specific
380
def get_cmp_by_dirs(self):
381
"""Get a specific implementation of cmp_by_dirs."""
382
from bzrlib._dirstate_helpers_py import cmp_by_dirs
385
def assertCmpByDirs(self, expected, str1, str2):
386
"""Compare the two strings, in both directions.
388
:param expected: The expected comparison value. -1 means str1 comes
389
first, 0 means they are equal, 1 means str2 comes first
390
:param str1: string to compare
391
:param str2: string to compare
393
cmp_by_dirs = self.get_cmp_by_dirs()
395
self.assertEqual(str1, str2)
396
self.assertEqual(0, cmp_by_dirs(str1, str2))
397
self.assertEqual(0, cmp_by_dirs(str2, str1))
399
self.assertPositive(cmp_by_dirs(str1, str2))
400
self.assertNegative(cmp_by_dirs(str2, str1))
402
self.assertNegative(cmp_by_dirs(str1, str2))
403
self.assertPositive(cmp_by_dirs(str2, str1))
405
def test_cmp_empty(self):
406
"""Compare against the empty string."""
407
self.assertCmpByDirs(0, '', '')
408
self.assertCmpByDirs(1, 'a', '')
409
self.assertCmpByDirs(1, 'ab', '')
410
self.assertCmpByDirs(1, 'abc', '')
411
self.assertCmpByDirs(1, 'abcd', '')
412
self.assertCmpByDirs(1, 'abcde', '')
413
self.assertCmpByDirs(1, 'abcdef', '')
414
self.assertCmpByDirs(1, 'abcdefg', '')
415
self.assertCmpByDirs(1, 'abcdefgh', '')
416
self.assertCmpByDirs(1, 'abcdefghi', '')
417
self.assertCmpByDirs(1, 'test/ing/a/path/', '')
419
def test_cmp_same_str(self):
420
"""Compare the same string"""
421
self.assertCmpByDirs(0, 'a', 'a')
422
self.assertCmpByDirs(0, 'ab', 'ab')
423
self.assertCmpByDirs(0, 'abc', 'abc')
424
self.assertCmpByDirs(0, 'abcd', 'abcd')
425
self.assertCmpByDirs(0, 'abcde', 'abcde')
426
self.assertCmpByDirs(0, 'abcdef', 'abcdef')
427
self.assertCmpByDirs(0, 'abcdefg', 'abcdefg')
428
self.assertCmpByDirs(0, 'abcdefgh', 'abcdefgh')
429
self.assertCmpByDirs(0, 'abcdefghi', 'abcdefghi')
430
self.assertCmpByDirs(0, 'testing a long string', 'testing a long string')
431
self.assertCmpByDirs(0, 'x'*10000, 'x'*10000)
432
self.assertCmpByDirs(0, 'a/b', 'a/b')
433
self.assertCmpByDirs(0, 'a/b/c', 'a/b/c')
434
self.assertCmpByDirs(0, 'a/b/c/d', 'a/b/c/d')
435
self.assertCmpByDirs(0, 'a/b/c/d/e', 'a/b/c/d/e')
437
def test_simple_paths(self):
438
"""Compare strings that act like normal string comparison"""
439
self.assertCmpByDirs(-1, 'a', 'b')
440
self.assertCmpByDirs(-1, 'aa', 'ab')
441
self.assertCmpByDirs(-1, 'ab', 'bb')
442
self.assertCmpByDirs(-1, 'aaa', 'aab')
443
self.assertCmpByDirs(-1, 'aab', 'abb')
444
self.assertCmpByDirs(-1, 'abb', 'bbb')
445
self.assertCmpByDirs(-1, 'aaaa', 'aaab')
446
self.assertCmpByDirs(-1, 'aaab', 'aabb')
447
self.assertCmpByDirs(-1, 'aabb', 'abbb')
448
self.assertCmpByDirs(-1, 'abbb', 'bbbb')
449
self.assertCmpByDirs(-1, 'aaaaa', 'aaaab')
450
self.assertCmpByDirs(-1, 'a/a', 'a/b')
451
self.assertCmpByDirs(-1, 'a/b', 'b/b')
452
self.assertCmpByDirs(-1, 'a/a/a', 'a/a/b')
453
self.assertCmpByDirs(-1, 'a/a/b', 'a/b/b')
454
self.assertCmpByDirs(-1, 'a/b/b', 'b/b/b')
455
self.assertCmpByDirs(-1, 'a/a/a/a', 'a/a/a/b')
456
self.assertCmpByDirs(-1, 'a/a/a/b', 'a/a/b/b')
457
self.assertCmpByDirs(-1, 'a/a/b/b', 'a/b/b/b')
458
self.assertCmpByDirs(-1, 'a/b/b/b', 'b/b/b/b')
459
self.assertCmpByDirs(-1, 'a/a/a/a/a', 'a/a/a/a/b')
461
def test_tricky_paths(self):
462
self.assertCmpByDirs(1, 'ab/cd/ef', 'ab/cc/ef')
463
self.assertCmpByDirs(1, 'ab/cd/ef', 'ab/c/ef')
464
self.assertCmpByDirs(-1, 'ab/cd/ef', 'ab/cd-ef')
465
self.assertCmpByDirs(-1, 'ab/cd', 'ab/cd-')
466
self.assertCmpByDirs(-1, 'ab/cd', 'ab-cd')
468
def test_cmp_unicode_not_allowed(self):
469
cmp_by_dirs = self.get_cmp_by_dirs()
470
self.assertRaises(TypeError, cmp_by_dirs, u'Unicode', 'str')
471
self.assertRaises(TypeError, cmp_by_dirs, 'str', u'Unicode')
472
self.assertRaises(TypeError, cmp_by_dirs, u'Unicode', u'Unicode')
474
def test_cmp_non_ascii(self):
475
self.assertCmpByDirs(-1, '\xc2\xb5', '\xc3\xa5') # u'\xb5', u'\xe5'
476
self.assertCmpByDirs(-1, 'a', '\xc3\xa5') # u'a', u'\xe5'
477
self.assertCmpByDirs(-1, 'b', '\xc2\xb5') # u'b', u'\xb5'
478
self.assertCmpByDirs(-1, 'a/b', 'a/\xc3\xa5') # u'a/b', u'a/\xe5'
479
self.assertCmpByDirs(-1, 'b/a', 'b/\xc2\xb5') # u'b/a', u'b/\xb5'
482
class TestCompiledCmpByDirs(TestCmpByDirs):
483
"""Test the pyrex implementation of cmp_by_dirs"""
485
_test_needs_features = [compiled_dirstate_helpers_feature]
487
def get_cmp_by_dirs(self):
488
from bzrlib._dirstate_helpers_pyx import cmp_by_dirs
492
class TestCmpPathByDirblock(tests.TestCase):
493
"""Test an implementation of _cmp_path_by_dirblock()
495
_cmp_path_by_dirblock() compares two paths using the sort order used by
496
DirState. All paths in the same directory are sorted together.
498
Child test cases can override ``get_cmp_path_by_dirblock`` to test a specific
502
def get_cmp_path_by_dirblock(self):
503
"""Get a specific implementation of _cmp_path_by_dirblock."""
504
from bzrlib._dirstate_helpers_py import _cmp_path_by_dirblock
505
return _cmp_path_by_dirblock
507
def assertCmpPathByDirblock(self, paths):
508
"""Compare all paths and make sure they evaluate to the correct order.
510
This does N^2 comparisons. It is assumed that ``paths`` is properly
513
:param paths: a sorted list of paths to compare
515
# First, make sure the paths being passed in are correct
517
dirname, basename = os.path.split(p)
518
return dirname.split('/'), basename
519
self.assertEqual(sorted(paths, key=_key), paths)
521
cmp_path_by_dirblock = self.get_cmp_path_by_dirblock()
522
for idx1, path1 in enumerate(paths):
523
for idx2, path2 in enumerate(paths):
524
cmp_val = cmp_path_by_dirblock(path1, path2)
526
self.assertTrue(cmp_val < 0,
527
'%s did not state that %r came before %r, cmp=%s'
528
% (cmp_path_by_dirblock.__name__,
529
path1, path2, cmp_val))
531
self.assertTrue(cmp_val > 0,
532
'%s did not state that %r came after %r, cmp=%s'
533
% (cmp_path_by_dirblock.__name__,
534
path1, path2, cmp_val))
536
self.assertTrue(cmp_val == 0,
537
'%s did not state that %r == %r, cmp=%s'
538
% (cmp_path_by_dirblock.__name__,
539
path1, path2, cmp_val))
541
def test_cmp_simple_paths(self):
542
"""Compare against the empty string."""
543
self.assertCmpPathByDirblock(['', 'a', 'ab', 'abc', 'a/b/c', 'b/d/e'])
544
self.assertCmpPathByDirblock(['kl', 'ab/cd', 'ab/ef', 'gh/ij'])
546
def test_tricky_paths(self):
547
self.assertCmpPathByDirblock([
549
'', 'a', 'a-a', 'a=a', 'b',
551
'a/a', 'a/a-a', 'a/a=a', 'a/b',
553
'a/a/a', 'a/a/a-a', 'a/a/a=a',
554
# Contents of 'a/a/a'
555
'a/a/a/a', 'a/a/a/b',
556
# Contents of 'a/a/a-a',
557
'a/a/a-a/a', 'a/a/a-a/b',
558
# Contents of 'a/a/a=a',
559
'a/a/a=a/a', 'a/a/a=a/b',
560
# Contents of 'a/a-a'
562
# Contents of 'a/a-a/a'
563
'a/a-a/a/a', 'a/a-a/a/b',
564
# Contents of 'a/a=a'
575
self.assertCmpPathByDirblock([
577
'', 'a', 'a-a', 'a-z', 'a=a', 'a=z',
579
'a/a', 'a/a-a', 'a/a-z',
581
'a/z', 'a/z-a', 'a/z-z',
605
def test_unicode_not_allowed(self):
606
cmp_path_by_dirblock = self.get_cmp_path_by_dirblock()
607
self.assertRaises(TypeError, cmp_path_by_dirblock, u'Uni', 'str')
608
self.assertRaises(TypeError, cmp_path_by_dirblock, 'str', u'Uni')
609
self.assertRaises(TypeError, cmp_path_by_dirblock, u'Uni', u'Uni')
610
self.assertRaises(TypeError, cmp_path_by_dirblock, u'x/Uni', 'x/str')
611
self.assertRaises(TypeError, cmp_path_by_dirblock, 'x/str', u'x/Uni')
612
self.assertRaises(TypeError, cmp_path_by_dirblock, u'x/Uni', u'x/Uni')
614
def test_nonascii(self):
615
self.assertCmpPathByDirblock([
617
'', 'a', '\xc2\xb5', '\xc3\xa5',
619
'a/a', 'a/\xc2\xb5', 'a/\xc3\xa5',
621
'a/a/a', 'a/a/\xc2\xb5', 'a/a/\xc3\xa5',
622
# content of 'a/\xc2\xb5'
623
'a/\xc2\xb5/a', 'a/\xc2\xb5/\xc2\xb5', 'a/\xc2\xb5/\xc3\xa5',
624
# content of 'a/\xc3\xa5'
625
'a/\xc3\xa5/a', 'a/\xc3\xa5/\xc2\xb5', 'a/\xc3\xa5/\xc3\xa5',
626
# content of '\xc2\xb5'
627
'\xc2\xb5/a', '\xc2\xb5/\xc2\xb5', '\xc2\xb5/\xc3\xa5',
628
# content of '\xc2\xe5'
629
'\xc3\xa5/a', '\xc3\xa5/\xc2\xb5', '\xc3\xa5/\xc3\xa5',
633
class TestCompiledCmpPathByDirblock(TestCmpPathByDirblock):
634
"""Test the pyrex implementation of _cmp_path_by_dirblock"""
636
_test_needs_features = [compiled_dirstate_helpers_feature]
638
def get_cmp_by_dirs(self):
639
from bzrlib._dirstate_helpers_pyx import _cmp_path_by_dirblock
640
return _cmp_path_by_dirblock
643
class TestMemRChr(tests.TestCase):
644
"""Test memrchr functionality"""
646
_test_needs_features = [compiled_dirstate_helpers_feature]
648
def assertMemRChr(self, expected, s, c):
649
from bzrlib._dirstate_helpers_pyx import _py_memrchr
650
self.assertEqual(expected, _py_memrchr(s, c))
652
def test_missing(self):
653
self.assertMemRChr(None, '', 'a')
654
self.assertMemRChr(None, '', 'c')
655
self.assertMemRChr(None, 'abcdefghijklm', 'q')
656
self.assertMemRChr(None, 'aaaaaaaaaaaaaaaaaaaaaaa', 'b')
658
def test_single_entry(self):
659
self.assertMemRChr(0, 'abcdefghijklm', 'a')
660
self.assertMemRChr(1, 'abcdefghijklm', 'b')
661
self.assertMemRChr(2, 'abcdefghijklm', 'c')
662
self.assertMemRChr(10, 'abcdefghijklm', 'k')
663
self.assertMemRChr(11, 'abcdefghijklm', 'l')
664
self.assertMemRChr(12, 'abcdefghijklm', 'm')
666
def test_multiple(self):
667
self.assertMemRChr(10, 'abcdefjklmabcdefghijklm', 'a')
668
self.assertMemRChr(11, 'abcdefjklmabcdefghijklm', 'b')
669
self.assertMemRChr(12, 'abcdefjklmabcdefghijklm', 'c')
670
self.assertMemRChr(20, 'abcdefjklmabcdefghijklm', 'k')
671
self.assertMemRChr(21, 'abcdefjklmabcdefghijklm', 'l')
672
self.assertMemRChr(22, 'abcdefjklmabcdefghijklm', 'm')
673
self.assertMemRChr(22, 'aaaaaaaaaaaaaaaaaaaaaaa', 'a')
675
def test_with_nulls(self):
676
self.assertMemRChr(10, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'a')
677
self.assertMemRChr(11, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'b')
678
self.assertMemRChr(12, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'c')
679
self.assertMemRChr(20, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'k')
680
self.assertMemRChr(21, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'l')
681
self.assertMemRChr(22, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'm')
682
self.assertMemRChr(22, 'aaa\0\0\0aaaaaaa\0\0\0aaaaaaa', 'a')
683
self.assertMemRChr(9, '\0\0\0\0\0\0\0\0\0\0', '\0')
686
class TestReadDirblocks(test_dirstate.TestCaseWithDirState):
687
"""Test an implementation of _read_dirblocks()
689
_read_dirblocks() reads in all of the dirblock information from the disk
692
Child test cases can override ``get_read_dirblocks`` to test a specific
696
# inherits scenarios from test_dirstate
698
def get_read_dirblocks(self):
699
from bzrlib._dirstate_helpers_py import _read_dirblocks
700
return _read_dirblocks
702
def test_smoketest(self):
703
"""Make sure that we can create and read back a simple file."""
704
tree, state, expected = self.create_basic_dirstate()
706
state._read_header_if_needed()
707
self.assertEqual(dirstate.DirState.NOT_IN_MEMORY,
708
state._dirblock_state)
709
read_dirblocks = self.get_read_dirblocks()
710
read_dirblocks(state)
711
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
712
state._dirblock_state)
714
def test_trailing_garbage(self):
715
tree, state, expected = self.create_basic_dirstate()
716
# On Unix, we can write extra data as long as we haven't read yet, but
717
# on Win32, if you've opened the file with FILE_SHARE_READ, trying to
718
# open it in append mode will fail.
720
f = open('dirstate', 'ab')
722
# Add bogus trailing garbage
727
e = self.assertRaises(errors.DirstateCorrupt,
728
state._read_dirblocks_if_needed)
729
# Make sure we mention the bogus characters in the error
730
self.assertContainsRe(str(e), 'bogus')
733
class TestCompiledReadDirblocks(TestReadDirblocks):
734
"""Test the pyrex implementation of _read_dirblocks"""
736
_test_needs_features = [compiled_dirstate_helpers_feature]
738
def get_read_dirblocks(self):
739
from bzrlib._dirstate_helpers_pyx import _read_dirblocks
740
return _read_dirblocks
743
class TestUsingCompiledIfAvailable(tests.TestCase):
744
"""Check that any compiled functions that are available are the default.
746
It is possible to have typos, etc in the import line, such that
747
_dirstate_helpers_pyx is actually available, but the compiled functions are
751
def test_bisect_dirblock(self):
752
if compiled_dirstate_helpers_feature.available():
753
from bzrlib._dirstate_helpers_pyx import bisect_dirblock
755
from bzrlib._dirstate_helpers_py import bisect_dirblock
756
self.assertIs(bisect_dirblock, dirstate.bisect_dirblock)
758
def test__bisect_path_left(self):
759
if compiled_dirstate_helpers_feature.available():
760
from bzrlib._dirstate_helpers_pyx import _bisect_path_left
762
from bzrlib._dirstate_helpers_py import _bisect_path_left
763
self.assertIs(_bisect_path_left, dirstate._bisect_path_left)
765
def test__bisect_path_right(self):
766
if compiled_dirstate_helpers_feature.available():
767
from bzrlib._dirstate_helpers_pyx import _bisect_path_right
769
from bzrlib._dirstate_helpers_py import _bisect_path_right
770
self.assertIs(_bisect_path_right, dirstate._bisect_path_right)
772
def test_cmp_by_dirs(self):
773
if compiled_dirstate_helpers_feature.available():
774
from bzrlib._dirstate_helpers_pyx import cmp_by_dirs
776
from bzrlib._dirstate_helpers_py import cmp_by_dirs
777
self.assertIs(cmp_by_dirs, dirstate.cmp_by_dirs)
779
def test__read_dirblocks(self):
780
if compiled_dirstate_helpers_feature.available():
781
from bzrlib._dirstate_helpers_pyx import _read_dirblocks
783
from bzrlib._dirstate_helpers_py import _read_dirblocks
784
self.assertIs(_read_dirblocks, dirstate._read_dirblocks)
786
def test_update_entry(self):
787
if compiled_dirstate_helpers_feature.available():
788
from bzrlib._dirstate_helpers_pyx import update_entry
790
from bzrlib.dirstate import update_entry
791
self.assertIs(update_entry, dirstate.update_entry)
793
def test_process_entry(self):
794
if compiled_dirstate_helpers_feature.available():
795
from bzrlib._dirstate_helpers_pyx import ProcessEntryC
796
self.assertIs(ProcessEntryC, dirstate._process_entry)
798
from bzrlib.dirstate import ProcessEntryPython
799
self.assertIs(ProcessEntryPython, dirstate._process_entry)
802
class TestUpdateEntry(test_dirstate.TestCaseWithDirState):
803
"""Test the DirState.update_entry functions"""
805
scenarios = multiply_scenarios(
806
dir_reader_scenarios(), ue_scenarios)
812
super(TestUpdateEntry, self).setUp()
813
self.overrideAttr(dirstate, 'update_entry', self.update_entry)
815
def get_state_with_a(self):
816
"""Create a DirState tracking a single object named 'a'"""
817
state = test_dirstate.InstrumentedDirState.initialize('dirstate')
818
self.addCleanup(state.unlock)
819
state.add('a', 'a-id', 'file', None, '')
820
entry = state._get_entry(0, path_utf8='a')
823
def test_observed_sha1_cachable(self):
824
state, entry = self.get_state_with_a()
825
atime = time.time() - 10
826
self.build_tree(['a'])
827
statvalue = os.lstat('a')
828
statvalue = test_dirstate._FakeStat(statvalue.st_size, atime, atime,
829
statvalue.st_dev, statvalue.st_ino, statvalue.st_mode)
830
state._observed_sha1(entry, "foo", statvalue)
831
self.assertEqual('foo', entry[1][0][1])
832
packed_stat = dirstate.pack_stat(statvalue)
833
self.assertEqual(packed_stat, entry[1][0][4])
835
def test_observed_sha1_not_cachable(self):
836
state, entry = self.get_state_with_a()
837
oldval = entry[1][0][1]
838
oldstat = entry[1][0][4]
839
self.build_tree(['a'])
840
statvalue = os.lstat('a')
841
state._observed_sha1(entry, "foo", statvalue)
842
self.assertEqual(oldval, entry[1][0][1])
843
self.assertEqual(oldstat, entry[1][0][4])
845
def test_update_entry(self):
846
state, _ = self.get_state_with_a()
847
tree = self.make_branch_and_tree('tree')
849
empty_revid = tree.commit('empty')
850
self.build_tree(['tree/a'])
851
tree.add(['a'], ['a-id'])
852
with_a_id = tree.commit('with_a')
853
self.addCleanup(tree.unlock)
854
state.set_parent_trees(
855
[(empty_revid, tree.branch.repository.revision_tree(empty_revid))],
857
entry = state._get_entry(0, path_utf8='a')
858
self.build_tree(['a'])
859
# Add one where we don't provide the stat or sha already
860
self.assertEqual(('', 'a', 'a-id'), entry[0])
861
self.assertEqual(('f', '', 0, False, dirstate.DirState.NULLSTAT),
863
# Flush the buffers to disk
865
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
866
state._dirblock_state)
868
stat_value = os.lstat('a')
869
packed_stat = dirstate.pack_stat(stat_value)
870
link_or_sha1 = self.update_entry(state, entry, abspath='a',
871
stat_value=stat_value)
872
self.assertEqual(None, link_or_sha1)
874
# The dirblock entry should not have cached the file's sha1 (too new)
875
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
877
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
878
state._dirblock_state)
879
mode = stat_value.st_mode
880
self.assertEqual([('is_exec', mode, False)], state._log)
883
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
884
state._dirblock_state)
886
# If we do it again right away, we don't know if the file has changed
887
# so we will re-read the file. Roll the clock back so the file is
888
# guaranteed to look too new.
889
state.adjust_time(-10)
892
link_or_sha1 = self.update_entry(state, entry, abspath='a',
893
stat_value=stat_value)
894
self.assertEqual([('is_exec', mode, False)], state._log)
895
self.assertEqual(None, link_or_sha1)
896
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
897
state._dirblock_state)
898
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
902
# If it is cachable (the clock has moved forward) but new it still
903
# won't calculate the sha or cache it.
904
state.adjust_time(+20)
906
link_or_sha1 = dirstate.update_entry(state, entry, abspath='a',
907
stat_value=stat_value)
908
self.assertEqual(None, link_or_sha1)
909
self.assertEqual([('is_exec', mode, False)], state._log)
910
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
913
# If the file is no longer new, and the clock has been moved forward
914
# sufficiently, it will cache the sha.
916
state.set_parent_trees(
917
[(with_a_id, tree.branch.repository.revision_tree(with_a_id))],
919
entry = state._get_entry(0, path_utf8='a')
921
link_or_sha1 = self.update_entry(state, entry, abspath='a',
922
stat_value=stat_value)
923
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
925
self.assertEqual([('is_exec', mode, False), ('sha1', 'a')],
927
self.assertEqual(('f', link_or_sha1, 14, False, packed_stat),
930
# Subsequent calls will just return the cached value
932
link_or_sha1 = self.update_entry(state, entry, abspath='a',
933
stat_value=stat_value)
934
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
936
self.assertEqual([], state._log)
937
self.assertEqual(('f', link_or_sha1, 14, False, packed_stat),
940
def test_update_entry_symlink(self):
941
"""Update entry should read symlinks."""
942
self.requireFeature(tests.SymlinkFeature)
943
state, entry = self.get_state_with_a()
945
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
946
state._dirblock_state)
947
os.symlink('target', 'a')
949
state.adjust_time(-10) # Make the symlink look new
950
stat_value = os.lstat('a')
951
packed_stat = dirstate.pack_stat(stat_value)
952
link_or_sha1 = self.update_entry(state, entry, abspath='a',
953
stat_value=stat_value)
954
self.assertEqual('target', link_or_sha1)
955
self.assertEqual([('read_link', 'a', '')], state._log)
956
# Dirblock is not updated (the link is too new)
957
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
959
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
960
state._dirblock_state)
962
# Because the stat_value looks new, we should re-read the target
963
link_or_sha1 = self.update_entry(state, entry, abspath='a',
964
stat_value=stat_value)
965
self.assertEqual('target', link_or_sha1)
966
self.assertEqual([('read_link', 'a', ''),
967
('read_link', 'a', ''),
969
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
971
state.adjust_time(+20) # Skip into the future, all files look old
972
link_or_sha1 = self.update_entry(state, entry, abspath='a',
973
stat_value=stat_value)
974
self.assertEqual('target', link_or_sha1)
975
# We need to re-read the link because only now can we cache it
976
self.assertEqual([('read_link', 'a', ''),
977
('read_link', 'a', ''),
978
('read_link', 'a', ''),
980
self.assertEqual([('l', 'target', 6, False, packed_stat)],
983
# Another call won't re-read the link
984
self.assertEqual([('read_link', 'a', ''),
985
('read_link', 'a', ''),
986
('read_link', 'a', ''),
988
link_or_sha1 = self.update_entry(state, entry, abspath='a',
989
stat_value=stat_value)
990
self.assertEqual('target', link_or_sha1)
991
self.assertEqual([('l', 'target', 6, False, packed_stat)],
994
def do_update_entry(self, state, entry, abspath):
995
stat_value = os.lstat(abspath)
996
return self.update_entry(state, entry, abspath, stat_value)
998
def test_update_entry_dir(self):
999
state, entry = self.get_state_with_a()
1000
self.build_tree(['a/'])
1001
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1003
def test_update_entry_dir_unchanged(self):
1004
state, entry = self.get_state_with_a()
1005
self.build_tree(['a/'])
1006
state.adjust_time(+20)
1007
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1008
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1009
state._dirblock_state)
1011
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1012
state._dirblock_state)
1013
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1014
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1015
state._dirblock_state)
1017
def test_update_entry_file_unchanged(self):
1018
state, _ = self.get_state_with_a()
1019
tree = self.make_branch_and_tree('tree')
1021
self.build_tree(['tree/a'])
1022
tree.add(['a'], ['a-id'])
1023
with_a_id = tree.commit('witha')
1024
self.addCleanup(tree.unlock)
1025
state.set_parent_trees(
1026
[(with_a_id, tree.branch.repository.revision_tree(with_a_id))],
1028
entry = state._get_entry(0, path_utf8='a')
1029
self.build_tree(['a'])
1030
sha1sum = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1031
state.adjust_time(+20)
1032
self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1033
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1034
state._dirblock_state)
1036
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1037
state._dirblock_state)
1038
self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1039
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1040
state._dirblock_state)
1042
def test_update_entry_tree_reference(self):
1043
state = test_dirstate.InstrumentedDirState.initialize('dirstate')
1044
self.addCleanup(state.unlock)
1045
state.add('r', 'r-id', 'tree-reference', None, '')
1046
self.build_tree(['r/'])
1047
entry = state._get_entry(0, path_utf8='r')
1048
self.do_update_entry(state, entry, 'r')
1049
entry = state._get_entry(0, path_utf8='r')
1050
self.assertEqual('t', entry[1][0][0])
1052
def create_and_test_file(self, state, entry):
1053
"""Create a file at 'a' and verify the state finds it during update.
1055
The state should already be versioning *something* at 'a'. This makes
1056
sure that state.update_entry recognizes it as a file.
1058
self.build_tree(['a'])
1059
stat_value = os.lstat('a')
1060
packed_stat = dirstate.pack_stat(stat_value)
1062
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1063
self.assertEqual(None, link_or_sha1)
1064
self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
1068
def create_and_test_dir(self, state, entry):
1069
"""Create a directory at 'a' and verify the state finds it.
1071
The state should already be versioning *something* at 'a'. This makes
1072
sure that state.update_entry recognizes it as a directory.
1074
self.build_tree(['a/'])
1075
stat_value = os.lstat('a')
1076
packed_stat = dirstate.pack_stat(stat_value)
1078
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1079
self.assertIs(None, link_or_sha1)
1080
self.assertEqual([('d', '', 0, False, packed_stat)], entry[1])
1084
# FIXME: Add unicode version
1085
def create_and_test_symlink(self, state, entry):
1086
"""Create a symlink at 'a' and verify the state finds it.
1088
The state should already be versioning *something* at 'a'. This makes
1089
sure that state.update_entry recognizes it as a symlink.
1091
This should not be called if this platform does not have symlink
1094
# caller should care about skipping test on platforms without symlinks
1095
os.symlink('path/to/foo', 'a')
1097
stat_value = os.lstat('a')
1098
packed_stat = dirstate.pack_stat(stat_value)
1100
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1101
self.assertEqual('path/to/foo', link_or_sha1)
1102
self.assertEqual([('l', 'path/to/foo', 11, False, packed_stat)],
1106
def test_update_file_to_dir(self):
1107
"""If a file changes to a directory we return None for the sha.
1108
We also update the inventory record.
1110
state, entry = self.get_state_with_a()
1111
# The file sha1 won't be cached unless the file is old
1112
state.adjust_time(+10)
1113
self.create_and_test_file(state, entry)
1115
self.create_and_test_dir(state, entry)
1117
def test_update_file_to_symlink(self):
1118
"""File becomes a symlink"""
1119
self.requireFeature(tests.SymlinkFeature)
1120
state, entry = self.get_state_with_a()
1121
# The file sha1 won't be cached unless the file is old
1122
state.adjust_time(+10)
1123
self.create_and_test_file(state, entry)
1125
self.create_and_test_symlink(state, entry)
1127
def test_update_dir_to_file(self):
1128
"""Directory becoming a file updates the entry."""
1129
state, entry = self.get_state_with_a()
1130
# The file sha1 won't be cached unless the file is old
1131
state.adjust_time(+10)
1132
self.create_and_test_dir(state, entry)
1134
self.create_and_test_file(state, entry)
1136
def test_update_dir_to_symlink(self):
1137
"""Directory becomes a symlink"""
1138
self.requireFeature(tests.SymlinkFeature)
1139
state, entry = self.get_state_with_a()
1140
# The symlink target won't be cached if it isn't old
1141
state.adjust_time(+10)
1142
self.create_and_test_dir(state, entry)
1144
self.create_and_test_symlink(state, entry)
1146
def test_update_symlink_to_file(self):
1147
"""Symlink becomes a file"""
1148
self.requireFeature(tests.SymlinkFeature)
1149
state, entry = self.get_state_with_a()
1150
# The symlink and file info won't be cached unless old
1151
state.adjust_time(+10)
1152
self.create_and_test_symlink(state, entry)
1154
self.create_and_test_file(state, entry)
1156
def test_update_symlink_to_dir(self):
1157
"""Symlink becomes a directory"""
1158
self.requireFeature(tests.SymlinkFeature)
1159
state, entry = self.get_state_with_a()
1160
# The symlink target won't be cached if it isn't old
1161
state.adjust_time(+10)
1162
self.create_and_test_symlink(state, entry)
1164
self.create_and_test_dir(state, entry)
1166
def test__is_executable_win32(self):
1167
state, entry = self.get_state_with_a()
1168
self.build_tree(['a'])
1170
# Make sure we are using the win32 implementation of _is_executable
1171
state._is_executable = state._is_executable_win32
1173
# The file on disk is not executable, but we are marking it as though
1174
# it is. With _is_executable_win32 we ignore what is on disk.
1175
entry[1][0] = ('f', '', 0, True, dirstate.DirState.NULLSTAT)
1177
stat_value = os.lstat('a')
1178
packed_stat = dirstate.pack_stat(stat_value)
1180
state.adjust_time(-10) # Make sure everything is new
1181
self.update_entry(state, entry, abspath='a', stat_value=stat_value)
1183
# The row is updated, but the executable bit stays set.
1184
self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1187
# Make the disk object look old enough to cache (but it won't cache the
1188
# sha as it is a new file).
1189
state.adjust_time(+20)
1190
digest = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1191
self.update_entry(state, entry, abspath='a', stat_value=stat_value)
1192
self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1195
def _prepare_tree(self):
1197
text = 'Hello World\n'
1198
tree = self.make_branch_and_tree('tree')
1199
self.build_tree_contents([('tree/a file', text)])
1200
tree.add('a file', 'a-file-id')
1201
# Note: dirstate does not sha prior to the first commit
1202
# so commit now in order for the test to work
1203
tree.commit('first')
1206
def test_sha1provider_sha1_used(self):
1207
tree, text = self._prepare_tree()
1208
state = dirstate.DirState.from_tree(tree, 'dirstate',
1209
UppercaseSHA1Provider())
1210
self.addCleanup(state.unlock)
1211
expected_sha = osutils.sha_string(text.upper() + "foo")
1212
entry = state._get_entry(0, path_utf8='a file')
1213
state._sha_cutoff_time()
1214
state._cutoff_time += 10
1215
sha1 = self.update_entry(state, entry, 'tree/a file',
1216
os.lstat('tree/a file'))
1217
self.assertEqual(expected_sha, sha1)
1219
def test_sha1provider_stat_and_sha1_used(self):
1220
tree, text = self._prepare_tree()
1222
self.addCleanup(tree.unlock)
1223
state = tree._current_dirstate()
1224
state._sha1_provider = UppercaseSHA1Provider()
1225
# If we used the standard provider, it would look like nothing has
1227
file_ids_changed = [change[0] for change
1228
in tree.iter_changes(tree.basis_tree())]
1229
self.assertEqual(['a-file-id'], file_ids_changed)
1232
class UppercaseSHA1Provider(dirstate.SHA1Provider):
1233
"""A custom SHA1Provider."""
1235
def sha1(self, abspath):
1236
return self.stat_and_sha1(abspath)[1]
1238
def stat_and_sha1(self, abspath):
1239
file_obj = file(abspath, 'rb')
1241
statvalue = os.fstat(file_obj.fileno())
1242
text = ''.join(file_obj.readlines())
1243
sha1 = osutils.sha_string(text.upper() + "foo")
1246
return statvalue, sha1
1249
class TestProcessEntry(test_dirstate.TestCaseWithDirState):
1251
scenarios = multiply_scenarios(dir_reader_scenarios(), pe_scenarios)
1254
_process_entry = None
1257
super(TestProcessEntry, self).setUp()
1258
self.overrideAttr(dirstate, '_process_entry', self._process_entry)
1260
def assertChangedFileIds(self, expected, tree):
1263
file_ids = [info[0] for info
1264
in tree.iter_changes(tree.basis_tree())]
1267
self.assertEqual(sorted(expected), sorted(file_ids))
1269
def test_exceptions_raised(self):
1270
# This is a direct test of bug #495023, it relies on osutils.is_inside
1271
# getting called in an inner function. Which makes it a bit brittle,
1272
# but at least it does reproduce the bug.
1273
tree = self.make_branch_and_tree('tree')
1274
self.build_tree(['tree/file', 'tree/dir/', 'tree/dir/sub',
1275
'tree/dir2/', 'tree/dir2/sub2'])
1276
tree.add(['file', 'dir', 'dir/sub', 'dir2', 'dir2/sub2'])
1277
tree.commit('first commit')
1279
self.addCleanup(tree.unlock)
1280
basis_tree = tree.basis_tree()
1281
def is_inside_raises(*args, **kwargs):
1282
raise RuntimeError('stop this')
1283
self.overrideAttr(osutils, 'is_inside', is_inside_raises)
1284
self.assertListRaises(RuntimeError, tree.iter_changes, basis_tree)
1286
def test_simple_changes(self):
1287
tree = self.make_branch_and_tree('tree')
1288
self.build_tree(['tree/file'])
1289
tree.add(['file'], ['file-id'])
1290
self.assertChangedFileIds([tree.get_root_id(), 'file-id'], tree)
1292
self.assertChangedFileIds([], tree)
1294
def test_sha1provider_stat_and_sha1_used(self):
1295
tree = self.make_branch_and_tree('tree')
1296
self.build_tree(['tree/file'])
1297
tree.add(['file'], ['file-id'])
1300
self.addCleanup(tree.unlock)
1301
state = tree._current_dirstate()
1302
state._sha1_provider = UppercaseSHA1Provider()
1303
self.assertChangedFileIds(['file-id'], tree)