1
# Copyright (C) 2007, 2008 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_c
36
has_dirstate_helpers_c = True
38
has_dirstate_helpers_c = False
41
class _CompiledDirstateHelpersFeature(tests.Feature):
43
return has_dirstate_helpers_c
45
def feature_name(self):
46
return 'bzrlib._dirstate_helpers_c'
47
CompiledDirstateHelpersFeature = _CompiledDirstateHelpersFeature()
50
def load_tests(basic_tests, module, loader):
51
# FIXME: we should also parametrize against SHA1Provider !
52
suite = loader.suiteClass()
53
remaining_tests = basic_tests
55
dir_reader_scenarios = test_osutils.dir_reader_scenarios()
57
ue_scenarios = [('dirstate_Python',
58
{'update_entry': dirstate.py_update_entry})]
59
if has_dirstate_helpers_c:
60
c_scenario = ('dirstate_C',
61
{'update_entry': _dirstate_helpers_c.update_entry})
62
ue_scenarios.append(c_scenario)
63
process_entry_tests, remaining_tests = tests.split_suite_by_condition(
64
remaining_tests, tests.condition_isinstance(TestUpdateEntry))
65
tests.multiply_tests(process_entry_tests,
66
tests.multiply_scenarios(dir_reader_scenarios,
70
pe_scenarios = [('dirstate_Python',
71
{'_process_entry': dirstate.ProcessEntryPython})]
72
if has_dirstate_helpers_c:
73
c_scenario = ('dirstate_C',
74
{'_process_entry': _dirstate_helpers_c.ProcessEntryC})
75
pe_scenarios.append(c_scenario)
76
process_entry_tests, remaining_tests = tests.split_suite_by_condition(
77
remaining_tests, tests.condition_isinstance(TestProcessEntry))
78
tests.multiply_tests(process_entry_tests,
79
tests.multiply_scenarios(dir_reader_scenarios,
83
dir_reader_tests, remaining_tests = tests.split_suite_by_condition(
84
remaining_tests, tests.condition_isinstance(
85
test_dirstate.TestCaseWithDirState))
86
tests.multiply_tests(dir_reader_tests, dir_reader_scenarios, suite)
87
suite.addTest(remaining_tests)
92
class TestBisectPathMixin(object):
93
"""Test that _bisect_path_*() returns the expected values.
95
_bisect_path_* is intended to work like bisect.bisect_*() except it
96
knows it is working on paths that are sorted by ('path', 'to', 'foo')
97
chunks rather than by raw 'path/to/foo'.
99
Test Cases should inherit from this and override ``get_bisect_path`` return
100
their implementation, and ``get_bisect`` to return the matching
101
bisect.bisect_* function.
104
def get_bisect_path(self):
105
"""Return an implementation of _bisect_path_*"""
106
raise NotImplementedError
108
def get_bisect(self):
109
"""Return a version of bisect.bisect_*.
111
Also, for the 'exists' check, return the offset to the real values.
112
For example bisect_left returns the index of an entry, while
113
bisect_right returns the index *after* an entry
115
:return: (bisect_func, offset)
117
raise NotImplementedError
119
def assertBisect(self, paths, split_paths, path, exists=True):
120
"""Assert that bisect_split works like bisect_left on the split paths.
122
:param paths: A list of path names
123
:param split_paths: A list of path names that are already split up by directory
124
('path/to/foo' => ('path', 'to', 'foo'))
125
:param path: The path we are indexing.
126
:param exists: The path should be present, so make sure the
127
final location actually points to the right value.
129
All other arguments will be passed along.
131
bisect_path = self.get_bisect_path()
132
self.assertIsInstance(paths, list)
133
bisect_path_idx = bisect_path(paths, path)
134
split_path = self.split_for_dirblocks([path])[0]
135
bisect_func, offset = self.get_bisect()
136
bisect_split_idx = bisect_func(split_paths, split_path)
137
self.assertEqual(bisect_split_idx, bisect_path_idx,
138
'%s disagreed. %s != %s'
140
% (bisect_path.__name__,
141
bisect_split_idx, bisect_path_idx, path)
144
self.assertEqual(path, paths[bisect_path_idx+offset])
146
def split_for_dirblocks(self, paths):
149
dirname, basename = os.path.split(path)
150
dir_split_paths.append((dirname.split('/'), basename))
151
dir_split_paths.sort()
152
return dir_split_paths
154
def test_simple(self):
155
"""In the simple case it works just like bisect_left"""
156
paths = ['', 'a', 'b', 'c', 'd']
157
split_paths = self.split_for_dirblocks(paths)
159
self.assertBisect(paths, split_paths, path, exists=True)
160
self.assertBisect(paths, split_paths, '_', exists=False)
161
self.assertBisect(paths, split_paths, 'aa', exists=False)
162
self.assertBisect(paths, split_paths, 'bb', exists=False)
163
self.assertBisect(paths, split_paths, 'cc', exists=False)
164
self.assertBisect(paths, split_paths, 'dd', exists=False)
165
self.assertBisect(paths, split_paths, 'a/a', exists=False)
166
self.assertBisect(paths, split_paths, 'b/b', exists=False)
167
self.assertBisect(paths, split_paths, 'c/c', exists=False)
168
self.assertBisect(paths, split_paths, 'd/d', exists=False)
170
def test_involved(self):
171
"""This is where bisect_path_* diverges slightly."""
172
# This is the list of paths and their contents
200
# This is the exact order that is stored by dirstate
201
# All children in a directory are mentioned before an children of
202
# children are mentioned.
203
# So all the root-directory paths, then all the
204
# first sub directory, etc.
205
paths = [# content of '/'
206
'', 'a', 'a-a', 'a-z', 'a=a', 'a=z',
208
'a/a', 'a/a-a', 'a/a-z',
210
'a/z', 'a/z-a', 'a/z-z',
233
split_paths = self.split_for_dirblocks(paths)
235
for dir_parts, basename in split_paths:
236
if dir_parts == ['']:
237
sorted_paths.append(basename)
239
sorted_paths.append('/'.join(dir_parts + [basename]))
241
self.assertEqual(sorted_paths, paths)
244
self.assertBisect(paths, split_paths, path, exists=True)
247
class TestBisectPathLeft(tests.TestCase, TestBisectPathMixin):
248
"""Run all Bisect Path tests against _bisect_path_left_py."""
250
def get_bisect_path(self):
251
from bzrlib._dirstate_helpers_py import _bisect_path_left_py
252
return _bisect_path_left_py
254
def get_bisect(self):
255
return bisect.bisect_left, 0
258
class TestCompiledBisectPathLeft(TestBisectPathLeft):
259
"""Run all Bisect Path tests against _bisect_path_right_c"""
261
_test_needs_features = [CompiledDirstateHelpersFeature]
263
def get_bisect_path(self):
264
from bzrlib._dirstate_helpers_c import _bisect_path_left_c
265
return _bisect_path_left_c
268
class TestBisectPathRight(tests.TestCase, TestBisectPathMixin):
269
"""Run all Bisect Path tests against _bisect_path_right_py"""
271
def get_bisect_path(self):
272
from bzrlib._dirstate_helpers_py import _bisect_path_right_py
273
return _bisect_path_right_py
275
def get_bisect(self):
276
return bisect.bisect_right, -1
279
class TestCompiledBisectPathRight(TestBisectPathRight):
280
"""Run all Bisect Path tests against _bisect_path_right_c"""
282
_test_needs_features = [CompiledDirstateHelpersFeature]
284
def get_bisect_path(self):
285
from bzrlib._dirstate_helpers_c import _bisect_path_right_c
286
return _bisect_path_right_c
289
class TestBisectDirblock(tests.TestCase):
290
"""Test that bisect_dirblock() returns the expected values.
292
bisect_dirblock is intended to work like bisect.bisect_left() except it
293
knows it is working on dirblocks and that dirblocks are sorted by ('path',
294
'to', 'foo') chunks rather than by raw 'path/to/foo'.
296
This test is parameterized by calling get_bisect_dirblock(). Child test
297
cases can override this function to test against a different
301
def get_bisect_dirblock(self):
302
"""Return an implementation of bisect_dirblock"""
303
from bzrlib._dirstate_helpers_py import bisect_dirblock_py
304
return bisect_dirblock_py
306
def assertBisect(self, dirblocks, split_dirblocks, path, *args, **kwargs):
307
"""Assert that bisect_split works like bisect_left on the split paths.
309
:param dirblocks: A list of (path, [info]) pairs.
310
:param split_dirblocks: A list of ((split, path), [info]) pairs.
311
:param path: The path we are indexing.
313
All other arguments will be passed along.
315
bisect_dirblock = self.get_bisect_dirblock()
316
self.assertIsInstance(dirblocks, list)
317
bisect_split_idx = bisect_dirblock(dirblocks, path, *args, **kwargs)
318
split_dirblock = (path.split('/'), [])
319
bisect_left_idx = bisect.bisect_left(split_dirblocks, split_dirblock,
321
self.assertEqual(bisect_left_idx, bisect_split_idx,
322
'bisect_split disagreed. %s != %s'
324
% (bisect_left_idx, bisect_split_idx, path)
327
def paths_to_dirblocks(self, paths):
328
"""Convert a list of paths into dirblock form.
330
Also, ensure that the paths are in proper sorted order.
332
dirblocks = [(path, []) for path in paths]
333
split_dirblocks = [(path.split('/'), []) for path in paths]
334
self.assertEqual(sorted(split_dirblocks), split_dirblocks)
335
return dirblocks, split_dirblocks
337
def test_simple(self):
338
"""In the simple case it works just like bisect_left"""
339
paths = ['', 'a', 'b', 'c', 'd']
340
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
342
self.assertBisect(dirblocks, split_dirblocks, path)
343
self.assertBisect(dirblocks, split_dirblocks, '_')
344
self.assertBisect(dirblocks, split_dirblocks, 'aa')
345
self.assertBisect(dirblocks, split_dirblocks, 'bb')
346
self.assertBisect(dirblocks, split_dirblocks, 'cc')
347
self.assertBisect(dirblocks, split_dirblocks, 'dd')
348
self.assertBisect(dirblocks, split_dirblocks, 'a/a')
349
self.assertBisect(dirblocks, split_dirblocks, 'b/b')
350
self.assertBisect(dirblocks, split_dirblocks, 'c/c')
351
self.assertBisect(dirblocks, split_dirblocks, 'd/d')
353
def test_involved(self):
354
"""This is where bisect_left diverges slightly."""
356
'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
357
'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
359
'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
360
'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
363
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
365
self.assertBisect(dirblocks, split_dirblocks, path)
367
def test_involved_cached(self):
368
"""This is where bisect_left diverges slightly."""
370
'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
371
'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
373
'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
374
'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
378
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
380
self.assertBisect(dirblocks, split_dirblocks, path, cache=cache)
383
class TestCompiledBisectDirblock(TestBisectDirblock):
384
"""Test that bisect_dirblock() returns the expected values.
386
bisect_dirblock is intended to work like bisect.bisect_left() except it
387
knows it is working on dirblocks and that dirblocks are sorted by ('path',
388
'to', 'foo') chunks rather than by raw 'path/to/foo'.
390
This runs all the normal tests that TestBisectDirblock did, but uses the
394
_test_needs_features = [CompiledDirstateHelpersFeature]
396
def get_bisect_dirblock(self):
397
from bzrlib._dirstate_helpers_c import bisect_dirblock_c
398
return bisect_dirblock_c
401
class TestCmpByDirs(tests.TestCase):
402
"""Test an implementation of cmp_by_dirs()
404
cmp_by_dirs() compares 2 paths by their directory sections, rather than as
407
Child test cases can override ``get_cmp_by_dirs`` to test a specific
411
def get_cmp_by_dirs(self):
412
"""Get a specific implementation of cmp_by_dirs."""
413
from bzrlib._dirstate_helpers_py import cmp_by_dirs_py
414
return cmp_by_dirs_py
416
def assertCmpByDirs(self, expected, str1, str2):
417
"""Compare the two strings, in both directions.
419
:param expected: The expected comparison value. -1 means str1 comes
420
first, 0 means they are equal, 1 means str2 comes first
421
:param str1: string to compare
422
:param str2: string to compare
424
cmp_by_dirs = self.get_cmp_by_dirs()
426
self.assertEqual(str1, str2)
427
self.assertEqual(0, cmp_by_dirs(str1, str2))
428
self.assertEqual(0, cmp_by_dirs(str2, str1))
430
self.assertPositive(cmp_by_dirs(str1, str2))
431
self.assertNegative(cmp_by_dirs(str2, str1))
433
self.assertNegative(cmp_by_dirs(str1, str2))
434
self.assertPositive(cmp_by_dirs(str2, str1))
436
def test_cmp_empty(self):
437
"""Compare against the empty string."""
438
self.assertCmpByDirs(0, '', '')
439
self.assertCmpByDirs(1, 'a', '')
440
self.assertCmpByDirs(1, 'ab', '')
441
self.assertCmpByDirs(1, 'abc', '')
442
self.assertCmpByDirs(1, 'abcd', '')
443
self.assertCmpByDirs(1, 'abcde', '')
444
self.assertCmpByDirs(1, 'abcdef', '')
445
self.assertCmpByDirs(1, 'abcdefg', '')
446
self.assertCmpByDirs(1, 'abcdefgh', '')
447
self.assertCmpByDirs(1, 'abcdefghi', '')
448
self.assertCmpByDirs(1, 'test/ing/a/path/', '')
450
def test_cmp_same_str(self):
451
"""Compare the same string"""
452
self.assertCmpByDirs(0, 'a', 'a')
453
self.assertCmpByDirs(0, 'ab', 'ab')
454
self.assertCmpByDirs(0, 'abc', 'abc')
455
self.assertCmpByDirs(0, 'abcd', 'abcd')
456
self.assertCmpByDirs(0, 'abcde', 'abcde')
457
self.assertCmpByDirs(0, 'abcdef', 'abcdef')
458
self.assertCmpByDirs(0, 'abcdefg', 'abcdefg')
459
self.assertCmpByDirs(0, 'abcdefgh', 'abcdefgh')
460
self.assertCmpByDirs(0, 'abcdefghi', 'abcdefghi')
461
self.assertCmpByDirs(0, 'testing a long string', 'testing a long string')
462
self.assertCmpByDirs(0, 'x'*10000, 'x'*10000)
463
self.assertCmpByDirs(0, 'a/b', 'a/b')
464
self.assertCmpByDirs(0, 'a/b/c', 'a/b/c')
465
self.assertCmpByDirs(0, 'a/b/c/d', 'a/b/c/d')
466
self.assertCmpByDirs(0, 'a/b/c/d/e', 'a/b/c/d/e')
468
def test_simple_paths(self):
469
"""Compare strings that act like normal string comparison"""
470
self.assertCmpByDirs(-1, 'a', 'b')
471
self.assertCmpByDirs(-1, 'aa', 'ab')
472
self.assertCmpByDirs(-1, 'ab', 'bb')
473
self.assertCmpByDirs(-1, 'aaa', 'aab')
474
self.assertCmpByDirs(-1, 'aab', 'abb')
475
self.assertCmpByDirs(-1, 'abb', 'bbb')
476
self.assertCmpByDirs(-1, 'aaaa', 'aaab')
477
self.assertCmpByDirs(-1, 'aaab', 'aabb')
478
self.assertCmpByDirs(-1, 'aabb', 'abbb')
479
self.assertCmpByDirs(-1, 'abbb', 'bbbb')
480
self.assertCmpByDirs(-1, 'aaaaa', 'aaaab')
481
self.assertCmpByDirs(-1, 'a/a', 'a/b')
482
self.assertCmpByDirs(-1, 'a/b', 'b/b')
483
self.assertCmpByDirs(-1, 'a/a/a', 'a/a/b')
484
self.assertCmpByDirs(-1, 'a/a/b', 'a/b/b')
485
self.assertCmpByDirs(-1, 'a/b/b', 'b/b/b')
486
self.assertCmpByDirs(-1, 'a/a/a/a', 'a/a/a/b')
487
self.assertCmpByDirs(-1, 'a/a/a/b', 'a/a/b/b')
488
self.assertCmpByDirs(-1, 'a/a/b/b', 'a/b/b/b')
489
self.assertCmpByDirs(-1, 'a/b/b/b', 'b/b/b/b')
490
self.assertCmpByDirs(-1, 'a/a/a/a/a', 'a/a/a/a/b')
492
def test_tricky_paths(self):
493
self.assertCmpByDirs(1, 'ab/cd/ef', 'ab/cc/ef')
494
self.assertCmpByDirs(1, 'ab/cd/ef', 'ab/c/ef')
495
self.assertCmpByDirs(-1, 'ab/cd/ef', 'ab/cd-ef')
496
self.assertCmpByDirs(-1, 'ab/cd', 'ab/cd-')
497
self.assertCmpByDirs(-1, 'ab/cd', 'ab-cd')
499
def test_cmp_unicode_not_allowed(self):
500
cmp_by_dirs = self.get_cmp_by_dirs()
501
self.assertRaises(TypeError, cmp_by_dirs, u'Unicode', 'str')
502
self.assertRaises(TypeError, cmp_by_dirs, 'str', u'Unicode')
503
self.assertRaises(TypeError, cmp_by_dirs, u'Unicode', u'Unicode')
505
def test_cmp_non_ascii(self):
506
self.assertCmpByDirs(-1, '\xc2\xb5', '\xc3\xa5') # u'\xb5', u'\xe5'
507
self.assertCmpByDirs(-1, 'a', '\xc3\xa5') # u'a', u'\xe5'
508
self.assertCmpByDirs(-1, 'b', '\xc2\xb5') # u'b', u'\xb5'
509
self.assertCmpByDirs(-1, 'a/b', 'a/\xc3\xa5') # u'a/b', u'a/\xe5'
510
self.assertCmpByDirs(-1, 'b/a', 'b/\xc2\xb5') # u'b/a', u'b/\xb5'
513
class TestCompiledCmpByDirs(TestCmpByDirs):
514
"""Test the pyrex implementation of cmp_by_dirs"""
516
_test_needs_features = [CompiledDirstateHelpersFeature]
518
def get_cmp_by_dirs(self):
519
from bzrlib._dirstate_helpers_c import cmp_by_dirs_c
523
class TestCmpPathByDirblock(tests.TestCase):
524
"""Test an implementation of _cmp_path_by_dirblock()
526
_cmp_path_by_dirblock() compares two paths using the sort order used by
527
DirState. All paths in the same directory are sorted together.
529
Child test cases can override ``get_cmp_path_by_dirblock`` to test a specific
533
def get_cmp_path_by_dirblock(self):
534
"""Get a specific implementation of _cmp_path_by_dirblock."""
535
from bzrlib._dirstate_helpers_py import _cmp_path_by_dirblock_py
536
return _cmp_path_by_dirblock_py
538
def assertCmpPathByDirblock(self, paths):
539
"""Compare all paths and make sure they evaluate to the correct order.
541
This does N^2 comparisons. It is assumed that ``paths`` is properly
544
:param paths: a sorted list of paths to compare
546
# First, make sure the paths being passed in are correct
548
dirname, basename = os.path.split(p)
549
return dirname.split('/'), basename
550
self.assertEqual(sorted(paths, key=_key), paths)
552
cmp_path_by_dirblock = self.get_cmp_path_by_dirblock()
553
for idx1, path1 in enumerate(paths):
554
for idx2, path2 in enumerate(paths):
555
cmp_val = cmp_path_by_dirblock(path1, path2)
557
self.assertTrue(cmp_val < 0,
558
'%s did not state that %r came before %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 came after %r, cmp=%s'
564
% (cmp_path_by_dirblock.__name__,
565
path1, path2, cmp_val))
567
self.assertTrue(cmp_val == 0,
568
'%s did not state that %r == %r, cmp=%s'
569
% (cmp_path_by_dirblock.__name__,
570
path1, path2, cmp_val))
572
def test_cmp_simple_paths(self):
573
"""Compare against the empty string."""
574
self.assertCmpPathByDirblock(['', 'a', 'ab', 'abc', 'a/b/c', 'b/d/e'])
575
self.assertCmpPathByDirblock(['kl', 'ab/cd', 'ab/ef', 'gh/ij'])
577
def test_tricky_paths(self):
578
self.assertCmpPathByDirblock([
580
'', 'a', 'a-a', 'a=a', 'b',
582
'a/a', 'a/a-a', 'a/a=a', 'a/b',
584
'a/a/a', 'a/a/a-a', 'a/a/a=a',
585
# Contents of 'a/a/a'
586
'a/a/a/a', 'a/a/a/b',
587
# Contents of 'a/a/a-a',
588
'a/a/a-a/a', 'a/a/a-a/b',
589
# Contents of 'a/a/a=a',
590
'a/a/a=a/a', 'a/a/a=a/b',
591
# Contents of 'a/a-a'
593
# Contents of 'a/a-a/a'
594
'a/a-a/a/a', 'a/a-a/a/b',
595
# Contents of 'a/a=a'
606
self.assertCmpPathByDirblock([
608
'', 'a', 'a-a', 'a-z', 'a=a', 'a=z',
610
'a/a', 'a/a-a', 'a/a-z',
612
'a/z', 'a/z-a', 'a/z-z',
636
def test_unicode_not_allowed(self):
637
cmp_path_by_dirblock = self.get_cmp_path_by_dirblock()
638
self.assertRaises(TypeError, cmp_path_by_dirblock, u'Uni', 'str')
639
self.assertRaises(TypeError, cmp_path_by_dirblock, 'str', u'Uni')
640
self.assertRaises(TypeError, cmp_path_by_dirblock, u'Uni', u'Uni')
641
self.assertRaises(TypeError, cmp_path_by_dirblock, u'x/Uni', 'x/str')
642
self.assertRaises(TypeError, cmp_path_by_dirblock, 'x/str', u'x/Uni')
643
self.assertRaises(TypeError, cmp_path_by_dirblock, u'x/Uni', u'x/Uni')
645
def test_nonascii(self):
646
self.assertCmpPathByDirblock([
648
'', 'a', '\xc2\xb5', '\xc3\xa5',
650
'a/a', 'a/\xc2\xb5', 'a/\xc3\xa5',
652
'a/a/a', 'a/a/\xc2\xb5', 'a/a/\xc3\xa5',
653
# content of 'a/\xc2\xb5'
654
'a/\xc2\xb5/a', 'a/\xc2\xb5/\xc2\xb5', 'a/\xc2\xb5/\xc3\xa5',
655
# content of 'a/\xc3\xa5'
656
'a/\xc3\xa5/a', 'a/\xc3\xa5/\xc2\xb5', 'a/\xc3\xa5/\xc3\xa5',
657
# content of '\xc2\xb5'
658
'\xc2\xb5/a', '\xc2\xb5/\xc2\xb5', '\xc2\xb5/\xc3\xa5',
659
# content of '\xc2\xe5'
660
'\xc3\xa5/a', '\xc3\xa5/\xc2\xb5', '\xc3\xa5/\xc3\xa5',
664
class TestCompiledCmpPathByDirblock(TestCmpPathByDirblock):
665
"""Test the pyrex implementation of _cmp_path_by_dirblock"""
667
_test_needs_features = [CompiledDirstateHelpersFeature]
669
def get_cmp_by_dirs(self):
670
from bzrlib._dirstate_helpers_c import _cmp_path_by_dirblock_c
671
return _cmp_path_by_dirblock_c
674
class TestMemRChr(tests.TestCase):
675
"""Test memrchr functionality"""
677
_test_needs_features = [CompiledDirstateHelpersFeature]
679
def assertMemRChr(self, expected, s, c):
680
from bzrlib._dirstate_helpers_c import _py_memrchr
681
self.assertEqual(expected, _py_memrchr(s, c))
683
def test_missing(self):
684
self.assertMemRChr(None, '', 'a')
685
self.assertMemRChr(None, '', 'c')
686
self.assertMemRChr(None, 'abcdefghijklm', 'q')
687
self.assertMemRChr(None, 'aaaaaaaaaaaaaaaaaaaaaaa', 'b')
689
def test_single_entry(self):
690
self.assertMemRChr(0, 'abcdefghijklm', 'a')
691
self.assertMemRChr(1, 'abcdefghijklm', 'b')
692
self.assertMemRChr(2, 'abcdefghijklm', 'c')
693
self.assertMemRChr(10, 'abcdefghijklm', 'k')
694
self.assertMemRChr(11, 'abcdefghijklm', 'l')
695
self.assertMemRChr(12, 'abcdefghijklm', 'm')
697
def test_multiple(self):
698
self.assertMemRChr(10, 'abcdefjklmabcdefghijklm', 'a')
699
self.assertMemRChr(11, 'abcdefjklmabcdefghijklm', 'b')
700
self.assertMemRChr(12, 'abcdefjklmabcdefghijklm', 'c')
701
self.assertMemRChr(20, 'abcdefjklmabcdefghijklm', 'k')
702
self.assertMemRChr(21, 'abcdefjklmabcdefghijklm', 'l')
703
self.assertMemRChr(22, 'abcdefjklmabcdefghijklm', 'm')
704
self.assertMemRChr(22, 'aaaaaaaaaaaaaaaaaaaaaaa', 'a')
706
def test_with_nulls(self):
707
self.assertMemRChr(10, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'a')
708
self.assertMemRChr(11, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'b')
709
self.assertMemRChr(12, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'c')
710
self.assertMemRChr(20, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'k')
711
self.assertMemRChr(21, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'l')
712
self.assertMemRChr(22, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'm')
713
self.assertMemRChr(22, 'aaa\0\0\0aaaaaaa\0\0\0aaaaaaa', 'a')
714
self.assertMemRChr(9, '\0\0\0\0\0\0\0\0\0\0', '\0')
717
class TestReadDirblocks(test_dirstate.TestCaseWithDirState):
718
"""Test an implementation of _read_dirblocks()
720
_read_dirblocks() reads in all of the dirblock information from the disk
723
Child test cases can override ``get_read_dirblocks`` to test a specific
727
def get_read_dirblocks(self):
728
from bzrlib._dirstate_helpers_py import _read_dirblocks_py
729
return _read_dirblocks_py
731
def test_smoketest(self):
732
"""Make sure that we can create and read back a simple file."""
733
tree, state, expected = self.create_basic_dirstate()
735
state._read_header_if_needed()
736
self.assertEqual(dirstate.DirState.NOT_IN_MEMORY,
737
state._dirblock_state)
738
read_dirblocks = self.get_read_dirblocks()
739
read_dirblocks(state)
740
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
741
state._dirblock_state)
743
def test_trailing_garbage(self):
744
tree, state, expected = self.create_basic_dirstate()
745
# We can modify the file as long as it hasn't been read yet.
746
f = open('dirstate', 'ab')
748
# Add bogus trailing garbage
752
e = self.assertRaises(errors.DirstateCorrupt,
753
state._read_dirblocks_if_needed)
754
# Make sure we mention the bogus characters in the error
755
self.assertContainsRe(str(e), 'bogus')
758
class TestCompiledReadDirblocks(TestReadDirblocks):
759
"""Test the pyrex implementation of _read_dirblocks"""
761
_test_needs_features = [CompiledDirstateHelpersFeature]
763
def get_read_dirblocks(self):
764
from bzrlib._dirstate_helpers_c import _read_dirblocks_c
765
return _read_dirblocks_c
768
class TestUsingCompiledIfAvailable(tests.TestCase):
769
"""Check that any compiled functions that are available are the default.
771
It is possible to have typos, etc in the import line, such that
772
_dirstate_helpers_c is actually available, but the compiled functions are
776
def test_bisect_dirblock(self):
777
if CompiledDirstateHelpersFeature.available():
778
from bzrlib._dirstate_helpers_c import bisect_dirblock_c
779
self.assertIs(bisect_dirblock_c, dirstate.bisect_dirblock)
781
from bzrlib._dirstate_helpers_py import bisect_dirblock_py
782
self.assertIs(bisect_dirblock_py, dirstate.bisect_dirblock)
784
def test__bisect_path_left(self):
785
if CompiledDirstateHelpersFeature.available():
786
from bzrlib._dirstate_helpers_c import _bisect_path_left_c
787
self.assertIs(_bisect_path_left_c, dirstate._bisect_path_left)
789
from bzrlib._dirstate_helpers_py import _bisect_path_left_py
790
self.assertIs(_bisect_path_left_py, dirstate._bisect_path_left)
792
def test__bisect_path_right(self):
793
if CompiledDirstateHelpersFeature.available():
794
from bzrlib._dirstate_helpers_c import _bisect_path_right_c
795
self.assertIs(_bisect_path_right_c, dirstate._bisect_path_right)
797
from bzrlib._dirstate_helpers_py import _bisect_path_right_py
798
self.assertIs(_bisect_path_right_py, dirstate._bisect_path_right)
800
def test_cmp_by_dirs(self):
801
if CompiledDirstateHelpersFeature.available():
802
from bzrlib._dirstate_helpers_c import cmp_by_dirs_c
803
self.assertIs(cmp_by_dirs_c, dirstate.cmp_by_dirs)
805
from bzrlib._dirstate_helpers_py import cmp_by_dirs_py
806
self.assertIs(cmp_by_dirs_py, dirstate.cmp_by_dirs)
808
def test__read_dirblocks(self):
809
if CompiledDirstateHelpersFeature.available():
810
from bzrlib._dirstate_helpers_c import _read_dirblocks_c
811
self.assertIs(_read_dirblocks_c, dirstate._read_dirblocks)
813
from bzrlib._dirstate_helpers_py import _read_dirblocks_py
814
self.assertIs(_read_dirblocks_py, dirstate._read_dirblocks)
816
def test_update_entry(self):
817
if CompiledDirstateHelpersFeature.available():
818
from bzrlib._dirstate_helpers_c import update_entry
819
self.assertIs(update_entry, dirstate.update_entry)
821
from bzrlib.dirstate import py_update_entry
822
self.assertIs(py_update_entry, dirstate.py_update_entry)
824
def test_process_entry(self):
825
if CompiledDirstateHelpersFeature.available():
826
from bzrlib._dirstate_helpers_c import ProcessEntryC
827
self.assertIs(ProcessEntryC, dirstate._process_entry)
829
from bzrlib.dirstate import ProcessEntryPython
830
self.assertIs(ProcessEntryPython, dirstate._process_entry)
833
class TestUpdateEntry(test_dirstate.TestCaseWithDirState):
834
"""Test the DirState.update_entry functions"""
840
super(TestUpdateEntry, self).setUp()
841
orig = dirstate.update_entry
843
dirstate.update_entry = orig
844
self.addCleanup(cleanup)
845
dirstate.update_entry = self.update_entry
847
def get_state_with_a(self):
848
"""Create a DirState tracking a single object named 'a'"""
849
state = test_dirstate.InstrumentedDirState.initialize('dirstate')
850
self.addCleanup(state.unlock)
851
state.add('a', 'a-id', 'file', None, '')
852
entry = state._get_entry(0, path_utf8='a')
855
def test_observed_sha1_cachable(self):
856
state, entry = self.get_state_with_a()
857
atime = time.time() - 10
858
self.build_tree(['a'])
859
statvalue = os.lstat('a')
860
statvalue = test_dirstate._FakeStat(statvalue.st_size, atime, atime,
861
statvalue.st_dev, statvalue.st_ino, statvalue.st_mode)
862
state._observed_sha1(entry, "foo", statvalue)
863
self.assertEqual('foo', entry[1][0][1])
864
packed_stat = dirstate.pack_stat(statvalue)
865
self.assertEqual(packed_stat, entry[1][0][4])
867
def test_observed_sha1_not_cachable(self):
868
state, entry = self.get_state_with_a()
869
oldval = entry[1][0][1]
870
oldstat = entry[1][0][4]
871
self.build_tree(['a'])
872
statvalue = os.lstat('a')
873
state._observed_sha1(entry, "foo", statvalue)
874
self.assertEqual(oldval, entry[1][0][1])
875
self.assertEqual(oldstat, entry[1][0][4])
877
def test_update_entry(self):
878
state, _ = self.get_state_with_a()
879
tree = self.make_branch_and_tree('tree')
881
empty_revid = tree.commit('empty')
882
self.build_tree(['tree/a'])
883
tree.add(['a'], ['a-id'])
884
with_a_id = tree.commit('with_a')
885
self.addCleanup(tree.unlock)
886
state.set_parent_trees(
887
[(empty_revid, tree.branch.repository.revision_tree(empty_revid))],
889
entry = state._get_entry(0, path_utf8='a')
890
self.build_tree(['a'])
891
# Add one where we don't provide the stat or sha already
892
self.assertEqual(('', 'a', 'a-id'), entry[0])
893
self.assertEqual(('f', '', 0, False, dirstate.DirState.NULLSTAT),
895
# Flush the buffers to disk
897
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
898
state._dirblock_state)
900
stat_value = os.lstat('a')
901
packed_stat = dirstate.pack_stat(stat_value)
902
link_or_sha1 = self.update_entry(state, entry, abspath='a',
903
stat_value=stat_value)
904
self.assertEqual(None, link_or_sha1)
906
# The dirblock entry should not have cached the file's sha1 (too new)
907
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
909
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
910
state._dirblock_state)
911
mode = stat_value.st_mode
912
self.assertEqual([('is_exec', mode, False)], state._log)
915
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
916
state._dirblock_state)
918
# If we do it again right away, we don't know if the file has changed
919
# so we will re-read the file. Roll the clock back so the file is
920
# guaranteed to look too new.
921
state.adjust_time(-10)
924
link_or_sha1 = self.update_entry(state, entry, abspath='a',
925
stat_value=stat_value)
926
self.assertEqual([('is_exec', mode, False)], state._log)
927
self.assertEqual(None, link_or_sha1)
928
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
929
state._dirblock_state)
930
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
934
# If it is cachable (the clock has moved forward) but new it still
935
# won't calculate the sha or cache it.
936
state.adjust_time(+20)
938
link_or_sha1 = dirstate.update_entry(state, entry, abspath='a',
939
stat_value=stat_value)
940
self.assertEqual(None, link_or_sha1)
941
self.assertEqual([('is_exec', mode, False)], state._log)
942
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
945
# If the file is no longer new, and the clock has been moved forward
946
# sufficiently, it will cache the sha.
948
state.set_parent_trees(
949
[(with_a_id, tree.branch.repository.revision_tree(with_a_id))],
951
entry = state._get_entry(0, path_utf8='a')
953
link_or_sha1 = self.update_entry(state, entry, abspath='a',
954
stat_value=stat_value)
955
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
957
self.assertEqual([('is_exec', mode, False), ('sha1', 'a')],
959
self.assertEqual(('f', link_or_sha1, 14, False, packed_stat),
962
# Subsequent calls will just return the cached value
964
link_or_sha1 = self.update_entry(state, entry, abspath='a',
965
stat_value=stat_value)
966
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
968
self.assertEqual([], state._log)
969
self.assertEqual(('f', link_or_sha1, 14, False, packed_stat),
972
def test_update_entry_symlink(self):
973
"""Update entry should read symlinks."""
974
self.requireFeature(tests.SymlinkFeature)
975
state, entry = self.get_state_with_a()
977
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
978
state._dirblock_state)
979
os.symlink('target', 'a')
981
state.adjust_time(-10) # Make the symlink look new
982
stat_value = os.lstat('a')
983
packed_stat = dirstate.pack_stat(stat_value)
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', '')], state._log)
988
# Dirblock is not updated (the link is too new)
989
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
991
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
992
state._dirblock_state)
994
# Because the stat_value looks new, we should re-read the target
995
link_or_sha1 = self.update_entry(state, entry, abspath='a',
996
stat_value=stat_value)
997
self.assertEqual('target', link_or_sha1)
998
self.assertEqual([('read_link', 'a', ''),
999
('read_link', 'a', ''),
1001
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
1003
state.adjust_time(+20) # Skip into the future, all files look old
1004
link_or_sha1 = self.update_entry(state, entry, abspath='a',
1005
stat_value=stat_value)
1006
self.assertEqual('target', link_or_sha1)
1007
# We need to re-read the link because only now can we cache it
1008
self.assertEqual([('read_link', 'a', ''),
1009
('read_link', 'a', ''),
1010
('read_link', 'a', ''),
1012
self.assertEqual([('l', 'target', 6, False, packed_stat)],
1015
# Another call won't re-read the link
1016
self.assertEqual([('read_link', 'a', ''),
1017
('read_link', 'a', ''),
1018
('read_link', 'a', ''),
1020
link_or_sha1 = self.update_entry(state, entry, abspath='a',
1021
stat_value=stat_value)
1022
self.assertEqual('target', link_or_sha1)
1023
self.assertEqual([('l', 'target', 6, False, packed_stat)],
1026
def do_update_entry(self, state, entry, abspath):
1027
stat_value = os.lstat(abspath)
1028
return self.update_entry(state, entry, abspath, stat_value)
1030
def test_update_entry_dir(self):
1031
state, entry = self.get_state_with_a()
1032
self.build_tree(['a/'])
1033
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1035
def test_update_entry_dir_unchanged(self):
1036
state, entry = self.get_state_with_a()
1037
self.build_tree(['a/'])
1038
state.adjust_time(+20)
1039
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1040
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1041
state._dirblock_state)
1043
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1044
state._dirblock_state)
1045
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1046
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1047
state._dirblock_state)
1049
def test_update_entry_file_unchanged(self):
1050
state, _ = self.get_state_with_a()
1051
tree = self.make_branch_and_tree('tree')
1053
self.build_tree(['tree/a'])
1054
tree.add(['a'], ['a-id'])
1055
with_a_id = tree.commit('witha')
1056
self.addCleanup(tree.unlock)
1057
state.set_parent_trees(
1058
[(with_a_id, tree.branch.repository.revision_tree(with_a_id))],
1060
entry = state._get_entry(0, path_utf8='a')
1061
self.build_tree(['a'])
1062
sha1sum = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1063
state.adjust_time(+20)
1064
self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1065
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1066
state._dirblock_state)
1068
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1069
state._dirblock_state)
1070
self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1071
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1072
state._dirblock_state)
1074
def test_update_entry_tree_reference(self):
1075
state = test_dirstate.InstrumentedDirState.initialize('dirstate')
1076
self.addCleanup(state.unlock)
1077
state.add('r', 'r-id', 'tree-reference', None, '')
1078
self.build_tree(['r/'])
1079
entry = state._get_entry(0, path_utf8='r')
1080
self.do_update_entry(state, entry, 'r')
1081
entry = state._get_entry(0, path_utf8='r')
1082
self.assertEqual('t', entry[1][0][0])
1084
def create_and_test_file(self, state, entry):
1085
"""Create a file at 'a' and verify the state finds it during update.
1087
The state should already be versioning *something* at 'a'. This makes
1088
sure that state.update_entry recognizes it as a file.
1090
self.build_tree(['a'])
1091
stat_value = os.lstat('a')
1092
packed_stat = dirstate.pack_stat(stat_value)
1094
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1095
self.assertEqual(None, link_or_sha1)
1096
self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
1100
def create_and_test_dir(self, state, entry):
1101
"""Create a directory at 'a' and verify the state finds it.
1103
The state should already be versioning *something* at 'a'. This makes
1104
sure that state.update_entry recognizes it as a directory.
1106
self.build_tree(['a/'])
1107
stat_value = os.lstat('a')
1108
packed_stat = dirstate.pack_stat(stat_value)
1110
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1111
self.assertIs(None, link_or_sha1)
1112
self.assertEqual([('d', '', 0, False, packed_stat)], entry[1])
1116
# FIXME: Add unicode version
1117
def create_and_test_symlink(self, state, entry):
1118
"""Create a symlink at 'a' and verify the state finds it.
1120
The state should already be versioning *something* at 'a'. This makes
1121
sure that state.update_entry recognizes it as a symlink.
1123
This should not be called if this platform does not have symlink
1126
# caller should care about skipping test on platforms without symlinks
1127
os.symlink('path/to/foo', 'a')
1129
stat_value = os.lstat('a')
1130
packed_stat = dirstate.pack_stat(stat_value)
1132
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1133
self.assertEqual('path/to/foo', link_or_sha1)
1134
self.assertEqual([('l', 'path/to/foo', 11, False, packed_stat)],
1138
def test_update_file_to_dir(self):
1139
"""If a file changes to a directory we return None for the sha.
1140
We also update the inventory record.
1142
state, entry = self.get_state_with_a()
1143
# The file sha1 won't be cached unless the file is old
1144
state.adjust_time(+10)
1145
self.create_and_test_file(state, entry)
1147
self.create_and_test_dir(state, entry)
1149
def test_update_file_to_symlink(self):
1150
"""File becomes a symlink"""
1151
self.requireFeature(tests.SymlinkFeature)
1152
state, entry = self.get_state_with_a()
1153
# The file sha1 won't be cached unless the file is old
1154
state.adjust_time(+10)
1155
self.create_and_test_file(state, entry)
1157
self.create_and_test_symlink(state, entry)
1159
def test_update_dir_to_file(self):
1160
"""Directory becoming a file updates the entry."""
1161
state, entry = self.get_state_with_a()
1162
# The file sha1 won't be cached unless the file is old
1163
state.adjust_time(+10)
1164
self.create_and_test_dir(state, entry)
1166
self.create_and_test_file(state, entry)
1168
def test_update_dir_to_symlink(self):
1169
"""Directory becomes a symlink"""
1170
self.requireFeature(tests.SymlinkFeature)
1171
state, entry = self.get_state_with_a()
1172
# The symlink target won't be cached if it isn't old
1173
state.adjust_time(+10)
1174
self.create_and_test_dir(state, entry)
1176
self.create_and_test_symlink(state, entry)
1178
def test_update_symlink_to_file(self):
1179
"""Symlink becomes a file"""
1180
self.requireFeature(tests.SymlinkFeature)
1181
state, entry = self.get_state_with_a()
1182
# The symlink and file info won't be cached unless old
1183
state.adjust_time(+10)
1184
self.create_and_test_symlink(state, entry)
1186
self.create_and_test_file(state, entry)
1188
def test_update_symlink_to_dir(self):
1189
"""Symlink becomes a directory"""
1190
self.requireFeature(tests.SymlinkFeature)
1191
state, entry = self.get_state_with_a()
1192
# The symlink target won't be cached if it isn't old
1193
state.adjust_time(+10)
1194
self.create_and_test_symlink(state, entry)
1196
self.create_and_test_dir(state, entry)
1198
def test__is_executable_win32(self):
1199
state, entry = self.get_state_with_a()
1200
self.build_tree(['a'])
1202
# Make sure we are using the win32 implementation of _is_executable
1203
state._is_executable = state._is_executable_win32
1205
# The file on disk is not executable, but we are marking it as though
1206
# it is. With _is_executable_win32 we ignore what is on disk.
1207
entry[1][0] = ('f', '', 0, True, dirstate.DirState.NULLSTAT)
1209
stat_value = os.lstat('a')
1210
packed_stat = dirstate.pack_stat(stat_value)
1212
state.adjust_time(-10) # Make sure everything is new
1213
self.update_entry(state, entry, abspath='a', stat_value=stat_value)
1215
# The row is updated, but the executable bit stays set.
1216
self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1219
# Make the disk object look old enough to cache (but it won't cache the
1220
# sha as it is a new file).
1221
state.adjust_time(+20)
1222
digest = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1223
self.update_entry(state, entry, abspath='a', stat_value=stat_value)
1224
self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1227
def _prepare_tree(self):
1229
text = 'Hello World\n'
1230
tree = self.make_branch_and_tree('tree')
1231
self.build_tree_contents([('tree/a file', text)])
1232
tree.add('a file', 'a-file-id')
1233
# Note: dirstate does not sha prior to the first commit
1234
# so commit now in order for the test to work
1235
tree.commit('first')
1238
def test_sha1provider_sha1_used(self):
1239
tree, text = self._prepare_tree()
1240
state = dirstate.DirState.from_tree(tree, 'dirstate',
1241
UppercaseSHA1Provider())
1242
self.addCleanup(state.unlock)
1243
expected_sha = osutils.sha_string(text.upper() + "foo")
1244
entry = state._get_entry(0, path_utf8='a file')
1245
state._sha_cutoff_time()
1246
state._cutoff_time += 10
1247
sha1 = self.update_entry(state, entry, 'tree/a file',
1248
os.lstat('tree/a file'))
1249
self.assertEqual(expected_sha, sha1)
1251
def test_sha1provider_stat_and_sha1_used(self):
1252
tree, text = self._prepare_tree()
1254
self.addCleanup(tree.unlock)
1255
state = tree._current_dirstate()
1256
state._sha1_provider = UppercaseSHA1Provider()
1257
# If we used the standard provider, it would look like nothing has
1259
file_ids_changed = [change[0] for change
1260
in tree.iter_changes(tree.basis_tree())]
1261
self.assertEqual(['a-file-id'], file_ids_changed)
1264
class UppercaseSHA1Provider(dirstate.SHA1Provider):
1265
"""A custom SHA1Provider."""
1267
def sha1(self, abspath):
1268
return self.stat_and_sha1(abspath)[1]
1270
def stat_and_sha1(self, abspath):
1271
file_obj = file(abspath, 'rb')
1273
statvalue = os.fstat(file_obj.fileno())
1274
text = ''.join(file_obj.readlines())
1275
sha1 = osutils.sha_string(text.upper() + "foo")
1278
return statvalue, sha1
1281
class TestProcessEntry(test_dirstate.TestCaseWithDirState):
1284
_process_entry = None
1287
super(TestProcessEntry, self).setUp()
1288
orig = dirstate._process_entry
1290
dirstate._process_entry = orig
1291
self.addCleanup(cleanup)
1292
dirstate._process_entry = self._process_entry
1294
def assertChangedFileIds(self, expected, tree):
1297
file_ids = [info[0] for info
1298
in tree.iter_changes(tree.basis_tree())]
1301
self.assertEqual(sorted(expected), sorted(file_ids))
1303
def test_simple_changes(self):
1304
tree = self.make_branch_and_tree('tree')
1305
self.build_tree(['tree/file'])
1306
tree.add(['file'], ['file-id'])
1307
self.assertChangedFileIds([tree.get_root_id(), 'file-id'], tree)
1309
self.assertChangedFileIds([], tree)
1311
def test_sha1provider_stat_and_sha1_used(self):
1312
tree = self.make_branch_and_tree('tree')
1313
self.build_tree(['tree/file'])
1314
tree.add(['file'], ['file-id'])
1317
self.addCleanup(tree.unlock)
1318
state = tree._current_dirstate()
1319
state._sha1_provider = UppercaseSHA1Provider()
1320
self.assertChangedFileIds(['file-id'], tree)