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_pyx
36
has_dirstate_helpers_pyx = True
38
has_dirstate_helpers_pyx = False
41
class _CompiledDirstateHelpersFeature(tests.Feature):
43
return has_dirstate_helpers_pyx
45
def feature_name(self):
46
return 'bzrlib._dirstate_helpers_pyx'
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_pyx:
60
pyrex_scenario = ('dirstate_Pyrex',
61
{'update_entry': _dirstate_helpers_pyx.update_entry})
62
ue_scenarios.append(pyrex_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_pyx:
75
{'_process_entry': _dirstate_helpers_pyx.ProcessEntryC})
76
pe_scenarios.append(pyrex_scenario)
77
process_entry_tests, remaining_tests = tests.split_suite_by_condition(
78
remaining_tests, tests.condition_isinstance(TestProcessEntry))
79
tests.multiply_tests(process_entry_tests,
80
tests.multiply_scenarios(dir_reader_scenarios,
84
dir_reader_tests, remaining_tests = tests.split_suite_by_condition(
85
remaining_tests, tests.condition_isinstance(
86
test_dirstate.TestCaseWithDirState))
87
tests.multiply_tests(dir_reader_tests, dir_reader_scenarios, suite)
88
suite.addTest(remaining_tests)
93
class TestBisectPathMixin(object):
94
"""Test that _bisect_path_*() returns the expected values.
96
_bisect_path_* is intended to work like bisect.bisect_*() except it
97
knows it is working on paths that are sorted by ('path', 'to', 'foo')
98
chunks rather than by raw 'path/to/foo'.
100
Test Cases should inherit from this and override ``get_bisect_path`` return
101
their implementation, and ``get_bisect`` to return the matching
102
bisect.bisect_* function.
105
def get_bisect_path(self):
106
"""Return an implementation of _bisect_path_*"""
107
raise NotImplementedError
109
def get_bisect(self):
110
"""Return a version of bisect.bisect_*.
112
Also, for the 'exists' check, return the offset to the real values.
113
For example bisect_left returns the index of an entry, while
114
bisect_right returns the index *after* an entry
116
:return: (bisect_func, offset)
118
raise NotImplementedError
120
def assertBisect(self, paths, split_paths, path, exists=True):
121
"""Assert that bisect_split works like bisect_left on the split paths.
123
:param paths: A list of path names
124
:param split_paths: A list of path names that are already split up by directory
125
('path/to/foo' => ('path', 'to', 'foo'))
126
:param path: The path we are indexing.
127
:param exists: The path should be present, so make sure the
128
final location actually points to the right value.
130
All other arguments will be passed along.
132
bisect_path = self.get_bisect_path()
133
self.assertIsInstance(paths, list)
134
bisect_path_idx = bisect_path(paths, path)
135
split_path = self.split_for_dirblocks([path])[0]
136
bisect_func, offset = self.get_bisect()
137
bisect_split_idx = bisect_func(split_paths, split_path)
138
self.assertEqual(bisect_split_idx, bisect_path_idx,
139
'%s disagreed. %s != %s'
141
% (bisect_path.__name__,
142
bisect_split_idx, bisect_path_idx, path)
145
self.assertEqual(path, paths[bisect_path_idx+offset])
147
def split_for_dirblocks(self, paths):
150
dirname, basename = os.path.split(path)
151
dir_split_paths.append((dirname.split('/'), basename))
152
dir_split_paths.sort()
153
return dir_split_paths
155
def test_simple(self):
156
"""In the simple case it works just like bisect_left"""
157
paths = ['', 'a', 'b', 'c', 'd']
158
split_paths = self.split_for_dirblocks(paths)
160
self.assertBisect(paths, split_paths, path, exists=True)
161
self.assertBisect(paths, split_paths, '_', exists=False)
162
self.assertBisect(paths, split_paths, 'aa', exists=False)
163
self.assertBisect(paths, split_paths, 'bb', exists=False)
164
self.assertBisect(paths, split_paths, 'cc', exists=False)
165
self.assertBisect(paths, split_paths, 'dd', exists=False)
166
self.assertBisect(paths, split_paths, 'a/a', exists=False)
167
self.assertBisect(paths, split_paths, 'b/b', exists=False)
168
self.assertBisect(paths, split_paths, 'c/c', exists=False)
169
self.assertBisect(paths, split_paths, 'd/d', exists=False)
171
def test_involved(self):
172
"""This is where bisect_path_* diverges slightly."""
173
# This is the list of paths and their contents
201
# This is the exact order that is stored by dirstate
202
# All children in a directory are mentioned before an children of
203
# children are mentioned.
204
# So all the root-directory paths, then all the
205
# first sub directory, etc.
206
paths = [# content of '/'
207
'', 'a', 'a-a', 'a-z', 'a=a', 'a=z',
209
'a/a', 'a/a-a', 'a/a-z',
211
'a/z', 'a/z-a', 'a/z-z',
234
split_paths = self.split_for_dirblocks(paths)
236
for dir_parts, basename in split_paths:
237
if dir_parts == ['']:
238
sorted_paths.append(basename)
240
sorted_paths.append('/'.join(dir_parts + [basename]))
242
self.assertEqual(sorted_paths, paths)
245
self.assertBisect(paths, split_paths, path, exists=True)
248
class TestBisectPathLeft(tests.TestCase, TestBisectPathMixin):
249
"""Run all Bisect Path tests against _bisect_path_left."""
251
def get_bisect_path(self):
252
from bzrlib._dirstate_helpers_py import _bisect_path_left
253
return _bisect_path_left
255
def get_bisect(self):
256
return bisect.bisect_left, 0
259
class TestCompiledBisectPathLeft(TestBisectPathLeft):
260
"""Run all Bisect Path tests against _bisect_path_lect"""
262
_test_needs_features = [CompiledDirstateHelpersFeature]
264
def get_bisect_path(self):
265
from bzrlib._dirstate_helpers_pyx import _bisect_path_left
266
return _bisect_path_left
269
class TestBisectPathRight(tests.TestCase, TestBisectPathMixin):
270
"""Run all Bisect Path tests against _bisect_path_right"""
272
def get_bisect_path(self):
273
from bzrlib._dirstate_helpers_py import _bisect_path_right
274
return _bisect_path_right
276
def get_bisect(self):
277
return bisect.bisect_right, -1
280
class TestCompiledBisectPathRight(TestBisectPathRight):
281
"""Run all Bisect Path tests against _bisect_path_right"""
283
_test_needs_features = [CompiledDirstateHelpersFeature]
285
def get_bisect_path(self):
286
from bzrlib._dirstate_helpers_pyx import _bisect_path_right
287
return _bisect_path_right
290
class TestBisectDirblock(tests.TestCase):
291
"""Test that bisect_dirblock() returns the expected values.
293
bisect_dirblock is intended to work like bisect.bisect_left() except it
294
knows it is working on dirblocks and that dirblocks are sorted by ('path',
295
'to', 'foo') chunks rather than by raw 'path/to/foo'.
297
This test is parameterized by calling get_bisect_dirblock(). Child test
298
cases can override this function to test against a different
302
def get_bisect_dirblock(self):
303
"""Return an implementation of bisect_dirblock"""
304
from bzrlib._dirstate_helpers_py import bisect_dirblock
305
return bisect_dirblock
307
def assertBisect(self, dirblocks, split_dirblocks, path, *args, **kwargs):
308
"""Assert that bisect_split works like bisect_left on the split paths.
310
:param dirblocks: A list of (path, [info]) pairs.
311
:param split_dirblocks: A list of ((split, path), [info]) pairs.
312
:param path: The path we are indexing.
314
All other arguments will be passed along.
316
bisect_dirblock = self.get_bisect_dirblock()
317
self.assertIsInstance(dirblocks, list)
318
bisect_split_idx = bisect_dirblock(dirblocks, path, *args, **kwargs)
319
split_dirblock = (path.split('/'), [])
320
bisect_left_idx = bisect.bisect_left(split_dirblocks, split_dirblock,
322
self.assertEqual(bisect_left_idx, bisect_split_idx,
323
'bisect_split disagreed. %s != %s'
325
% (bisect_left_idx, bisect_split_idx, path)
328
def paths_to_dirblocks(self, paths):
329
"""Convert a list of paths into dirblock form.
331
Also, ensure that the paths are in proper sorted order.
333
dirblocks = [(path, []) for path in paths]
334
split_dirblocks = [(path.split('/'), []) for path in paths]
335
self.assertEqual(sorted(split_dirblocks), split_dirblocks)
336
return dirblocks, split_dirblocks
338
def test_simple(self):
339
"""In the simple case it works just like bisect_left"""
340
paths = ['', 'a', 'b', 'c', 'd']
341
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
343
self.assertBisect(dirblocks, split_dirblocks, path)
344
self.assertBisect(dirblocks, split_dirblocks, '_')
345
self.assertBisect(dirblocks, split_dirblocks, 'aa')
346
self.assertBisect(dirblocks, split_dirblocks, 'bb')
347
self.assertBisect(dirblocks, split_dirblocks, 'cc')
348
self.assertBisect(dirblocks, split_dirblocks, 'dd')
349
self.assertBisect(dirblocks, split_dirblocks, 'a/a')
350
self.assertBisect(dirblocks, split_dirblocks, 'b/b')
351
self.assertBisect(dirblocks, split_dirblocks, 'c/c')
352
self.assertBisect(dirblocks, split_dirblocks, 'd/d')
354
def test_involved(self):
355
"""This is where bisect_left diverges slightly."""
357
'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
358
'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
360
'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
361
'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
364
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
366
self.assertBisect(dirblocks, split_dirblocks, path)
368
def test_involved_cached(self):
369
"""This is where bisect_left diverges slightly."""
371
'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
372
'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
374
'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
375
'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
379
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
381
self.assertBisect(dirblocks, split_dirblocks, path, cache=cache)
384
class TestCompiledBisectDirblock(TestBisectDirblock):
385
"""Test that bisect_dirblock() returns the expected values.
387
bisect_dirblock is intended to work like bisect.bisect_left() except it
388
knows it is working on dirblocks and that dirblocks are sorted by ('path',
389
'to', 'foo') chunks rather than by raw 'path/to/foo'.
391
This runs all the normal tests that TestBisectDirblock did, but uses the
395
_test_needs_features = [CompiledDirstateHelpersFeature]
397
def get_bisect_dirblock(self):
398
from bzrlib._dirstate_helpers_pyx import bisect_dirblock
399
return bisect_dirblock
402
class TestCmpByDirs(tests.TestCase):
403
"""Test an implementation of cmp_by_dirs()
405
cmp_by_dirs() compares 2 paths by their directory sections, rather than as
408
Child test cases can override ``get_cmp_by_dirs`` to test a specific
412
def get_cmp_by_dirs(self):
413
"""Get a specific implementation of cmp_by_dirs."""
414
from bzrlib._dirstate_helpers_py import cmp_by_dirs
417
def assertCmpByDirs(self, expected, str1, str2):
418
"""Compare the two strings, in both directions.
420
:param expected: The expected comparison value. -1 means str1 comes
421
first, 0 means they are equal, 1 means str2 comes first
422
:param str1: string to compare
423
:param str2: string to compare
425
cmp_by_dirs = self.get_cmp_by_dirs()
427
self.assertEqual(str1, str2)
428
self.assertEqual(0, cmp_by_dirs(str1, str2))
429
self.assertEqual(0, cmp_by_dirs(str2, str1))
431
self.assertPositive(cmp_by_dirs(str1, str2))
432
self.assertNegative(cmp_by_dirs(str2, str1))
434
self.assertNegative(cmp_by_dirs(str1, str2))
435
self.assertPositive(cmp_by_dirs(str2, str1))
437
def test_cmp_empty(self):
438
"""Compare against the empty string."""
439
self.assertCmpByDirs(0, '', '')
440
self.assertCmpByDirs(1, 'a', '')
441
self.assertCmpByDirs(1, 'ab', '')
442
self.assertCmpByDirs(1, 'abc', '')
443
self.assertCmpByDirs(1, 'abcd', '')
444
self.assertCmpByDirs(1, 'abcde', '')
445
self.assertCmpByDirs(1, 'abcdef', '')
446
self.assertCmpByDirs(1, 'abcdefg', '')
447
self.assertCmpByDirs(1, 'abcdefgh', '')
448
self.assertCmpByDirs(1, 'abcdefghi', '')
449
self.assertCmpByDirs(1, 'test/ing/a/path/', '')
451
def test_cmp_same_str(self):
452
"""Compare the same string"""
453
self.assertCmpByDirs(0, 'a', 'a')
454
self.assertCmpByDirs(0, 'ab', 'ab')
455
self.assertCmpByDirs(0, 'abc', 'abc')
456
self.assertCmpByDirs(0, 'abcd', 'abcd')
457
self.assertCmpByDirs(0, 'abcde', 'abcde')
458
self.assertCmpByDirs(0, 'abcdef', 'abcdef')
459
self.assertCmpByDirs(0, 'abcdefg', 'abcdefg')
460
self.assertCmpByDirs(0, 'abcdefgh', 'abcdefgh')
461
self.assertCmpByDirs(0, 'abcdefghi', 'abcdefghi')
462
self.assertCmpByDirs(0, 'testing a long string', 'testing a long string')
463
self.assertCmpByDirs(0, 'x'*10000, 'x'*10000)
464
self.assertCmpByDirs(0, 'a/b', 'a/b')
465
self.assertCmpByDirs(0, 'a/b/c', 'a/b/c')
466
self.assertCmpByDirs(0, 'a/b/c/d', 'a/b/c/d')
467
self.assertCmpByDirs(0, 'a/b/c/d/e', 'a/b/c/d/e')
469
def test_simple_paths(self):
470
"""Compare strings that act like normal string comparison"""
471
self.assertCmpByDirs(-1, 'a', 'b')
472
self.assertCmpByDirs(-1, 'aa', 'ab')
473
self.assertCmpByDirs(-1, 'ab', 'bb')
474
self.assertCmpByDirs(-1, 'aaa', 'aab')
475
self.assertCmpByDirs(-1, 'aab', 'abb')
476
self.assertCmpByDirs(-1, 'abb', 'bbb')
477
self.assertCmpByDirs(-1, 'aaaa', 'aaab')
478
self.assertCmpByDirs(-1, 'aaab', 'aabb')
479
self.assertCmpByDirs(-1, 'aabb', 'abbb')
480
self.assertCmpByDirs(-1, 'abbb', 'bbbb')
481
self.assertCmpByDirs(-1, 'aaaaa', 'aaaab')
482
self.assertCmpByDirs(-1, 'a/a', 'a/b')
483
self.assertCmpByDirs(-1, 'a/b', 'b/b')
484
self.assertCmpByDirs(-1, 'a/a/a', 'a/a/b')
485
self.assertCmpByDirs(-1, 'a/a/b', 'a/b/b')
486
self.assertCmpByDirs(-1, 'a/b/b', 'b/b/b')
487
self.assertCmpByDirs(-1, 'a/a/a/a', 'a/a/a/b')
488
self.assertCmpByDirs(-1, 'a/a/a/b', 'a/a/b/b')
489
self.assertCmpByDirs(-1, 'a/a/b/b', 'a/b/b/b')
490
self.assertCmpByDirs(-1, 'a/b/b/b', 'b/b/b/b')
491
self.assertCmpByDirs(-1, 'a/a/a/a/a', 'a/a/a/a/b')
493
def test_tricky_paths(self):
494
self.assertCmpByDirs(1, 'ab/cd/ef', 'ab/cc/ef')
495
self.assertCmpByDirs(1, 'ab/cd/ef', 'ab/c/ef')
496
self.assertCmpByDirs(-1, 'ab/cd/ef', 'ab/cd-ef')
497
self.assertCmpByDirs(-1, 'ab/cd', 'ab/cd-')
498
self.assertCmpByDirs(-1, 'ab/cd', 'ab-cd')
500
def test_cmp_unicode_not_allowed(self):
501
cmp_by_dirs = self.get_cmp_by_dirs()
502
self.assertRaises(TypeError, cmp_by_dirs, u'Unicode', 'str')
503
self.assertRaises(TypeError, cmp_by_dirs, 'str', u'Unicode')
504
self.assertRaises(TypeError, cmp_by_dirs, u'Unicode', u'Unicode')
506
def test_cmp_non_ascii(self):
507
self.assertCmpByDirs(-1, '\xc2\xb5', '\xc3\xa5') # u'\xb5', u'\xe5'
508
self.assertCmpByDirs(-1, 'a', '\xc3\xa5') # u'a', u'\xe5'
509
self.assertCmpByDirs(-1, 'b', '\xc2\xb5') # u'b', u'\xb5'
510
self.assertCmpByDirs(-1, 'a/b', 'a/\xc3\xa5') # u'a/b', u'a/\xe5'
511
self.assertCmpByDirs(-1, 'b/a', 'b/\xc2\xb5') # u'b/a', u'b/\xb5'
514
class TestCompiledCmpByDirs(TestCmpByDirs):
515
"""Test the pyrex implementation of cmp_by_dirs"""
517
_test_needs_features = [CompiledDirstateHelpersFeature]
519
def get_cmp_by_dirs(self):
520
from bzrlib._dirstate_helpers_pyx import cmp_by_dirs
524
class TestCmpPathByDirblock(tests.TestCase):
525
"""Test an implementation of _cmp_path_by_dirblock()
527
_cmp_path_by_dirblock() compares two paths using the sort order used by
528
DirState. All paths in the same directory are sorted together.
530
Child test cases can override ``get_cmp_path_by_dirblock`` to test a specific
534
def get_cmp_path_by_dirblock(self):
535
"""Get a specific implementation of _cmp_path_by_dirblock."""
536
from bzrlib._dirstate_helpers_py import _cmp_path_by_dirblock
537
return _cmp_path_by_dirblock
539
def assertCmpPathByDirblock(self, paths):
540
"""Compare all paths and make sure they evaluate to the correct order.
542
This does N^2 comparisons. It is assumed that ``paths`` is properly
545
:param paths: a sorted list of paths to compare
547
# First, make sure the paths being passed in are correct
549
dirname, basename = os.path.split(p)
550
return dirname.split('/'), basename
551
self.assertEqual(sorted(paths, key=_key), paths)
553
cmp_path_by_dirblock = self.get_cmp_path_by_dirblock()
554
for idx1, path1 in enumerate(paths):
555
for idx2, path2 in enumerate(paths):
556
cmp_val = cmp_path_by_dirblock(path1, path2)
558
self.assertTrue(cmp_val < 0,
559
'%s did not state that %r came before %r, cmp=%s'
560
% (cmp_path_by_dirblock.__name__,
561
path1, path2, cmp_val))
563
self.assertTrue(cmp_val > 0,
564
'%s did not state that %r came after %r, cmp=%s'
565
% (cmp_path_by_dirblock.__name__,
566
path1, path2, cmp_val))
568
self.assertTrue(cmp_val == 0,
569
'%s did not state that %r == %r, cmp=%s'
570
% (cmp_path_by_dirblock.__name__,
571
path1, path2, cmp_val))
573
def test_cmp_simple_paths(self):
574
"""Compare against the empty string."""
575
self.assertCmpPathByDirblock(['', 'a', 'ab', 'abc', 'a/b/c', 'b/d/e'])
576
self.assertCmpPathByDirblock(['kl', 'ab/cd', 'ab/ef', 'gh/ij'])
578
def test_tricky_paths(self):
579
self.assertCmpPathByDirblock([
581
'', 'a', 'a-a', 'a=a', 'b',
583
'a/a', 'a/a-a', 'a/a=a', 'a/b',
585
'a/a/a', 'a/a/a-a', 'a/a/a=a',
586
# Contents of 'a/a/a'
587
'a/a/a/a', 'a/a/a/b',
588
# Contents of 'a/a/a-a',
589
'a/a/a-a/a', 'a/a/a-a/b',
590
# Contents of 'a/a/a=a',
591
'a/a/a=a/a', 'a/a/a=a/b',
592
# Contents of 'a/a-a'
594
# Contents of 'a/a-a/a'
595
'a/a-a/a/a', 'a/a-a/a/b',
596
# Contents of 'a/a=a'
607
self.assertCmpPathByDirblock([
609
'', 'a', 'a-a', 'a-z', 'a=a', 'a=z',
611
'a/a', 'a/a-a', 'a/a-z',
613
'a/z', 'a/z-a', 'a/z-z',
637
def test_unicode_not_allowed(self):
638
cmp_path_by_dirblock = self.get_cmp_path_by_dirblock()
639
self.assertRaises(TypeError, cmp_path_by_dirblock, u'Uni', 'str')
640
self.assertRaises(TypeError, cmp_path_by_dirblock, 'str', u'Uni')
641
self.assertRaises(TypeError, cmp_path_by_dirblock, u'Uni', u'Uni')
642
self.assertRaises(TypeError, cmp_path_by_dirblock, u'x/Uni', 'x/str')
643
self.assertRaises(TypeError, cmp_path_by_dirblock, 'x/str', u'x/Uni')
644
self.assertRaises(TypeError, cmp_path_by_dirblock, u'x/Uni', u'x/Uni')
646
def test_nonascii(self):
647
self.assertCmpPathByDirblock([
649
'', 'a', '\xc2\xb5', '\xc3\xa5',
651
'a/a', 'a/\xc2\xb5', 'a/\xc3\xa5',
653
'a/a/a', 'a/a/\xc2\xb5', 'a/a/\xc3\xa5',
654
# content of 'a/\xc2\xb5'
655
'a/\xc2\xb5/a', 'a/\xc2\xb5/\xc2\xb5', 'a/\xc2\xb5/\xc3\xa5',
656
# content of 'a/\xc3\xa5'
657
'a/\xc3\xa5/a', 'a/\xc3\xa5/\xc2\xb5', 'a/\xc3\xa5/\xc3\xa5',
658
# content of '\xc2\xb5'
659
'\xc2\xb5/a', '\xc2\xb5/\xc2\xb5', '\xc2\xb5/\xc3\xa5',
660
# content of '\xc2\xe5'
661
'\xc3\xa5/a', '\xc3\xa5/\xc2\xb5', '\xc3\xa5/\xc3\xa5',
665
class TestCompiledCmpPathByDirblock(TestCmpPathByDirblock):
666
"""Test the pyrex implementation of _cmp_path_by_dirblock"""
668
_test_needs_features = [CompiledDirstateHelpersFeature]
670
def get_cmp_by_dirs(self):
671
from bzrlib._dirstate_helpers_pyx import _cmp_path_by_dirblock
672
return _cmp_path_by_dirblock
675
class TestMemRChr(tests.TestCase):
676
"""Test memrchr functionality"""
678
_test_needs_features = [CompiledDirstateHelpersFeature]
680
def assertMemRChr(self, expected, s, c):
681
from bzrlib._dirstate_helpers_pyx import _py_memrchr
682
self.assertEqual(expected, _py_memrchr(s, c))
684
def test_missing(self):
685
self.assertMemRChr(None, '', 'a')
686
self.assertMemRChr(None, '', 'c')
687
self.assertMemRChr(None, 'abcdefghijklm', 'q')
688
self.assertMemRChr(None, 'aaaaaaaaaaaaaaaaaaaaaaa', 'b')
690
def test_single_entry(self):
691
self.assertMemRChr(0, 'abcdefghijklm', 'a')
692
self.assertMemRChr(1, 'abcdefghijklm', 'b')
693
self.assertMemRChr(2, 'abcdefghijklm', 'c')
694
self.assertMemRChr(10, 'abcdefghijklm', 'k')
695
self.assertMemRChr(11, 'abcdefghijklm', 'l')
696
self.assertMemRChr(12, 'abcdefghijklm', 'm')
698
def test_multiple(self):
699
self.assertMemRChr(10, 'abcdefjklmabcdefghijklm', 'a')
700
self.assertMemRChr(11, 'abcdefjklmabcdefghijklm', 'b')
701
self.assertMemRChr(12, 'abcdefjklmabcdefghijklm', 'c')
702
self.assertMemRChr(20, 'abcdefjklmabcdefghijklm', 'k')
703
self.assertMemRChr(21, 'abcdefjklmabcdefghijklm', 'l')
704
self.assertMemRChr(22, 'abcdefjklmabcdefghijklm', 'm')
705
self.assertMemRChr(22, 'aaaaaaaaaaaaaaaaaaaaaaa', 'a')
707
def test_with_nulls(self):
708
self.assertMemRChr(10, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'a')
709
self.assertMemRChr(11, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'b')
710
self.assertMemRChr(12, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'c')
711
self.assertMemRChr(20, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'k')
712
self.assertMemRChr(21, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'l')
713
self.assertMemRChr(22, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'm')
714
self.assertMemRChr(22, 'aaa\0\0\0aaaaaaa\0\0\0aaaaaaa', 'a')
715
self.assertMemRChr(9, '\0\0\0\0\0\0\0\0\0\0', '\0')
718
class TestReadDirblocks(test_dirstate.TestCaseWithDirState):
719
"""Test an implementation of _read_dirblocks()
721
_read_dirblocks() reads in all of the dirblock information from the disk
724
Child test cases can override ``get_read_dirblocks`` to test a specific
728
def get_read_dirblocks(self):
729
from bzrlib._dirstate_helpers_py import _read_dirblocks
730
return _read_dirblocks
732
def test_smoketest(self):
733
"""Make sure that we can create and read back a simple file."""
734
tree, state, expected = self.create_basic_dirstate()
736
state._read_header_if_needed()
737
self.assertEqual(dirstate.DirState.NOT_IN_MEMORY,
738
state._dirblock_state)
739
read_dirblocks = self.get_read_dirblocks()
740
read_dirblocks(state)
741
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
742
state._dirblock_state)
744
def test_trailing_garbage(self):
745
tree, state, expected = self.create_basic_dirstate()
746
# On Linux, we can write extra data as long as we haven't read yet, but
747
# on Win32, if you've opened the file with FILE_SHARE_READ, trying to
748
# open it in append mode will fail.
750
f = open('dirstate', 'ab')
752
# Add bogus trailing garbage
757
e = self.assertRaises(errors.DirstateCorrupt,
758
state._read_dirblocks_if_needed)
759
# Make sure we mention the bogus characters in the error
760
self.assertContainsRe(str(e), 'bogus')
763
class TestCompiledReadDirblocks(TestReadDirblocks):
764
"""Test the pyrex implementation of _read_dirblocks"""
766
_test_needs_features = [CompiledDirstateHelpersFeature]
768
def get_read_dirblocks(self):
769
from bzrlib._dirstate_helpers_pyx import _read_dirblocks
770
return _read_dirblocks
773
class TestUsingCompiledIfAvailable(tests.TestCase):
774
"""Check that any compiled functions that are available are the default.
776
It is possible to have typos, etc in the import line, such that
777
_dirstate_helpers_pyx is actually available, but the compiled functions are
781
def test_bisect_dirblock(self):
782
if CompiledDirstateHelpersFeature.available():
783
from bzrlib._dirstate_helpers_pyx import bisect_dirblock
785
from bzrlib._dirstate_helpers_py import bisect_dirblock
786
self.assertIs(bisect_dirblock, dirstate.bisect_dirblock)
788
def test__bisect_path_left(self):
789
if CompiledDirstateHelpersFeature.available():
790
from bzrlib._dirstate_helpers_pyx import _bisect_path_left
792
from bzrlib._dirstate_helpers_py import _bisect_path_left
793
self.assertIs(_bisect_path_left, dirstate._bisect_path_left)
795
def test__bisect_path_right(self):
796
if CompiledDirstateHelpersFeature.available():
797
from bzrlib._dirstate_helpers_pyx import _bisect_path_right
799
from bzrlib._dirstate_helpers_py import _bisect_path_right
800
self.assertIs(_bisect_path_right, dirstate._bisect_path_right)
802
def test_cmp_by_dirs(self):
803
if CompiledDirstateHelpersFeature.available():
804
from bzrlib._dirstate_helpers_pyx import cmp_by_dirs
806
from bzrlib._dirstate_helpers_py import cmp_by_dirs
807
self.assertIs(cmp_by_dirs, dirstate.cmp_by_dirs)
809
def test__read_dirblocks(self):
810
if CompiledDirstateHelpersFeature.available():
811
from bzrlib._dirstate_helpers_pyx import _read_dirblocks
813
from bzrlib._dirstate_helpers_py import _read_dirblocks
814
self.assertIs(_read_dirblocks, dirstate._read_dirblocks)
816
def test_update_entry(self):
817
if CompiledDirstateHelpersFeature.available():
818
from bzrlib._dirstate_helpers_pyx import update_entry
820
from bzrlib.dirstate import update_entry
821
self.assertIs(update_entry, dirstate.update_entry)
823
def test_process_entry(self):
824
if CompiledDirstateHelpersFeature.available():
825
from bzrlib._dirstate_helpers_pyx import ProcessEntryC
826
self.assertIs(ProcessEntryC, dirstate._process_entry)
828
from bzrlib.dirstate import ProcessEntryPython
829
self.assertIs(ProcessEntryPython, dirstate._process_entry)
832
class TestUpdateEntry(test_dirstate.TestCaseWithDirState):
833
"""Test the DirState.update_entry functions"""
839
super(TestUpdateEntry, self).setUp()
840
orig = dirstate.update_entry
842
dirstate.update_entry = orig
843
self.addCleanup(cleanup)
844
dirstate.update_entry = self.update_entry
846
def get_state_with_a(self):
847
"""Create a DirState tracking a single object named 'a'"""
848
state = test_dirstate.InstrumentedDirState.initialize('dirstate')
849
self.addCleanup(state.unlock)
850
state.add('a', 'a-id', 'file', None, '')
851
entry = state._get_entry(0, path_utf8='a')
854
def test_observed_sha1_cachable(self):
855
state, entry = self.get_state_with_a()
856
atime = time.time() - 10
857
self.build_tree(['a'])
858
statvalue = os.lstat('a')
859
statvalue = test_dirstate._FakeStat(statvalue.st_size, atime, atime,
860
statvalue.st_dev, statvalue.st_ino, statvalue.st_mode)
861
state._observed_sha1(entry, "foo", statvalue)
862
self.assertEqual('foo', entry[1][0][1])
863
packed_stat = dirstate.pack_stat(statvalue)
864
self.assertEqual(packed_stat, entry[1][0][4])
866
def test_observed_sha1_not_cachable(self):
867
state, entry = self.get_state_with_a()
868
oldval = entry[1][0][1]
869
oldstat = entry[1][0][4]
870
self.build_tree(['a'])
871
statvalue = os.lstat('a')
872
state._observed_sha1(entry, "foo", statvalue)
873
self.assertEqual(oldval, entry[1][0][1])
874
self.assertEqual(oldstat, entry[1][0][4])
876
def test_update_entry(self):
877
state, _ = self.get_state_with_a()
878
tree = self.make_branch_and_tree('tree')
880
empty_revid = tree.commit('empty')
881
self.build_tree(['tree/a'])
882
tree.add(['a'], ['a-id'])
883
with_a_id = tree.commit('with_a')
884
self.addCleanup(tree.unlock)
885
state.set_parent_trees(
886
[(empty_revid, tree.branch.repository.revision_tree(empty_revid))],
888
entry = state._get_entry(0, path_utf8='a')
889
self.build_tree(['a'])
890
# Add one where we don't provide the stat or sha already
891
self.assertEqual(('', 'a', 'a-id'), entry[0])
892
self.assertEqual(('f', '', 0, False, dirstate.DirState.NULLSTAT),
894
# Flush the buffers to disk
896
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
897
state._dirblock_state)
899
stat_value = os.lstat('a')
900
packed_stat = dirstate.pack_stat(stat_value)
901
link_or_sha1 = self.update_entry(state, entry, abspath='a',
902
stat_value=stat_value)
903
self.assertEqual(None, link_or_sha1)
905
# The dirblock entry should not have cached the file's sha1 (too new)
906
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
908
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
909
state._dirblock_state)
910
mode = stat_value.st_mode
911
self.assertEqual([('is_exec', mode, False)], state._log)
914
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
915
state._dirblock_state)
917
# If we do it again right away, we don't know if the file has changed
918
# so we will re-read the file. Roll the clock back so the file is
919
# guaranteed to look too new.
920
state.adjust_time(-10)
923
link_or_sha1 = self.update_entry(state, entry, abspath='a',
924
stat_value=stat_value)
925
self.assertEqual([('is_exec', mode, False)], state._log)
926
self.assertEqual(None, link_or_sha1)
927
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
928
state._dirblock_state)
929
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
933
# If it is cachable (the clock has moved forward) but new it still
934
# won't calculate the sha or cache it.
935
state.adjust_time(+20)
937
link_or_sha1 = dirstate.update_entry(state, entry, abspath='a',
938
stat_value=stat_value)
939
self.assertEqual(None, link_or_sha1)
940
self.assertEqual([('is_exec', mode, False)], state._log)
941
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
944
# If the file is no longer new, and the clock has been moved forward
945
# sufficiently, it will cache the sha.
947
state.set_parent_trees(
948
[(with_a_id, tree.branch.repository.revision_tree(with_a_id))],
950
entry = state._get_entry(0, path_utf8='a')
952
link_or_sha1 = self.update_entry(state, entry, abspath='a',
953
stat_value=stat_value)
954
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
956
self.assertEqual([('is_exec', mode, False), ('sha1', 'a')],
958
self.assertEqual(('f', link_or_sha1, 14, False, packed_stat),
961
# Subsequent calls will just return the cached value
963
link_or_sha1 = self.update_entry(state, entry, abspath='a',
964
stat_value=stat_value)
965
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
967
self.assertEqual([], state._log)
968
self.assertEqual(('f', link_or_sha1, 14, False, packed_stat),
971
def test_update_entry_symlink(self):
972
"""Update entry should read symlinks."""
973
self.requireFeature(tests.SymlinkFeature)
974
state, entry = self.get_state_with_a()
976
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
977
state._dirblock_state)
978
os.symlink('target', 'a')
980
state.adjust_time(-10) # Make the symlink look new
981
stat_value = os.lstat('a')
982
packed_stat = dirstate.pack_stat(stat_value)
983
link_or_sha1 = self.update_entry(state, entry, abspath='a',
984
stat_value=stat_value)
985
self.assertEqual('target', link_or_sha1)
986
self.assertEqual([('read_link', 'a', '')], state._log)
987
# Dirblock is not updated (the link is too new)
988
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
990
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
991
state._dirblock_state)
993
# Because the stat_value looks new, we should re-read the target
994
link_or_sha1 = self.update_entry(state, entry, abspath='a',
995
stat_value=stat_value)
996
self.assertEqual('target', link_or_sha1)
997
self.assertEqual([('read_link', 'a', ''),
998
('read_link', 'a', ''),
1000
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
1002
state.adjust_time(+20) # Skip into the future, all files look old
1003
link_or_sha1 = self.update_entry(state, entry, abspath='a',
1004
stat_value=stat_value)
1005
self.assertEqual('target', link_or_sha1)
1006
# We need to re-read the link because only now can we cache it
1007
self.assertEqual([('read_link', 'a', ''),
1008
('read_link', 'a', ''),
1009
('read_link', 'a', ''),
1011
self.assertEqual([('l', 'target', 6, False, packed_stat)],
1014
# Another call won't re-read the link
1015
self.assertEqual([('read_link', 'a', ''),
1016
('read_link', 'a', ''),
1017
('read_link', 'a', ''),
1019
link_or_sha1 = self.update_entry(state, entry, abspath='a',
1020
stat_value=stat_value)
1021
self.assertEqual('target', link_or_sha1)
1022
self.assertEqual([('l', 'target', 6, False, packed_stat)],
1025
def do_update_entry(self, state, entry, abspath):
1026
stat_value = os.lstat(abspath)
1027
return self.update_entry(state, entry, abspath, stat_value)
1029
def test_update_entry_dir(self):
1030
state, entry = self.get_state_with_a()
1031
self.build_tree(['a/'])
1032
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1034
def test_update_entry_dir_unchanged(self):
1035
state, entry = self.get_state_with_a()
1036
self.build_tree(['a/'])
1037
state.adjust_time(+20)
1038
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1039
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1040
state._dirblock_state)
1042
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1043
state._dirblock_state)
1044
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1045
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1046
state._dirblock_state)
1048
def test_update_entry_file_unchanged(self):
1049
state, _ = self.get_state_with_a()
1050
tree = self.make_branch_and_tree('tree')
1052
self.build_tree(['tree/a'])
1053
tree.add(['a'], ['a-id'])
1054
with_a_id = tree.commit('witha')
1055
self.addCleanup(tree.unlock)
1056
state.set_parent_trees(
1057
[(with_a_id, tree.branch.repository.revision_tree(with_a_id))],
1059
entry = state._get_entry(0, path_utf8='a')
1060
self.build_tree(['a'])
1061
sha1sum = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1062
state.adjust_time(+20)
1063
self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1064
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1065
state._dirblock_state)
1067
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1068
state._dirblock_state)
1069
self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1070
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1071
state._dirblock_state)
1073
def test_update_entry_tree_reference(self):
1074
state = test_dirstate.InstrumentedDirState.initialize('dirstate')
1075
self.addCleanup(state.unlock)
1076
state.add('r', 'r-id', 'tree-reference', None, '')
1077
self.build_tree(['r/'])
1078
entry = state._get_entry(0, path_utf8='r')
1079
self.do_update_entry(state, entry, 'r')
1080
entry = state._get_entry(0, path_utf8='r')
1081
self.assertEqual('t', entry[1][0][0])
1083
def create_and_test_file(self, state, entry):
1084
"""Create a file at 'a' and verify the state finds it during update.
1086
The state should already be versioning *something* at 'a'. This makes
1087
sure that state.update_entry recognizes it as a file.
1089
self.build_tree(['a'])
1090
stat_value = os.lstat('a')
1091
packed_stat = dirstate.pack_stat(stat_value)
1093
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1094
self.assertEqual(None, link_or_sha1)
1095
self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
1099
def create_and_test_dir(self, state, entry):
1100
"""Create a directory at 'a' and verify the state finds it.
1102
The state should already be versioning *something* at 'a'. This makes
1103
sure that state.update_entry recognizes it as a directory.
1105
self.build_tree(['a/'])
1106
stat_value = os.lstat('a')
1107
packed_stat = dirstate.pack_stat(stat_value)
1109
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1110
self.assertIs(None, link_or_sha1)
1111
self.assertEqual([('d', '', 0, False, packed_stat)], entry[1])
1115
# FIXME: Add unicode version
1116
def create_and_test_symlink(self, state, entry):
1117
"""Create a symlink at 'a' and verify the state finds it.
1119
The state should already be versioning *something* at 'a'. This makes
1120
sure that state.update_entry recognizes it as a symlink.
1122
This should not be called if this platform does not have symlink
1125
# caller should care about skipping test on platforms without symlinks
1126
os.symlink('path/to/foo', 'a')
1128
stat_value = os.lstat('a')
1129
packed_stat = dirstate.pack_stat(stat_value)
1131
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1132
self.assertEqual('path/to/foo', link_or_sha1)
1133
self.assertEqual([('l', 'path/to/foo', 11, False, packed_stat)],
1137
def test_update_file_to_dir(self):
1138
"""If a file changes to a directory we return None for the sha.
1139
We also update the inventory record.
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_dir(state, entry)
1148
def test_update_file_to_symlink(self):
1149
"""File becomes a symlink"""
1150
self.requireFeature(tests.SymlinkFeature)
1151
state, entry = self.get_state_with_a()
1152
# The file sha1 won't be cached unless the file is old
1153
state.adjust_time(+10)
1154
self.create_and_test_file(state, entry)
1156
self.create_and_test_symlink(state, entry)
1158
def test_update_dir_to_file(self):
1159
"""Directory becoming a file updates the entry."""
1160
state, entry = self.get_state_with_a()
1161
# The file sha1 won't be cached unless the file is old
1162
state.adjust_time(+10)
1163
self.create_and_test_dir(state, entry)
1165
self.create_and_test_file(state, entry)
1167
def test_update_dir_to_symlink(self):
1168
"""Directory becomes a symlink"""
1169
self.requireFeature(tests.SymlinkFeature)
1170
state, entry = self.get_state_with_a()
1171
# The symlink target won't be cached if it isn't old
1172
state.adjust_time(+10)
1173
self.create_and_test_dir(state, entry)
1175
self.create_and_test_symlink(state, entry)
1177
def test_update_symlink_to_file(self):
1178
"""Symlink becomes a file"""
1179
self.requireFeature(tests.SymlinkFeature)
1180
state, entry = self.get_state_with_a()
1181
# The symlink and file info won't be cached unless old
1182
state.adjust_time(+10)
1183
self.create_and_test_symlink(state, entry)
1185
self.create_and_test_file(state, entry)
1187
def test_update_symlink_to_dir(self):
1188
"""Symlink becomes a directory"""
1189
self.requireFeature(tests.SymlinkFeature)
1190
state, entry = self.get_state_with_a()
1191
# The symlink target won't be cached if it isn't old
1192
state.adjust_time(+10)
1193
self.create_and_test_symlink(state, entry)
1195
self.create_and_test_dir(state, entry)
1197
def test__is_executable_win32(self):
1198
state, entry = self.get_state_with_a()
1199
self.build_tree(['a'])
1201
# Make sure we are using the win32 implementation of _is_executable
1202
state._is_executable = state._is_executable_win32
1204
# The file on disk is not executable, but we are marking it as though
1205
# it is. With _is_executable_win32 we ignore what is on disk.
1206
entry[1][0] = ('f', '', 0, True, dirstate.DirState.NULLSTAT)
1208
stat_value = os.lstat('a')
1209
packed_stat = dirstate.pack_stat(stat_value)
1211
state.adjust_time(-10) # Make sure everything is new
1212
self.update_entry(state, entry, abspath='a', stat_value=stat_value)
1214
# The row is updated, but the executable bit stays set.
1215
self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1218
# Make the disk object look old enough to cache (but it won't cache the
1219
# sha as it is a new file).
1220
state.adjust_time(+20)
1221
digest = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1222
self.update_entry(state, entry, abspath='a', stat_value=stat_value)
1223
self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1226
def _prepare_tree(self):
1228
text = 'Hello World\n'
1229
tree = self.make_branch_and_tree('tree')
1230
self.build_tree_contents([('tree/a file', text)])
1231
tree.add('a file', 'a-file-id')
1232
# Note: dirstate does not sha prior to the first commit
1233
# so commit now in order for the test to work
1234
tree.commit('first')
1237
def test_sha1provider_sha1_used(self):
1238
tree, text = self._prepare_tree()
1239
state = dirstate.DirState.from_tree(tree, 'dirstate',
1240
UppercaseSHA1Provider())
1241
self.addCleanup(state.unlock)
1242
expected_sha = osutils.sha_string(text.upper() + "foo")
1243
entry = state._get_entry(0, path_utf8='a file')
1244
state._sha_cutoff_time()
1245
state._cutoff_time += 10
1246
sha1 = self.update_entry(state, entry, 'tree/a file',
1247
os.lstat('tree/a file'))
1248
self.assertEqual(expected_sha, sha1)
1250
def test_sha1provider_stat_and_sha1_used(self):
1251
tree, text = self._prepare_tree()
1253
self.addCleanup(tree.unlock)
1254
state = tree._current_dirstate()
1255
state._sha1_provider = UppercaseSHA1Provider()
1256
# If we used the standard provider, it would look like nothing has
1258
file_ids_changed = [change[0] for change
1259
in tree.iter_changes(tree.basis_tree())]
1260
self.assertEqual(['a-file-id'], file_ids_changed)
1263
class UppercaseSHA1Provider(dirstate.SHA1Provider):
1264
"""A custom SHA1Provider."""
1266
def sha1(self, abspath):
1267
return self.stat_and_sha1(abspath)[1]
1269
def stat_and_sha1(self, abspath):
1270
file_obj = file(abspath, 'rb')
1272
statvalue = os.fstat(file_obj.fileno())
1273
text = ''.join(file_obj.readlines())
1274
sha1 = osutils.sha_string(text.upper() + "foo")
1277
return statvalue, sha1
1280
class TestProcessEntry(test_dirstate.TestCaseWithDirState):
1283
_process_entry = None
1286
super(TestProcessEntry, self).setUp()
1287
orig = dirstate._process_entry
1289
dirstate._process_entry = orig
1290
self.addCleanup(cleanup)
1291
dirstate._process_entry = self._process_entry
1293
def assertChangedFileIds(self, expected, tree):
1296
file_ids = [info[0] for info
1297
in tree.iter_changes(tree.basis_tree())]
1300
self.assertEqual(sorted(expected), sorted(file_ids))
1302
def test_simple_changes(self):
1303
tree = self.make_branch_and_tree('tree')
1304
self.build_tree(['tree/file'])
1305
tree.add(['file'], ['file-id'])
1306
self.assertChangedFileIds([tree.get_root_id(), 'file-id'], tree)
1308
self.assertChangedFileIds([], tree)
1310
def test_sha1provider_stat_and_sha1_used(self):
1311
tree = self.make_branch_and_tree('tree')
1312
self.build_tree(['tree/file'])
1313
tree.add(['file'], ['file-id'])
1316
self.addCleanup(tree.unlock)
1317
state = tree._current_dirstate()
1318
state._sha1_provider = UppercaseSHA1Provider()
1319
self.assertChangedFileIds(['file-id'], tree)