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
orig = dirstate.update_entry
836
dirstate.update_entry = orig
837
self.addCleanup(cleanup)
838
dirstate.update_entry = self.update_entry
840
def get_state_with_a(self):
841
"""Create a DirState tracking a single object named 'a'"""
842
state = test_dirstate.InstrumentedDirState.initialize('dirstate')
843
self.addCleanup(state.unlock)
844
state.add('a', 'a-id', 'file', None, '')
845
entry = state._get_entry(0, path_utf8='a')
848
def test_observed_sha1_cachable(self):
849
state, entry = self.get_state_with_a()
850
atime = time.time() - 10
851
self.build_tree(['a'])
852
statvalue = os.lstat('a')
853
statvalue = test_dirstate._FakeStat(statvalue.st_size, atime, atime,
854
statvalue.st_dev, statvalue.st_ino, statvalue.st_mode)
855
state._observed_sha1(entry, "foo", statvalue)
856
self.assertEqual('foo', entry[1][0][1])
857
packed_stat = dirstate.pack_stat(statvalue)
858
self.assertEqual(packed_stat, entry[1][0][4])
860
def test_observed_sha1_not_cachable(self):
861
state, entry = self.get_state_with_a()
862
oldval = entry[1][0][1]
863
oldstat = entry[1][0][4]
864
self.build_tree(['a'])
865
statvalue = os.lstat('a')
866
state._observed_sha1(entry, "foo", statvalue)
867
self.assertEqual(oldval, entry[1][0][1])
868
self.assertEqual(oldstat, entry[1][0][4])
870
def test_update_entry(self):
871
state, _ = self.get_state_with_a()
872
tree = self.make_branch_and_tree('tree')
874
empty_revid = tree.commit('empty')
875
self.build_tree(['tree/a'])
876
tree.add(['a'], ['a-id'])
877
with_a_id = tree.commit('with_a')
878
self.addCleanup(tree.unlock)
879
state.set_parent_trees(
880
[(empty_revid, tree.branch.repository.revision_tree(empty_revid))],
882
entry = state._get_entry(0, path_utf8='a')
883
self.build_tree(['a'])
884
# Add one where we don't provide the stat or sha already
885
self.assertEqual(('', 'a', 'a-id'), entry[0])
886
self.assertEqual(('f', '', 0, False, dirstate.DirState.NULLSTAT),
888
# Flush the buffers to disk
890
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
891
state._dirblock_state)
893
stat_value = os.lstat('a')
894
packed_stat = dirstate.pack_stat(stat_value)
895
link_or_sha1 = self.update_entry(state, entry, abspath='a',
896
stat_value=stat_value)
897
self.assertEqual(None, link_or_sha1)
899
# The dirblock entry should not have cached the file's sha1 (too new)
900
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
902
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
903
state._dirblock_state)
904
mode = stat_value.st_mode
905
self.assertEqual([('is_exec', mode, False)], state._log)
908
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
909
state._dirblock_state)
911
# If we do it again right away, we don't know if the file has changed
912
# so we will re-read the file. Roll the clock back so the file is
913
# guaranteed to look too new.
914
state.adjust_time(-10)
917
link_or_sha1 = self.update_entry(state, entry, abspath='a',
918
stat_value=stat_value)
919
self.assertEqual([('is_exec', mode, False)], state._log)
920
self.assertEqual(None, link_or_sha1)
921
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
922
state._dirblock_state)
923
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
927
# If it is cachable (the clock has moved forward) but new it still
928
# won't calculate the sha or cache it.
929
state.adjust_time(+20)
931
link_or_sha1 = dirstate.update_entry(state, entry, abspath='a',
932
stat_value=stat_value)
933
self.assertEqual(None, link_or_sha1)
934
self.assertEqual([('is_exec', mode, False)], state._log)
935
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
938
# If the file is no longer new, and the clock has been moved forward
939
# sufficiently, it will cache the sha.
941
state.set_parent_trees(
942
[(with_a_id, tree.branch.repository.revision_tree(with_a_id))],
944
entry = state._get_entry(0, path_utf8='a')
946
link_or_sha1 = self.update_entry(state, entry, abspath='a',
947
stat_value=stat_value)
948
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
950
self.assertEqual([('is_exec', mode, False), ('sha1', 'a')],
952
self.assertEqual(('f', link_or_sha1, 14, False, packed_stat),
955
# Subsequent calls will just return the cached value
957
link_or_sha1 = self.update_entry(state, entry, abspath='a',
958
stat_value=stat_value)
959
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
961
self.assertEqual([], state._log)
962
self.assertEqual(('f', link_or_sha1, 14, False, packed_stat),
965
def test_update_entry_symlink(self):
966
"""Update entry should read symlinks."""
967
self.requireFeature(tests.SymlinkFeature)
968
state, entry = self.get_state_with_a()
970
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
971
state._dirblock_state)
972
os.symlink('target', 'a')
974
state.adjust_time(-10) # Make the symlink look new
975
stat_value = os.lstat('a')
976
packed_stat = dirstate.pack_stat(stat_value)
977
link_or_sha1 = self.update_entry(state, entry, abspath='a',
978
stat_value=stat_value)
979
self.assertEqual('target', link_or_sha1)
980
self.assertEqual([('read_link', 'a', '')], state._log)
981
# Dirblock is not updated (the link is too new)
982
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
984
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
985
state._dirblock_state)
987
# Because the stat_value looks new, we should re-read the target
988
link_or_sha1 = self.update_entry(state, entry, abspath='a',
989
stat_value=stat_value)
990
self.assertEqual('target', link_or_sha1)
991
self.assertEqual([('read_link', 'a', ''),
992
('read_link', 'a', ''),
994
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
996
state.adjust_time(+20) # Skip into the future, all files look old
997
link_or_sha1 = self.update_entry(state, entry, abspath='a',
998
stat_value=stat_value)
999
self.assertEqual('target', link_or_sha1)
1000
# We need to re-read the link because only now can we cache it
1001
self.assertEqual([('read_link', 'a', ''),
1002
('read_link', 'a', ''),
1003
('read_link', 'a', ''),
1005
self.assertEqual([('l', 'target', 6, False, packed_stat)],
1008
# Another call won't re-read the link
1009
self.assertEqual([('read_link', 'a', ''),
1010
('read_link', 'a', ''),
1011
('read_link', 'a', ''),
1013
link_or_sha1 = self.update_entry(state, entry, abspath='a',
1014
stat_value=stat_value)
1015
self.assertEqual('target', link_or_sha1)
1016
self.assertEqual([('l', 'target', 6, False, packed_stat)],
1019
def do_update_entry(self, state, entry, abspath):
1020
stat_value = os.lstat(abspath)
1021
return self.update_entry(state, entry, abspath, stat_value)
1023
def test_update_entry_dir(self):
1024
state, entry = self.get_state_with_a()
1025
self.build_tree(['a/'])
1026
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1028
def test_update_entry_dir_unchanged(self):
1029
state, entry = self.get_state_with_a()
1030
self.build_tree(['a/'])
1031
state.adjust_time(+20)
1032
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1033
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1034
state._dirblock_state)
1036
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1037
state._dirblock_state)
1038
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1039
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1040
state._dirblock_state)
1042
def test_update_entry_file_unchanged(self):
1043
state, _ = self.get_state_with_a()
1044
tree = self.make_branch_and_tree('tree')
1046
self.build_tree(['tree/a'])
1047
tree.add(['a'], ['a-id'])
1048
with_a_id = tree.commit('witha')
1049
self.addCleanup(tree.unlock)
1050
state.set_parent_trees(
1051
[(with_a_id, tree.branch.repository.revision_tree(with_a_id))],
1053
entry = state._get_entry(0, path_utf8='a')
1054
self.build_tree(['a'])
1055
sha1sum = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1056
state.adjust_time(+20)
1057
self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1058
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1059
state._dirblock_state)
1061
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1062
state._dirblock_state)
1063
self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1064
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1065
state._dirblock_state)
1067
def test_update_entry_tree_reference(self):
1068
state = test_dirstate.InstrumentedDirState.initialize('dirstate')
1069
self.addCleanup(state.unlock)
1070
state.add('r', 'r-id', 'tree-reference', None, '')
1071
self.build_tree(['r/'])
1072
entry = state._get_entry(0, path_utf8='r')
1073
self.do_update_entry(state, entry, 'r')
1074
entry = state._get_entry(0, path_utf8='r')
1075
self.assertEqual('t', entry[1][0][0])
1077
def create_and_test_file(self, state, entry):
1078
"""Create a file at 'a' and verify the state finds it during update.
1080
The state should already be versioning *something* at 'a'. This makes
1081
sure that state.update_entry recognizes it as a file.
1083
self.build_tree(['a'])
1084
stat_value = os.lstat('a')
1085
packed_stat = dirstate.pack_stat(stat_value)
1087
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1088
self.assertEqual(None, link_or_sha1)
1089
self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
1093
def create_and_test_dir(self, state, entry):
1094
"""Create a directory at 'a' and verify the state finds it.
1096
The state should already be versioning *something* at 'a'. This makes
1097
sure that state.update_entry recognizes it as a directory.
1099
self.build_tree(['a/'])
1100
stat_value = os.lstat('a')
1101
packed_stat = dirstate.pack_stat(stat_value)
1103
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1104
self.assertIs(None, link_or_sha1)
1105
self.assertEqual([('d', '', 0, False, packed_stat)], entry[1])
1109
# FIXME: Add unicode version
1110
def create_and_test_symlink(self, state, entry):
1111
"""Create a symlink at 'a' and verify the state finds it.
1113
The state should already be versioning *something* at 'a'. This makes
1114
sure that state.update_entry recognizes it as a symlink.
1116
This should not be called if this platform does not have symlink
1119
# caller should care about skipping test on platforms without symlinks
1120
os.symlink('path/to/foo', 'a')
1122
stat_value = os.lstat('a')
1123
packed_stat = dirstate.pack_stat(stat_value)
1125
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1126
self.assertEqual('path/to/foo', link_or_sha1)
1127
self.assertEqual([('l', 'path/to/foo', 11, False, packed_stat)],
1131
def test_update_file_to_dir(self):
1132
"""If a file changes to a directory we return None for the sha.
1133
We also update the inventory record.
1135
state, entry = self.get_state_with_a()
1136
# The file sha1 won't be cached unless the file is old
1137
state.adjust_time(+10)
1138
self.create_and_test_file(state, entry)
1140
self.create_and_test_dir(state, entry)
1142
def test_update_file_to_symlink(self):
1143
"""File becomes a symlink"""
1144
self.requireFeature(tests.SymlinkFeature)
1145
state, entry = self.get_state_with_a()
1146
# The file sha1 won't be cached unless the file is old
1147
state.adjust_time(+10)
1148
self.create_and_test_file(state, entry)
1150
self.create_and_test_symlink(state, entry)
1152
def test_update_dir_to_file(self):
1153
"""Directory becoming a file updates the entry."""
1154
state, entry = self.get_state_with_a()
1155
# The file sha1 won't be cached unless the file is old
1156
state.adjust_time(+10)
1157
self.create_and_test_dir(state, entry)
1159
self.create_and_test_file(state, entry)
1161
def test_update_dir_to_symlink(self):
1162
"""Directory becomes a symlink"""
1163
self.requireFeature(tests.SymlinkFeature)
1164
state, entry = self.get_state_with_a()
1165
# The symlink target won't be cached if it isn't old
1166
state.adjust_time(+10)
1167
self.create_and_test_dir(state, entry)
1169
self.create_and_test_symlink(state, entry)
1171
def test_update_symlink_to_file(self):
1172
"""Symlink becomes a file"""
1173
self.requireFeature(tests.SymlinkFeature)
1174
state, entry = self.get_state_with_a()
1175
# The symlink and file info won't be cached unless old
1176
state.adjust_time(+10)
1177
self.create_and_test_symlink(state, entry)
1179
self.create_and_test_file(state, entry)
1181
def test_update_symlink_to_dir(self):
1182
"""Symlink becomes a directory"""
1183
self.requireFeature(tests.SymlinkFeature)
1184
state, entry = self.get_state_with_a()
1185
# The symlink target won't be cached if it isn't old
1186
state.adjust_time(+10)
1187
self.create_and_test_symlink(state, entry)
1189
self.create_and_test_dir(state, entry)
1191
def test__is_executable_win32(self):
1192
state, entry = self.get_state_with_a()
1193
self.build_tree(['a'])
1195
# Make sure we are using the win32 implementation of _is_executable
1196
state._is_executable = state._is_executable_win32
1198
# The file on disk is not executable, but we are marking it as though
1199
# it is. With _is_executable_win32 we ignore what is on disk.
1200
entry[1][0] = ('f', '', 0, True, dirstate.DirState.NULLSTAT)
1202
stat_value = os.lstat('a')
1203
packed_stat = dirstate.pack_stat(stat_value)
1205
state.adjust_time(-10) # Make sure everything is new
1206
self.update_entry(state, entry, abspath='a', stat_value=stat_value)
1208
# The row is updated, but the executable bit stays set.
1209
self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1212
# Make the disk object look old enough to cache (but it won't cache the
1213
# sha as it is a new file).
1214
state.adjust_time(+20)
1215
digest = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1216
self.update_entry(state, entry, abspath='a', stat_value=stat_value)
1217
self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1220
def _prepare_tree(self):
1222
text = 'Hello World\n'
1223
tree = self.make_branch_and_tree('tree')
1224
self.build_tree_contents([('tree/a file', text)])
1225
tree.add('a file', 'a-file-id')
1226
# Note: dirstate does not sha prior to the first commit
1227
# so commit now in order for the test to work
1228
tree.commit('first')
1231
def test_sha1provider_sha1_used(self):
1232
tree, text = self._prepare_tree()
1233
state = dirstate.DirState.from_tree(tree, 'dirstate',
1234
UppercaseSHA1Provider())
1235
self.addCleanup(state.unlock)
1236
expected_sha = osutils.sha_string(text.upper() + "foo")
1237
entry = state._get_entry(0, path_utf8='a file')
1238
state._sha_cutoff_time()
1239
state._cutoff_time += 10
1240
sha1 = self.update_entry(state, entry, 'tree/a file',
1241
os.lstat('tree/a file'))
1242
self.assertEqual(expected_sha, sha1)
1244
def test_sha1provider_stat_and_sha1_used(self):
1245
tree, text = self._prepare_tree()
1247
self.addCleanup(tree.unlock)
1248
state = tree._current_dirstate()
1249
state._sha1_provider = UppercaseSHA1Provider()
1250
# If we used the standard provider, it would look like nothing has
1252
file_ids_changed = [change[0] for change
1253
in tree.iter_changes(tree.basis_tree())]
1254
self.assertEqual(['a-file-id'], file_ids_changed)
1257
class UppercaseSHA1Provider(dirstate.SHA1Provider):
1258
"""A custom SHA1Provider."""
1260
def sha1(self, abspath):
1261
return self.stat_and_sha1(abspath)[1]
1263
def stat_and_sha1(self, abspath):
1264
file_obj = file(abspath, 'rb')
1266
statvalue = os.fstat(file_obj.fileno())
1267
text = ''.join(file_obj.readlines())
1268
sha1 = osutils.sha_string(text.upper() + "foo")
1271
return statvalue, sha1
1274
class TestProcessEntry(test_dirstate.TestCaseWithDirState):
1277
_process_entry = None
1280
super(TestProcessEntry, self).setUp()
1281
orig = dirstate._process_entry
1283
dirstate._process_entry = orig
1284
self.addCleanup(cleanup)
1285
dirstate._process_entry = self._process_entry
1287
def assertChangedFileIds(self, expected, tree):
1290
file_ids = [info[0] for info
1291
in tree.iter_changes(tree.basis_tree())]
1294
self.assertEqual(sorted(expected), sorted(file_ids))
1296
def test_exceptions_raised(self):
1297
# This is a direct test of bug #495023, it relies on osutils.is_inside
1298
# getting called in an inner function. Which makes it a bit brittle,
1299
# but at least it does reproduce the bug.
1300
def is_inside_raises(*args, **kwargs):
1301
raise RuntimeError('stop this')
1302
tree = self.make_branch_and_tree('tree')
1303
self.build_tree(['tree/file', 'tree/dir/', 'tree/dir/sub',
1304
'tree/dir2/', 'tree/dir2/sub2'])
1305
tree.add(['file', 'dir', 'dir/sub', 'dir2', 'dir2/sub2'])
1306
tree.commit('first commit')
1308
self.addCleanup(tree.unlock)
1309
basis_tree = tree.basis_tree()
1310
orig = osutils.is_inside
1311
self.addCleanup(setattr, osutils, 'is_inside', orig)
1312
osutils.is_inside = is_inside_raises
1313
self.assertListRaises(RuntimeError, tree.iter_changes, basis_tree)
1315
def test_simple_changes(self):
1316
tree = self.make_branch_and_tree('tree')
1317
self.build_tree(['tree/file'])
1318
tree.add(['file'], ['file-id'])
1319
self.assertChangedFileIds([tree.get_root_id(), 'file-id'], tree)
1321
self.assertChangedFileIds([], tree)
1323
def test_sha1provider_stat_and_sha1_used(self):
1324
tree = self.make_branch_and_tree('tree')
1325
self.build_tree(['tree/file'])
1326
tree.add(['file'], ['file-id'])
1329
self.addCleanup(tree.unlock)
1330
state = tree._current_dirstate()
1331
state._sha1_provider = UppercaseSHA1Provider()
1332
self.assertChangedFileIds(['file-id'], tree)