1
# Copyright (C) 2007, 2008, 2009 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 (
35
from bzrlib import _dirstate_helpers_pyx
36
has_dirstate_helpers_pyx = True
38
has_dirstate_helpers_pyx = False
41
compiled_dirstate_helpers_feature = tests.ModuleAvailableFeature(
42
'bzrlib._dirstate_helpers_pyx')
45
def load_tests(basic_tests, module, loader):
46
# FIXME: we should also parametrize against SHA1Provider !
47
suite = loader.suiteClass()
48
remaining_tests = basic_tests
50
dir_reader_scenarios = test_osutils.dir_reader_scenarios()
52
ue_scenarios = [('dirstate_Python',
53
{'update_entry': dirstate.py_update_entry})]
54
if compiled_dirstate_helpers_feature.available():
55
update_entry = compiled_dirstate_helpers_feature.module.update_entry
56
pyrex_scenario = ('dirstate_Pyrex', {'update_entry': update_entry})
57
ue_scenarios.append(pyrex_scenario)
58
process_entry_tests, remaining_tests = tests.split_suite_by_condition(
59
remaining_tests, tests.condition_isinstance(TestUpdateEntry))
60
tests.multiply_tests(process_entry_tests,
61
tests.multiply_scenarios(dir_reader_scenarios,
65
pe_scenarios = [('dirstate_Python',
66
{'_process_entry': dirstate.ProcessEntryPython})]
67
if compiled_dirstate_helpers_feature.available():
68
process_entry = compiled_dirstate_helpers_feature.module.ProcessEntryC
69
pyrex_scenario = ('dirstate_Pyrex', {'_process_entry': process_entry})
70
pe_scenarios.append(pyrex_scenario)
71
process_entry_tests, remaining_tests = tests.split_suite_by_condition(
72
remaining_tests, tests.condition_isinstance(TestProcessEntry))
73
tests.multiply_tests(process_entry_tests,
74
tests.multiply_scenarios(dir_reader_scenarios,
78
dir_reader_tests, remaining_tests = tests.split_suite_by_condition(
79
remaining_tests, tests.condition_isinstance(
80
test_dirstate.TestCaseWithDirState))
81
tests.multiply_tests(dir_reader_tests, dir_reader_scenarios, suite)
82
suite.addTest(remaining_tests)
87
class TestBisectPathMixin(object):
88
"""Test that _bisect_path_*() returns the expected values.
90
_bisect_path_* is intended to work like bisect.bisect_*() except it
91
knows it is working on paths that are sorted by ('path', 'to', 'foo')
92
chunks rather than by raw 'path/to/foo'.
94
Test Cases should inherit from this and override ``get_bisect_path`` return
95
their implementation, and ``get_bisect`` to return the matching
96
bisect.bisect_* function.
99
def get_bisect_path(self):
100
"""Return an implementation of _bisect_path_*"""
101
raise NotImplementedError
103
def get_bisect(self):
104
"""Return a version of bisect.bisect_*.
106
Also, for the 'exists' check, return the offset to the real values.
107
For example bisect_left returns the index of an entry, while
108
bisect_right returns the index *after* an entry
110
:return: (bisect_func, offset)
112
raise NotImplementedError
114
def assertBisect(self, paths, split_paths, path, exists=True):
115
"""Assert that bisect_split works like bisect_left on the split paths.
117
:param paths: A list of path names
118
:param split_paths: A list of path names that are already split up by directory
119
('path/to/foo' => ('path', 'to', 'foo'))
120
:param path: The path we are indexing.
121
:param exists: The path should be present, so make sure the
122
final location actually points to the right value.
124
All other arguments will be passed along.
126
bisect_path = self.get_bisect_path()
127
self.assertIsInstance(paths, list)
128
bisect_path_idx = bisect_path(paths, path)
129
split_path = self.split_for_dirblocks([path])[0]
130
bisect_func, offset = self.get_bisect()
131
bisect_split_idx = bisect_func(split_paths, split_path)
132
self.assertEqual(bisect_split_idx, bisect_path_idx,
133
'%s disagreed. %s != %s'
135
% (bisect_path.__name__,
136
bisect_split_idx, bisect_path_idx, path)
139
self.assertEqual(path, paths[bisect_path_idx+offset])
141
def split_for_dirblocks(self, paths):
144
dirname, basename = os.path.split(path)
145
dir_split_paths.append((dirname.split('/'), basename))
146
dir_split_paths.sort()
147
return dir_split_paths
149
def test_simple(self):
150
"""In the simple case it works just like bisect_left"""
151
paths = ['', 'a', 'b', 'c', 'd']
152
split_paths = self.split_for_dirblocks(paths)
154
self.assertBisect(paths, split_paths, path, exists=True)
155
self.assertBisect(paths, split_paths, '_', exists=False)
156
self.assertBisect(paths, split_paths, 'aa', exists=False)
157
self.assertBisect(paths, split_paths, 'bb', exists=False)
158
self.assertBisect(paths, split_paths, 'cc', exists=False)
159
self.assertBisect(paths, split_paths, 'dd', exists=False)
160
self.assertBisect(paths, split_paths, 'a/a', exists=False)
161
self.assertBisect(paths, split_paths, 'b/b', exists=False)
162
self.assertBisect(paths, split_paths, 'c/c', exists=False)
163
self.assertBisect(paths, split_paths, 'd/d', exists=False)
165
def test_involved(self):
166
"""This is where bisect_path_* diverges slightly."""
167
# This is the list of paths and their contents
195
# This is the exact order that is stored by dirstate
196
# All children in a directory are mentioned before an children of
197
# children are mentioned.
198
# So all the root-directory paths, then all the
199
# first sub directory, etc.
200
paths = [# content of '/'
201
'', 'a', 'a-a', 'a-z', 'a=a', 'a=z',
203
'a/a', 'a/a-a', 'a/a-z',
205
'a/z', 'a/z-a', 'a/z-z',
228
split_paths = self.split_for_dirblocks(paths)
230
for dir_parts, basename in split_paths:
231
if dir_parts == ['']:
232
sorted_paths.append(basename)
234
sorted_paths.append('/'.join(dir_parts + [basename]))
236
self.assertEqual(sorted_paths, paths)
239
self.assertBisect(paths, split_paths, path, exists=True)
242
class TestBisectPathLeft(tests.TestCase, TestBisectPathMixin):
243
"""Run all Bisect Path tests against _bisect_path_left."""
245
def get_bisect_path(self):
246
from bzrlib._dirstate_helpers_py import _bisect_path_left
247
return _bisect_path_left
249
def get_bisect(self):
250
return bisect.bisect_left, 0
253
class TestCompiledBisectPathLeft(TestBisectPathLeft):
254
"""Run all Bisect Path tests against _bisect_path_lect"""
256
_test_needs_features = [compiled_dirstate_helpers_feature]
258
def get_bisect_path(self):
259
from bzrlib._dirstate_helpers_pyx import _bisect_path_left
260
return _bisect_path_left
263
class TestBisectPathRight(tests.TestCase, TestBisectPathMixin):
264
"""Run all Bisect Path tests against _bisect_path_right"""
266
def get_bisect_path(self):
267
from bzrlib._dirstate_helpers_py import _bisect_path_right
268
return _bisect_path_right
270
def get_bisect(self):
271
return bisect.bisect_right, -1
274
class TestCompiledBisectPathRight(TestBisectPathRight):
275
"""Run all Bisect Path tests against _bisect_path_right"""
277
_test_needs_features = [compiled_dirstate_helpers_feature]
279
def get_bisect_path(self):
280
from bzrlib._dirstate_helpers_pyx import _bisect_path_right
281
return _bisect_path_right
284
class TestBisectDirblock(tests.TestCase):
285
"""Test that bisect_dirblock() returns the expected values.
287
bisect_dirblock is intended to work like bisect.bisect_left() except it
288
knows it is working on dirblocks and that dirblocks are sorted by ('path',
289
'to', 'foo') chunks rather than by raw 'path/to/foo'.
291
This test is parameterized by calling get_bisect_dirblock(). Child test
292
cases can override this function to test against a different
296
def get_bisect_dirblock(self):
297
"""Return an implementation of bisect_dirblock"""
298
from bzrlib._dirstate_helpers_py import bisect_dirblock
299
return bisect_dirblock
301
def assertBisect(self, dirblocks, split_dirblocks, path, *args, **kwargs):
302
"""Assert that bisect_split works like bisect_left on the split paths.
304
:param dirblocks: A list of (path, [info]) pairs.
305
:param split_dirblocks: A list of ((split, path), [info]) pairs.
306
:param path: The path we are indexing.
308
All other arguments will be passed along.
310
bisect_dirblock = self.get_bisect_dirblock()
311
self.assertIsInstance(dirblocks, list)
312
bisect_split_idx = bisect_dirblock(dirblocks, path, *args, **kwargs)
313
split_dirblock = (path.split('/'), [])
314
bisect_left_idx = bisect.bisect_left(split_dirblocks, split_dirblock,
316
self.assertEqual(bisect_left_idx, bisect_split_idx,
317
'bisect_split disagreed. %s != %s'
319
% (bisect_left_idx, bisect_split_idx, path)
322
def paths_to_dirblocks(self, paths):
323
"""Convert a list of paths into dirblock form.
325
Also, ensure that the paths are in proper sorted order.
327
dirblocks = [(path, []) for path in paths]
328
split_dirblocks = [(path.split('/'), []) for path in paths]
329
self.assertEqual(sorted(split_dirblocks), split_dirblocks)
330
return dirblocks, split_dirblocks
332
def test_simple(self):
333
"""In the simple case it works just like bisect_left"""
334
paths = ['', 'a', 'b', 'c', 'd']
335
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
337
self.assertBisect(dirblocks, split_dirblocks, path)
338
self.assertBisect(dirblocks, split_dirblocks, '_')
339
self.assertBisect(dirblocks, split_dirblocks, 'aa')
340
self.assertBisect(dirblocks, split_dirblocks, 'bb')
341
self.assertBisect(dirblocks, split_dirblocks, 'cc')
342
self.assertBisect(dirblocks, split_dirblocks, 'dd')
343
self.assertBisect(dirblocks, split_dirblocks, 'a/a')
344
self.assertBisect(dirblocks, split_dirblocks, 'b/b')
345
self.assertBisect(dirblocks, split_dirblocks, 'c/c')
346
self.assertBisect(dirblocks, split_dirblocks, 'd/d')
348
def test_involved(self):
349
"""This is where bisect_left diverges slightly."""
351
'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
352
'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
354
'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
355
'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
358
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
360
self.assertBisect(dirblocks, split_dirblocks, path)
362
def test_involved_cached(self):
363
"""This is where bisect_left diverges slightly."""
365
'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
366
'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
368
'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
369
'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
373
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
375
self.assertBisect(dirblocks, split_dirblocks, path, cache=cache)
378
class TestCompiledBisectDirblock(TestBisectDirblock):
379
"""Test that bisect_dirblock() returns the expected values.
381
bisect_dirblock is intended to work like bisect.bisect_left() except it
382
knows it is working on dirblocks and that dirblocks are sorted by ('path',
383
'to', 'foo') chunks rather than by raw 'path/to/foo'.
385
This runs all the normal tests that TestBisectDirblock did, but uses the
389
_test_needs_features = [compiled_dirstate_helpers_feature]
391
def get_bisect_dirblock(self):
392
from bzrlib._dirstate_helpers_pyx import bisect_dirblock
393
return bisect_dirblock
396
class TestCmpByDirs(tests.TestCase):
397
"""Test an implementation of cmp_by_dirs()
399
cmp_by_dirs() compares 2 paths by their directory sections, rather than as
402
Child test cases can override ``get_cmp_by_dirs`` to test a specific
406
def get_cmp_by_dirs(self):
407
"""Get a specific implementation of cmp_by_dirs."""
408
from bzrlib._dirstate_helpers_py import cmp_by_dirs
411
def assertCmpByDirs(self, expected, str1, str2):
412
"""Compare the two strings, in both directions.
414
:param expected: The expected comparison value. -1 means str1 comes
415
first, 0 means they are equal, 1 means str2 comes first
416
:param str1: string to compare
417
:param str2: string to compare
419
cmp_by_dirs = self.get_cmp_by_dirs()
421
self.assertEqual(str1, str2)
422
self.assertEqual(0, cmp_by_dirs(str1, str2))
423
self.assertEqual(0, cmp_by_dirs(str2, str1))
425
self.assertPositive(cmp_by_dirs(str1, str2))
426
self.assertNegative(cmp_by_dirs(str2, str1))
428
self.assertNegative(cmp_by_dirs(str1, str2))
429
self.assertPositive(cmp_by_dirs(str2, str1))
431
def test_cmp_empty(self):
432
"""Compare against the empty string."""
433
self.assertCmpByDirs(0, '', '')
434
self.assertCmpByDirs(1, 'a', '')
435
self.assertCmpByDirs(1, 'ab', '')
436
self.assertCmpByDirs(1, 'abc', '')
437
self.assertCmpByDirs(1, 'abcd', '')
438
self.assertCmpByDirs(1, 'abcde', '')
439
self.assertCmpByDirs(1, 'abcdef', '')
440
self.assertCmpByDirs(1, 'abcdefg', '')
441
self.assertCmpByDirs(1, 'abcdefgh', '')
442
self.assertCmpByDirs(1, 'abcdefghi', '')
443
self.assertCmpByDirs(1, 'test/ing/a/path/', '')
445
def test_cmp_same_str(self):
446
"""Compare the same string"""
447
self.assertCmpByDirs(0, 'a', 'a')
448
self.assertCmpByDirs(0, 'ab', 'ab')
449
self.assertCmpByDirs(0, 'abc', 'abc')
450
self.assertCmpByDirs(0, 'abcd', 'abcd')
451
self.assertCmpByDirs(0, 'abcde', 'abcde')
452
self.assertCmpByDirs(0, 'abcdef', 'abcdef')
453
self.assertCmpByDirs(0, 'abcdefg', 'abcdefg')
454
self.assertCmpByDirs(0, 'abcdefgh', 'abcdefgh')
455
self.assertCmpByDirs(0, 'abcdefghi', 'abcdefghi')
456
self.assertCmpByDirs(0, 'testing a long string', 'testing a long string')
457
self.assertCmpByDirs(0, 'x'*10000, 'x'*10000)
458
self.assertCmpByDirs(0, 'a/b', 'a/b')
459
self.assertCmpByDirs(0, 'a/b/c', 'a/b/c')
460
self.assertCmpByDirs(0, 'a/b/c/d', 'a/b/c/d')
461
self.assertCmpByDirs(0, 'a/b/c/d/e', 'a/b/c/d/e')
463
def test_simple_paths(self):
464
"""Compare strings that act like normal string comparison"""
465
self.assertCmpByDirs(-1, 'a', 'b')
466
self.assertCmpByDirs(-1, 'aa', 'ab')
467
self.assertCmpByDirs(-1, 'ab', 'bb')
468
self.assertCmpByDirs(-1, 'aaa', 'aab')
469
self.assertCmpByDirs(-1, 'aab', 'abb')
470
self.assertCmpByDirs(-1, 'abb', 'bbb')
471
self.assertCmpByDirs(-1, 'aaaa', 'aaab')
472
self.assertCmpByDirs(-1, 'aaab', 'aabb')
473
self.assertCmpByDirs(-1, 'aabb', 'abbb')
474
self.assertCmpByDirs(-1, 'abbb', 'bbbb')
475
self.assertCmpByDirs(-1, 'aaaaa', 'aaaab')
476
self.assertCmpByDirs(-1, 'a/a', 'a/b')
477
self.assertCmpByDirs(-1, 'a/b', 'b/b')
478
self.assertCmpByDirs(-1, 'a/a/a', 'a/a/b')
479
self.assertCmpByDirs(-1, 'a/a/b', 'a/b/b')
480
self.assertCmpByDirs(-1, 'a/b/b', 'b/b/b')
481
self.assertCmpByDirs(-1, 'a/a/a/a', 'a/a/a/b')
482
self.assertCmpByDirs(-1, 'a/a/a/b', 'a/a/b/b')
483
self.assertCmpByDirs(-1, 'a/a/b/b', 'a/b/b/b')
484
self.assertCmpByDirs(-1, 'a/b/b/b', 'b/b/b/b')
485
self.assertCmpByDirs(-1, 'a/a/a/a/a', 'a/a/a/a/b')
487
def test_tricky_paths(self):
488
self.assertCmpByDirs(1, 'ab/cd/ef', 'ab/cc/ef')
489
self.assertCmpByDirs(1, 'ab/cd/ef', 'ab/c/ef')
490
self.assertCmpByDirs(-1, 'ab/cd/ef', 'ab/cd-ef')
491
self.assertCmpByDirs(-1, 'ab/cd', 'ab/cd-')
492
self.assertCmpByDirs(-1, 'ab/cd', 'ab-cd')
494
def test_cmp_unicode_not_allowed(self):
495
cmp_by_dirs = self.get_cmp_by_dirs()
496
self.assertRaises(TypeError, cmp_by_dirs, u'Unicode', 'str')
497
self.assertRaises(TypeError, cmp_by_dirs, 'str', u'Unicode')
498
self.assertRaises(TypeError, cmp_by_dirs, u'Unicode', u'Unicode')
500
def test_cmp_non_ascii(self):
501
self.assertCmpByDirs(-1, '\xc2\xb5', '\xc3\xa5') # u'\xb5', u'\xe5'
502
self.assertCmpByDirs(-1, 'a', '\xc3\xa5') # u'a', u'\xe5'
503
self.assertCmpByDirs(-1, 'b', '\xc2\xb5') # u'b', u'\xb5'
504
self.assertCmpByDirs(-1, 'a/b', 'a/\xc3\xa5') # u'a/b', u'a/\xe5'
505
self.assertCmpByDirs(-1, 'b/a', 'b/\xc2\xb5') # u'b/a', u'b/\xb5'
508
class TestCompiledCmpByDirs(TestCmpByDirs):
509
"""Test the pyrex implementation of cmp_by_dirs"""
511
_test_needs_features = [compiled_dirstate_helpers_feature]
513
def get_cmp_by_dirs(self):
514
from bzrlib._dirstate_helpers_pyx import cmp_by_dirs
518
class TestCmpPathByDirblock(tests.TestCase):
519
"""Test an implementation of _cmp_path_by_dirblock()
521
_cmp_path_by_dirblock() compares two paths using the sort order used by
522
DirState. All paths in the same directory are sorted together.
524
Child test cases can override ``get_cmp_path_by_dirblock`` to test a specific
528
def get_cmp_path_by_dirblock(self):
529
"""Get a specific implementation of _cmp_path_by_dirblock."""
530
from bzrlib._dirstate_helpers_py import _cmp_path_by_dirblock
531
return _cmp_path_by_dirblock
533
def assertCmpPathByDirblock(self, paths):
534
"""Compare all paths and make sure they evaluate to the correct order.
536
This does N^2 comparisons. It is assumed that ``paths`` is properly
539
:param paths: a sorted list of paths to compare
541
# First, make sure the paths being passed in are correct
543
dirname, basename = os.path.split(p)
544
return dirname.split('/'), basename
545
self.assertEqual(sorted(paths, key=_key), paths)
547
cmp_path_by_dirblock = self.get_cmp_path_by_dirblock()
548
for idx1, path1 in enumerate(paths):
549
for idx2, path2 in enumerate(paths):
550
cmp_val = cmp_path_by_dirblock(path1, path2)
552
self.assertTrue(cmp_val < 0,
553
'%s did not state that %r came before %r, cmp=%s'
554
% (cmp_path_by_dirblock.__name__,
555
path1, path2, cmp_val))
557
self.assertTrue(cmp_val > 0,
558
'%s did not state that %r came after %r, cmp=%s'
559
% (cmp_path_by_dirblock.__name__,
560
path1, path2, cmp_val))
562
self.assertTrue(cmp_val == 0,
563
'%s did not state that %r == %r, cmp=%s'
564
% (cmp_path_by_dirblock.__name__,
565
path1, path2, cmp_val))
567
def test_cmp_simple_paths(self):
568
"""Compare against the empty string."""
569
self.assertCmpPathByDirblock(['', 'a', 'ab', 'abc', 'a/b/c', 'b/d/e'])
570
self.assertCmpPathByDirblock(['kl', 'ab/cd', 'ab/ef', 'gh/ij'])
572
def test_tricky_paths(self):
573
self.assertCmpPathByDirblock([
575
'', 'a', 'a-a', 'a=a', 'b',
577
'a/a', 'a/a-a', 'a/a=a', 'a/b',
579
'a/a/a', 'a/a/a-a', 'a/a/a=a',
580
# Contents of 'a/a/a'
581
'a/a/a/a', 'a/a/a/b',
582
# Contents of 'a/a/a-a',
583
'a/a/a-a/a', 'a/a/a-a/b',
584
# Contents of 'a/a/a=a',
585
'a/a/a=a/a', 'a/a/a=a/b',
586
# Contents of 'a/a-a'
588
# Contents of 'a/a-a/a'
589
'a/a-a/a/a', 'a/a-a/a/b',
590
# Contents of 'a/a=a'
601
self.assertCmpPathByDirblock([
603
'', 'a', 'a-a', 'a-z', 'a=a', 'a=z',
605
'a/a', 'a/a-a', 'a/a-z',
607
'a/z', 'a/z-a', 'a/z-z',
631
def test_unicode_not_allowed(self):
632
cmp_path_by_dirblock = self.get_cmp_path_by_dirblock()
633
self.assertRaises(TypeError, cmp_path_by_dirblock, u'Uni', 'str')
634
self.assertRaises(TypeError, cmp_path_by_dirblock, 'str', u'Uni')
635
self.assertRaises(TypeError, cmp_path_by_dirblock, u'Uni', u'Uni')
636
self.assertRaises(TypeError, cmp_path_by_dirblock, u'x/Uni', 'x/str')
637
self.assertRaises(TypeError, cmp_path_by_dirblock, 'x/str', u'x/Uni')
638
self.assertRaises(TypeError, cmp_path_by_dirblock, u'x/Uni', u'x/Uni')
640
def test_nonascii(self):
641
self.assertCmpPathByDirblock([
643
'', 'a', '\xc2\xb5', '\xc3\xa5',
645
'a/a', 'a/\xc2\xb5', 'a/\xc3\xa5',
647
'a/a/a', 'a/a/\xc2\xb5', 'a/a/\xc3\xa5',
648
# content of 'a/\xc2\xb5'
649
'a/\xc2\xb5/a', 'a/\xc2\xb5/\xc2\xb5', 'a/\xc2\xb5/\xc3\xa5',
650
# content of 'a/\xc3\xa5'
651
'a/\xc3\xa5/a', 'a/\xc3\xa5/\xc2\xb5', 'a/\xc3\xa5/\xc3\xa5',
652
# content of '\xc2\xb5'
653
'\xc2\xb5/a', '\xc2\xb5/\xc2\xb5', '\xc2\xb5/\xc3\xa5',
654
# content of '\xc2\xe5'
655
'\xc3\xa5/a', '\xc3\xa5/\xc2\xb5', '\xc3\xa5/\xc3\xa5',
659
class TestCompiledCmpPathByDirblock(TestCmpPathByDirblock):
660
"""Test the pyrex implementation of _cmp_path_by_dirblock"""
662
_test_needs_features = [compiled_dirstate_helpers_feature]
664
def get_cmp_by_dirs(self):
665
from bzrlib._dirstate_helpers_pyx import _cmp_path_by_dirblock
666
return _cmp_path_by_dirblock
669
class TestMemRChr(tests.TestCase):
670
"""Test memrchr functionality"""
672
_test_needs_features = [compiled_dirstate_helpers_feature]
674
def assertMemRChr(self, expected, s, c):
675
from bzrlib._dirstate_helpers_pyx import _py_memrchr
676
self.assertEqual(expected, _py_memrchr(s, c))
678
def test_missing(self):
679
self.assertMemRChr(None, '', 'a')
680
self.assertMemRChr(None, '', 'c')
681
self.assertMemRChr(None, 'abcdefghijklm', 'q')
682
self.assertMemRChr(None, 'aaaaaaaaaaaaaaaaaaaaaaa', 'b')
684
def test_single_entry(self):
685
self.assertMemRChr(0, 'abcdefghijklm', 'a')
686
self.assertMemRChr(1, 'abcdefghijklm', 'b')
687
self.assertMemRChr(2, 'abcdefghijklm', 'c')
688
self.assertMemRChr(10, 'abcdefghijklm', 'k')
689
self.assertMemRChr(11, 'abcdefghijklm', 'l')
690
self.assertMemRChr(12, 'abcdefghijklm', 'm')
692
def test_multiple(self):
693
self.assertMemRChr(10, 'abcdefjklmabcdefghijklm', 'a')
694
self.assertMemRChr(11, 'abcdefjklmabcdefghijklm', 'b')
695
self.assertMemRChr(12, 'abcdefjklmabcdefghijklm', 'c')
696
self.assertMemRChr(20, 'abcdefjklmabcdefghijklm', 'k')
697
self.assertMemRChr(21, 'abcdefjklmabcdefghijklm', 'l')
698
self.assertMemRChr(22, 'abcdefjklmabcdefghijklm', 'm')
699
self.assertMemRChr(22, 'aaaaaaaaaaaaaaaaaaaaaaa', 'a')
701
def test_with_nulls(self):
702
self.assertMemRChr(10, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'a')
703
self.assertMemRChr(11, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'b')
704
self.assertMemRChr(12, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'c')
705
self.assertMemRChr(20, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'k')
706
self.assertMemRChr(21, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'l')
707
self.assertMemRChr(22, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'm')
708
self.assertMemRChr(22, 'aaa\0\0\0aaaaaaa\0\0\0aaaaaaa', 'a')
709
self.assertMemRChr(9, '\0\0\0\0\0\0\0\0\0\0', '\0')
712
class TestReadDirblocks(test_dirstate.TestCaseWithDirState):
713
"""Test an implementation of _read_dirblocks()
715
_read_dirblocks() reads in all of the dirblock information from the disk
718
Child test cases can override ``get_read_dirblocks`` to test a specific
722
def get_read_dirblocks(self):
723
from bzrlib._dirstate_helpers_py import _read_dirblocks
724
return _read_dirblocks
726
def test_smoketest(self):
727
"""Make sure that we can create and read back a simple file."""
728
tree, state, expected = self.create_basic_dirstate()
730
state._read_header_if_needed()
731
self.assertEqual(dirstate.DirState.NOT_IN_MEMORY,
732
state._dirblock_state)
733
read_dirblocks = self.get_read_dirblocks()
734
read_dirblocks(state)
735
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
736
state._dirblock_state)
738
def test_trailing_garbage(self):
739
tree, state, expected = self.create_basic_dirstate()
740
# On Linux, we can write extra data as long as we haven't read yet, but
741
# on Win32, if you've opened the file with FILE_SHARE_READ, trying to
742
# open it in append mode will fail.
744
f = open('dirstate', 'ab')
746
# Add bogus trailing garbage
751
e = self.assertRaises(errors.DirstateCorrupt,
752
state._read_dirblocks_if_needed)
753
# Make sure we mention the bogus characters in the error
754
self.assertContainsRe(str(e), 'bogus')
757
class TestCompiledReadDirblocks(TestReadDirblocks):
758
"""Test the pyrex implementation of _read_dirblocks"""
760
_test_needs_features = [compiled_dirstate_helpers_feature]
762
def get_read_dirblocks(self):
763
from bzrlib._dirstate_helpers_pyx import _read_dirblocks
764
return _read_dirblocks
767
class TestUsingCompiledIfAvailable(tests.TestCase):
768
"""Check that any compiled functions that are available are the default.
770
It is possible to have typos, etc in the import line, such that
771
_dirstate_helpers_pyx is actually available, but the compiled functions are
775
def test_bisect_dirblock(self):
776
if compiled_dirstate_helpers_feature.available():
777
from bzrlib._dirstate_helpers_pyx import bisect_dirblock
779
from bzrlib._dirstate_helpers_py import bisect_dirblock
780
self.assertIs(bisect_dirblock, dirstate.bisect_dirblock)
782
def test__bisect_path_left(self):
783
if compiled_dirstate_helpers_feature.available():
784
from bzrlib._dirstate_helpers_pyx import _bisect_path_left
786
from bzrlib._dirstate_helpers_py import _bisect_path_left
787
self.assertIs(_bisect_path_left, dirstate._bisect_path_left)
789
def test__bisect_path_right(self):
790
if compiled_dirstate_helpers_feature.available():
791
from bzrlib._dirstate_helpers_pyx import _bisect_path_right
793
from bzrlib._dirstate_helpers_py import _bisect_path_right
794
self.assertIs(_bisect_path_right, dirstate._bisect_path_right)
796
def test_cmp_by_dirs(self):
797
if compiled_dirstate_helpers_feature.available():
798
from bzrlib._dirstate_helpers_pyx import cmp_by_dirs
800
from bzrlib._dirstate_helpers_py import cmp_by_dirs
801
self.assertIs(cmp_by_dirs, dirstate.cmp_by_dirs)
803
def test__read_dirblocks(self):
804
if compiled_dirstate_helpers_feature.available():
805
from bzrlib._dirstate_helpers_pyx import _read_dirblocks
807
from bzrlib._dirstate_helpers_py import _read_dirblocks
808
self.assertIs(_read_dirblocks, dirstate._read_dirblocks)
810
def test_update_entry(self):
811
if compiled_dirstate_helpers_feature.available():
812
from bzrlib._dirstate_helpers_pyx import update_entry
814
from bzrlib.dirstate import update_entry
815
self.assertIs(update_entry, dirstate.update_entry)
817
def test_process_entry(self):
818
if compiled_dirstate_helpers_feature.available():
819
from bzrlib._dirstate_helpers_pyx import ProcessEntryC
820
self.assertIs(ProcessEntryC, dirstate._process_entry)
822
from bzrlib.dirstate import ProcessEntryPython
823
self.assertIs(ProcessEntryPython, dirstate._process_entry)
826
class TestUpdateEntry(test_dirstate.TestCaseWithDirState):
827
"""Test the DirState.update_entry functions"""
833
super(TestUpdateEntry, self).setUp()
834
self.overrideAttr(dirstate, 'update_entry', self.update_entry)
836
def get_state_with_a(self):
837
"""Create a DirState tracking a single object named 'a'"""
838
state = test_dirstate.InstrumentedDirState.initialize('dirstate')
839
self.addCleanup(state.unlock)
840
state.add('a', 'a-id', 'file', None, '')
841
entry = state._get_entry(0, path_utf8='a')
844
def test_observed_sha1_cachable(self):
845
state, entry = self.get_state_with_a()
846
atime = time.time() - 10
847
self.build_tree(['a'])
848
statvalue = os.lstat('a')
849
statvalue = test_dirstate._FakeStat(statvalue.st_size, atime, atime,
850
statvalue.st_dev, statvalue.st_ino, statvalue.st_mode)
851
state._observed_sha1(entry, "foo", statvalue)
852
self.assertEqual('foo', entry[1][0][1])
853
packed_stat = dirstate.pack_stat(statvalue)
854
self.assertEqual(packed_stat, entry[1][0][4])
856
def test_observed_sha1_not_cachable(self):
857
state, entry = self.get_state_with_a()
858
oldval = entry[1][0][1]
859
oldstat = entry[1][0][4]
860
self.build_tree(['a'])
861
statvalue = os.lstat('a')
862
state._observed_sha1(entry, "foo", statvalue)
863
self.assertEqual(oldval, entry[1][0][1])
864
self.assertEqual(oldstat, entry[1][0][4])
866
def test_update_entry(self):
867
state, _ = self.get_state_with_a()
868
tree = self.make_branch_and_tree('tree')
870
empty_revid = tree.commit('empty')
871
self.build_tree(['tree/a'])
872
tree.add(['a'], ['a-id'])
873
with_a_id = tree.commit('with_a')
874
self.addCleanup(tree.unlock)
875
state.set_parent_trees(
876
[(empty_revid, tree.branch.repository.revision_tree(empty_revid))],
878
entry = state._get_entry(0, path_utf8='a')
879
self.build_tree(['a'])
880
# Add one where we don't provide the stat or sha already
881
self.assertEqual(('', 'a', 'a-id'), entry[0])
882
self.assertEqual(('f', '', 0, False, dirstate.DirState.NULLSTAT),
884
# Flush the buffers to disk
886
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
887
state._dirblock_state)
889
stat_value = os.lstat('a')
890
packed_stat = dirstate.pack_stat(stat_value)
891
link_or_sha1 = self.update_entry(state, entry, abspath='a',
892
stat_value=stat_value)
893
self.assertEqual(None, link_or_sha1)
895
# The dirblock entry should not have cached the file's sha1 (too new)
896
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
898
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
899
state._dirblock_state)
900
mode = stat_value.st_mode
901
self.assertEqual([('is_exec', mode, False)], state._log)
904
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
905
state._dirblock_state)
907
# If we do it again right away, we don't know if the file has changed
908
# so we will re-read the file. Roll the clock back so the file is
909
# guaranteed to look too new.
910
state.adjust_time(-10)
913
link_or_sha1 = self.update_entry(state, entry, abspath='a',
914
stat_value=stat_value)
915
self.assertEqual([('is_exec', mode, False)], state._log)
916
self.assertEqual(None, link_or_sha1)
917
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
918
state._dirblock_state)
919
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
923
# If it is cachable (the clock has moved forward) but new it still
924
# won't calculate the sha or cache it.
925
state.adjust_time(+20)
927
link_or_sha1 = dirstate.update_entry(state, entry, abspath='a',
928
stat_value=stat_value)
929
self.assertEqual(None, link_or_sha1)
930
self.assertEqual([('is_exec', mode, False)], state._log)
931
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
934
# If the file is no longer new, and the clock has been moved forward
935
# sufficiently, it will cache the sha.
937
state.set_parent_trees(
938
[(with_a_id, tree.branch.repository.revision_tree(with_a_id))],
940
entry = state._get_entry(0, path_utf8='a')
942
link_or_sha1 = self.update_entry(state, entry, abspath='a',
943
stat_value=stat_value)
944
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
946
self.assertEqual([('is_exec', mode, False), ('sha1', 'a')],
948
self.assertEqual(('f', link_or_sha1, 14, False, packed_stat),
951
# Subsequent calls will just return the cached value
953
link_or_sha1 = self.update_entry(state, entry, abspath='a',
954
stat_value=stat_value)
955
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
957
self.assertEqual([], state._log)
958
self.assertEqual(('f', link_or_sha1, 14, False, packed_stat),
961
def test_update_entry_symlink(self):
962
"""Update entry should read symlinks."""
963
self.requireFeature(tests.SymlinkFeature)
964
state, entry = self.get_state_with_a()
966
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
967
state._dirblock_state)
968
os.symlink('target', 'a')
970
state.adjust_time(-10) # Make the symlink look new
971
stat_value = os.lstat('a')
972
packed_stat = dirstate.pack_stat(stat_value)
973
link_or_sha1 = self.update_entry(state, entry, abspath='a',
974
stat_value=stat_value)
975
self.assertEqual('target', link_or_sha1)
976
self.assertEqual([('read_link', 'a', '')], state._log)
977
# Dirblock is not updated (the link is too new)
978
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
980
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
981
state._dirblock_state)
983
# Because the stat_value looks new, we should re-read the target
984
link_or_sha1 = self.update_entry(state, entry, abspath='a',
985
stat_value=stat_value)
986
self.assertEqual('target', link_or_sha1)
987
self.assertEqual([('read_link', 'a', ''),
988
('read_link', 'a', ''),
990
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
992
state.adjust_time(+20) # Skip into the future, all files look old
993
link_or_sha1 = self.update_entry(state, entry, abspath='a',
994
stat_value=stat_value)
995
self.assertEqual('target', link_or_sha1)
996
# We need to re-read the link because only now can we cache it
997
self.assertEqual([('read_link', 'a', ''),
998
('read_link', 'a', ''),
999
('read_link', 'a', ''),
1001
self.assertEqual([('l', 'target', 6, False, packed_stat)],
1004
# Another call won't re-read the link
1005
self.assertEqual([('read_link', 'a', ''),
1006
('read_link', 'a', ''),
1007
('read_link', 'a', ''),
1009
link_or_sha1 = self.update_entry(state, entry, abspath='a',
1010
stat_value=stat_value)
1011
self.assertEqual('target', link_or_sha1)
1012
self.assertEqual([('l', 'target', 6, False, packed_stat)],
1015
def do_update_entry(self, state, entry, abspath):
1016
stat_value = os.lstat(abspath)
1017
return self.update_entry(state, entry, abspath, stat_value)
1019
def test_update_entry_dir(self):
1020
state, entry = self.get_state_with_a()
1021
self.build_tree(['a/'])
1022
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1024
def test_update_entry_dir_unchanged(self):
1025
state, entry = self.get_state_with_a()
1026
self.build_tree(['a/'])
1027
state.adjust_time(+20)
1028
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1029
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1030
state._dirblock_state)
1032
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1033
state._dirblock_state)
1034
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1035
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1036
state._dirblock_state)
1038
def test_update_entry_file_unchanged(self):
1039
state, _ = self.get_state_with_a()
1040
tree = self.make_branch_and_tree('tree')
1042
self.build_tree(['tree/a'])
1043
tree.add(['a'], ['a-id'])
1044
with_a_id = tree.commit('witha')
1045
self.addCleanup(tree.unlock)
1046
state.set_parent_trees(
1047
[(with_a_id, tree.branch.repository.revision_tree(with_a_id))],
1049
entry = state._get_entry(0, path_utf8='a')
1050
self.build_tree(['a'])
1051
sha1sum = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1052
state.adjust_time(+20)
1053
self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1054
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1055
state._dirblock_state)
1057
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1058
state._dirblock_state)
1059
self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1060
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1061
state._dirblock_state)
1063
def test_update_entry_tree_reference(self):
1064
state = test_dirstate.InstrumentedDirState.initialize('dirstate')
1065
self.addCleanup(state.unlock)
1066
state.add('r', 'r-id', 'tree-reference', None, '')
1067
self.build_tree(['r/'])
1068
entry = state._get_entry(0, path_utf8='r')
1069
self.do_update_entry(state, entry, 'r')
1070
entry = state._get_entry(0, path_utf8='r')
1071
self.assertEqual('t', entry[1][0][0])
1073
def create_and_test_file(self, state, entry):
1074
"""Create a file at 'a' and verify the state finds it during update.
1076
The state should already be versioning *something* at 'a'. This makes
1077
sure that state.update_entry recognizes it as a file.
1079
self.build_tree(['a'])
1080
stat_value = os.lstat('a')
1081
packed_stat = dirstate.pack_stat(stat_value)
1083
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1084
self.assertEqual(None, link_or_sha1)
1085
self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
1089
def create_and_test_dir(self, state, entry):
1090
"""Create a directory at 'a' and verify the state finds it.
1092
The state should already be versioning *something* at 'a'. This makes
1093
sure that state.update_entry recognizes it as a directory.
1095
self.build_tree(['a/'])
1096
stat_value = os.lstat('a')
1097
packed_stat = dirstate.pack_stat(stat_value)
1099
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1100
self.assertIs(None, link_or_sha1)
1101
self.assertEqual([('d', '', 0, False, packed_stat)], entry[1])
1105
# FIXME: Add unicode version
1106
def create_and_test_symlink(self, state, entry):
1107
"""Create a symlink at 'a' and verify the state finds it.
1109
The state should already be versioning *something* at 'a'. This makes
1110
sure that state.update_entry recognizes it as a symlink.
1112
This should not be called if this platform does not have symlink
1115
# caller should care about skipping test on platforms without symlinks
1116
os.symlink('path/to/foo', 'a')
1118
stat_value = os.lstat('a')
1119
packed_stat = dirstate.pack_stat(stat_value)
1121
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1122
self.assertEqual('path/to/foo', link_or_sha1)
1123
self.assertEqual([('l', 'path/to/foo', 11, False, packed_stat)],
1127
def test_update_file_to_dir(self):
1128
"""If a file changes to a directory we return None for the sha.
1129
We also update the inventory record.
1131
state, entry = self.get_state_with_a()
1132
# The file sha1 won't be cached unless the file is old
1133
state.adjust_time(+10)
1134
self.create_and_test_file(state, entry)
1136
self.create_and_test_dir(state, entry)
1138
def test_update_file_to_symlink(self):
1139
"""File becomes a symlink"""
1140
self.requireFeature(tests.SymlinkFeature)
1141
state, entry = self.get_state_with_a()
1142
# The file sha1 won't be cached unless the file is old
1143
state.adjust_time(+10)
1144
self.create_and_test_file(state, entry)
1146
self.create_and_test_symlink(state, entry)
1148
def test_update_dir_to_file(self):
1149
"""Directory becoming a file updates the entry."""
1150
state, entry = self.get_state_with_a()
1151
# The file sha1 won't be cached unless the file is old
1152
state.adjust_time(+10)
1153
self.create_and_test_dir(state, entry)
1155
self.create_and_test_file(state, entry)
1157
def test_update_dir_to_symlink(self):
1158
"""Directory becomes a symlink"""
1159
self.requireFeature(tests.SymlinkFeature)
1160
state, entry = self.get_state_with_a()
1161
# The symlink target won't be cached if it isn't old
1162
state.adjust_time(+10)
1163
self.create_and_test_dir(state, entry)
1165
self.create_and_test_symlink(state, entry)
1167
def test_update_symlink_to_file(self):
1168
"""Symlink becomes a file"""
1169
self.requireFeature(tests.SymlinkFeature)
1170
state, entry = self.get_state_with_a()
1171
# The symlink and file info won't be cached unless old
1172
state.adjust_time(+10)
1173
self.create_and_test_symlink(state, entry)
1175
self.create_and_test_file(state, entry)
1177
def test_update_symlink_to_dir(self):
1178
"""Symlink becomes a directory"""
1179
self.requireFeature(tests.SymlinkFeature)
1180
state, entry = self.get_state_with_a()
1181
# The symlink target won't be cached if it isn't old
1182
state.adjust_time(+10)
1183
self.create_and_test_symlink(state, entry)
1185
self.create_and_test_dir(state, entry)
1187
def test__is_executable_win32(self):
1188
state, entry = self.get_state_with_a()
1189
self.build_tree(['a'])
1191
# Make sure we are using the win32 implementation of _is_executable
1192
state._is_executable = state._is_executable_win32
1194
# The file on disk is not executable, but we are marking it as though
1195
# it is. With _is_executable_win32 we ignore what is on disk.
1196
entry[1][0] = ('f', '', 0, True, dirstate.DirState.NULLSTAT)
1198
stat_value = os.lstat('a')
1199
packed_stat = dirstate.pack_stat(stat_value)
1201
state.adjust_time(-10) # Make sure everything is new
1202
self.update_entry(state, entry, abspath='a', stat_value=stat_value)
1204
# The row is updated, but the executable bit stays set.
1205
self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1208
# Make the disk object look old enough to cache (but it won't cache the
1209
# sha as it is a new file).
1210
state.adjust_time(+20)
1211
digest = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1212
self.update_entry(state, entry, abspath='a', stat_value=stat_value)
1213
self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1216
def _prepare_tree(self):
1218
text = 'Hello World\n'
1219
tree = self.make_branch_and_tree('tree')
1220
self.build_tree_contents([('tree/a file', text)])
1221
tree.add('a file', 'a-file-id')
1222
# Note: dirstate does not sha prior to the first commit
1223
# so commit now in order for the test to work
1224
tree.commit('first')
1227
def test_sha1provider_sha1_used(self):
1228
tree, text = self._prepare_tree()
1229
state = dirstate.DirState.from_tree(tree, 'dirstate',
1230
UppercaseSHA1Provider())
1231
self.addCleanup(state.unlock)
1232
expected_sha = osutils.sha_string(text.upper() + "foo")
1233
entry = state._get_entry(0, path_utf8='a file')
1234
state._sha_cutoff_time()
1235
state._cutoff_time += 10
1236
sha1 = self.update_entry(state, entry, 'tree/a file',
1237
os.lstat('tree/a file'))
1238
self.assertEqual(expected_sha, sha1)
1240
def test_sha1provider_stat_and_sha1_used(self):
1241
tree, text = self._prepare_tree()
1243
self.addCleanup(tree.unlock)
1244
state = tree._current_dirstate()
1245
state._sha1_provider = UppercaseSHA1Provider()
1246
# If we used the standard provider, it would look like nothing has
1248
file_ids_changed = [change[0] for change
1249
in tree.iter_changes(tree.basis_tree())]
1250
self.assertEqual(['a-file-id'], file_ids_changed)
1253
class UppercaseSHA1Provider(dirstate.SHA1Provider):
1254
"""A custom SHA1Provider."""
1256
def sha1(self, abspath):
1257
return self.stat_and_sha1(abspath)[1]
1259
def stat_and_sha1(self, abspath):
1260
file_obj = file(abspath, 'rb')
1262
statvalue = os.fstat(file_obj.fileno())
1263
text = ''.join(file_obj.readlines())
1264
sha1 = osutils.sha_string(text.upper() + "foo")
1267
return statvalue, sha1
1270
class TestProcessEntry(test_dirstate.TestCaseWithDirState):
1273
_process_entry = None
1276
super(TestProcessEntry, self).setUp()
1277
self.overrideAttr(dirstate, '_process_entry', self._process_entry)
1279
def assertChangedFileIds(self, expected, tree):
1282
file_ids = [info[0] for info
1283
in tree.iter_changes(tree.basis_tree())]
1286
self.assertEqual(sorted(expected), sorted(file_ids))
1288
def test_exceptions_raised(self):
1289
# This is a direct test of bug #495023, it relies on osutils.is_inside
1290
# getting called in an inner function. Which makes it a bit brittle,
1291
# but at least it does reproduce the bug.
1292
tree = self.make_branch_and_tree('tree')
1293
self.build_tree(['tree/file', 'tree/dir/', 'tree/dir/sub',
1294
'tree/dir2/', 'tree/dir2/sub2'])
1295
tree.add(['file', 'dir', 'dir/sub', 'dir2', 'dir2/sub2'])
1296
tree.commit('first commit')
1298
self.addCleanup(tree.unlock)
1299
basis_tree = tree.basis_tree()
1300
def is_inside_raises(*args, **kwargs):
1301
raise RuntimeError('stop this')
1302
self.overrideAttr(osutils, 'is_inside', is_inside_raises)
1303
self.assertListRaises(RuntimeError, tree.iter_changes, basis_tree)
1305
def test_simple_changes(self):
1306
tree = self.make_branch_and_tree('tree')
1307
self.build_tree(['tree/file'])
1308
tree.add(['file'], ['file-id'])
1309
self.assertChangedFileIds([tree.get_root_id(), 'file-id'], tree)
1311
self.assertChangedFileIds([], tree)
1313
def test_sha1provider_stat_and_sha1_used(self):
1314
tree = self.make_branch_and_tree('tree')
1315
self.build_tree(['tree/file'])
1316
tree.add(['file'], ['file-id'])
1319
self.addCleanup(tree.unlock)
1320
state = tree._current_dirstate()
1321
state._sha1_provider = UppercaseSHA1Provider()
1322
self.assertChangedFileIds(['file-id'], tree)