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
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
# We can modify the file as long as it hasn't been read yet.
747
f = open('dirstate', 'ab')
749
# Add bogus trailing garbage
753
e = self.assertRaises(errors.DirstateCorrupt,
754
state._read_dirblocks_if_needed)
755
# Make sure we mention the bogus characters in the error
756
self.assertContainsRe(str(e), 'bogus')
759
class TestCompiledReadDirblocks(TestReadDirblocks):
760
"""Test the pyrex implementation of _read_dirblocks"""
762
_test_needs_features = [CompiledDirstateHelpersFeature]
764
def get_read_dirblocks(self):
765
from bzrlib._dirstate_helpers_pyx import _read_dirblocks
766
return _read_dirblocks
769
class TestUsingCompiledIfAvailable(tests.TestCase):
770
"""Check that any compiled functions that are available are the default.
772
It is possible to have typos, etc in the import line, such that
773
_dirstate_helpers_pyx is actually available, but the compiled functions are
777
def test_bisect_dirblock(self):
778
if CompiledDirstateHelpersFeature.available():
779
from bzrlib._dirstate_helpers_pyx import bisect_dirblock
781
from bzrlib._dirstate_helpers_py import bisect_dirblock
782
self.assertIs(bisect_dirblock, dirstate.bisect_dirblock)
784
def test__bisect_path_left(self):
785
if CompiledDirstateHelpersFeature.available():
786
from bzrlib._dirstate_helpers_pyx import _bisect_path_left
788
from bzrlib._dirstate_helpers_py import _bisect_path_left
789
self.assertIs(_bisect_path_left, dirstate._bisect_path_left)
791
def test__bisect_path_right(self):
792
if CompiledDirstateHelpersFeature.available():
793
from bzrlib._dirstate_helpers_pyx import _bisect_path_right
795
from bzrlib._dirstate_helpers_py import _bisect_path_right
796
self.assertIs(_bisect_path_right, dirstate._bisect_path_right)
798
def test_cmp_by_dirs(self):
799
if CompiledDirstateHelpersFeature.available():
800
from bzrlib._dirstate_helpers_pyx import cmp_by_dirs
802
from bzrlib._dirstate_helpers_py import cmp_by_dirs
803
self.assertIs(cmp_by_dirs, dirstate.cmp_by_dirs)
805
def test__read_dirblocks(self):
806
if CompiledDirstateHelpersFeature.available():
807
from bzrlib._dirstate_helpers_pyx import _read_dirblocks
809
from bzrlib._dirstate_helpers_py import _read_dirblocks
810
self.assertIs(_read_dirblocks, dirstate._read_dirblocks)
812
def test_update_entry(self):
813
if CompiledDirstateHelpersFeature.available():
814
from bzrlib._dirstate_helpers_pyx import update_entry
816
from bzrlib.dirstate import update_entry
817
self.assertIs(update_entry, dirstate.update_entry)
819
def test_process_entry(self):
820
if CompiledDirstateHelpersFeature.available():
821
from bzrlib._dirstate_helpers_pyx import ProcessEntryC
822
self.assertIs(ProcessEntryC, dirstate._process_entry)
824
from bzrlib.dirstate import ProcessEntryPython
825
self.assertIs(ProcessEntryPython, dirstate._process_entry)
828
class TestUpdateEntry(test_dirstate.TestCaseWithDirState):
829
"""Test the DirState.update_entry functions"""
835
super(TestUpdateEntry, self).setUp()
836
orig = dirstate.update_entry
838
dirstate.update_entry = orig
839
self.addCleanup(cleanup)
840
dirstate.update_entry = self.update_entry
842
def get_state_with_a(self):
843
"""Create a DirState tracking a single object named 'a'"""
844
state = test_dirstate.InstrumentedDirState.initialize('dirstate')
845
self.addCleanup(state.unlock)
846
state.add('a', 'a-id', 'file', None, '')
847
entry = state._get_entry(0, path_utf8='a')
850
def test_observed_sha1_cachable(self):
851
state, entry = self.get_state_with_a()
852
atime = time.time() - 10
853
self.build_tree(['a'])
854
statvalue = os.lstat('a')
855
statvalue = test_dirstate._FakeStat(statvalue.st_size, atime, atime,
856
statvalue.st_dev, statvalue.st_ino, statvalue.st_mode)
857
state._observed_sha1(entry, "foo", statvalue)
858
self.assertEqual('foo', entry[1][0][1])
859
packed_stat = dirstate.pack_stat(statvalue)
860
self.assertEqual(packed_stat, entry[1][0][4])
862
def test_observed_sha1_not_cachable(self):
863
state, entry = self.get_state_with_a()
864
oldval = entry[1][0][1]
865
oldstat = entry[1][0][4]
866
self.build_tree(['a'])
867
statvalue = os.lstat('a')
868
state._observed_sha1(entry, "foo", statvalue)
869
self.assertEqual(oldval, entry[1][0][1])
870
self.assertEqual(oldstat, entry[1][0][4])
872
def test_update_entry(self):
873
state, _ = self.get_state_with_a()
874
tree = self.make_branch_and_tree('tree')
876
empty_revid = tree.commit('empty')
877
self.build_tree(['tree/a'])
878
tree.add(['a'], ['a-id'])
879
with_a_id = tree.commit('with_a')
880
self.addCleanup(tree.unlock)
881
state.set_parent_trees(
882
[(empty_revid, tree.branch.repository.revision_tree(empty_revid))],
884
entry = state._get_entry(0, path_utf8='a')
885
self.build_tree(['a'])
886
# Add one where we don't provide the stat or sha already
887
self.assertEqual(('', 'a', 'a-id'), entry[0])
888
self.assertEqual(('f', '', 0, False, dirstate.DirState.NULLSTAT),
890
# Flush the buffers to disk
892
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
893
state._dirblock_state)
895
stat_value = os.lstat('a')
896
packed_stat = dirstate.pack_stat(stat_value)
897
link_or_sha1 = self.update_entry(state, entry, abspath='a',
898
stat_value=stat_value)
899
self.assertEqual(None, link_or_sha1)
901
# The dirblock entry should not have cached the file's sha1 (too new)
902
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
904
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
905
state._dirblock_state)
906
mode = stat_value.st_mode
907
self.assertEqual([('is_exec', mode, False)], state._log)
910
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
911
state._dirblock_state)
913
# If we do it again right away, we don't know if the file has changed
914
# so we will re-read the file. Roll the clock back so the file is
915
# guaranteed to look too new.
916
state.adjust_time(-10)
919
link_or_sha1 = self.update_entry(state, entry, abspath='a',
920
stat_value=stat_value)
921
self.assertEqual([('is_exec', mode, False)], state._log)
922
self.assertEqual(None, link_or_sha1)
923
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
924
state._dirblock_state)
925
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
929
# If it is cachable (the clock has moved forward) but new it still
930
# won't calculate the sha or cache it.
931
state.adjust_time(+20)
933
link_or_sha1 = dirstate.update_entry(state, entry, abspath='a',
934
stat_value=stat_value)
935
self.assertEqual(None, link_or_sha1)
936
self.assertEqual([('is_exec', mode, False)], state._log)
937
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
940
# If the file is no longer new, and the clock has been moved forward
941
# sufficiently, it will cache the sha.
943
state.set_parent_trees(
944
[(with_a_id, tree.branch.repository.revision_tree(with_a_id))],
946
entry = state._get_entry(0, path_utf8='a')
948
link_or_sha1 = self.update_entry(state, entry, abspath='a',
949
stat_value=stat_value)
950
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
952
self.assertEqual([('is_exec', mode, False), ('sha1', 'a')],
954
self.assertEqual(('f', link_or_sha1, 14, False, packed_stat),
957
# Subsequent calls will just return the cached value
959
link_or_sha1 = self.update_entry(state, entry, abspath='a',
960
stat_value=stat_value)
961
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
963
self.assertEqual([], state._log)
964
self.assertEqual(('f', link_or_sha1, 14, False, packed_stat),
967
def test_update_entry_symlink(self):
968
"""Update entry should read symlinks."""
969
self.requireFeature(tests.SymlinkFeature)
970
state, entry = self.get_state_with_a()
972
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
973
state._dirblock_state)
974
os.symlink('target', 'a')
976
state.adjust_time(-10) # Make the symlink look new
977
stat_value = os.lstat('a')
978
packed_stat = dirstate.pack_stat(stat_value)
979
link_or_sha1 = self.update_entry(state, entry, abspath='a',
980
stat_value=stat_value)
981
self.assertEqual('target', link_or_sha1)
982
self.assertEqual([('read_link', 'a', '')], state._log)
983
# Dirblock is not updated (the link is too new)
984
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
986
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
987
state._dirblock_state)
989
# Because the stat_value looks new, we should re-read the target
990
link_or_sha1 = self.update_entry(state, entry, abspath='a',
991
stat_value=stat_value)
992
self.assertEqual('target', link_or_sha1)
993
self.assertEqual([('read_link', 'a', ''),
994
('read_link', 'a', ''),
996
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
998
state.adjust_time(+20) # Skip into the future, all files look old
999
link_or_sha1 = self.update_entry(state, entry, abspath='a',
1000
stat_value=stat_value)
1001
self.assertEqual('target', link_or_sha1)
1002
# We need to re-read the link because only now can we cache it
1003
self.assertEqual([('read_link', 'a', ''),
1004
('read_link', 'a', ''),
1005
('read_link', 'a', ''),
1007
self.assertEqual([('l', 'target', 6, False, packed_stat)],
1010
# Another call won't re-read the link
1011
self.assertEqual([('read_link', 'a', ''),
1012
('read_link', 'a', ''),
1013
('read_link', 'a', ''),
1015
link_or_sha1 = self.update_entry(state, entry, abspath='a',
1016
stat_value=stat_value)
1017
self.assertEqual('target', link_or_sha1)
1018
self.assertEqual([('l', 'target', 6, False, packed_stat)],
1021
def do_update_entry(self, state, entry, abspath):
1022
stat_value = os.lstat(abspath)
1023
return self.update_entry(state, entry, abspath, stat_value)
1025
def test_update_entry_dir(self):
1026
state, entry = self.get_state_with_a()
1027
self.build_tree(['a/'])
1028
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1030
def test_update_entry_dir_unchanged(self):
1031
state, entry = self.get_state_with_a()
1032
self.build_tree(['a/'])
1033
state.adjust_time(+20)
1034
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1035
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1036
state._dirblock_state)
1038
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1039
state._dirblock_state)
1040
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1041
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1042
state._dirblock_state)
1044
def test_update_entry_file_unchanged(self):
1045
state, _ = self.get_state_with_a()
1046
tree = self.make_branch_and_tree('tree')
1048
self.build_tree(['tree/a'])
1049
tree.add(['a'], ['a-id'])
1050
with_a_id = tree.commit('witha')
1051
self.addCleanup(tree.unlock)
1052
state.set_parent_trees(
1053
[(with_a_id, tree.branch.repository.revision_tree(with_a_id))],
1055
entry = state._get_entry(0, path_utf8='a')
1056
self.build_tree(['a'])
1057
sha1sum = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1058
state.adjust_time(+20)
1059
self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1060
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1061
state._dirblock_state)
1063
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1064
state._dirblock_state)
1065
self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1066
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1067
state._dirblock_state)
1069
def test_update_entry_tree_reference(self):
1070
state = test_dirstate.InstrumentedDirState.initialize('dirstate')
1071
self.addCleanup(state.unlock)
1072
state.add('r', 'r-id', 'tree-reference', None, '')
1073
self.build_tree(['r/'])
1074
entry = state._get_entry(0, path_utf8='r')
1075
self.do_update_entry(state, entry, 'r')
1076
entry = state._get_entry(0, path_utf8='r')
1077
self.assertEqual('t', entry[1][0][0])
1079
def create_and_test_file(self, state, entry):
1080
"""Create a file at 'a' and verify the state finds it during update.
1082
The state should already be versioning *something* at 'a'. This makes
1083
sure that state.update_entry recognizes it as a file.
1085
self.build_tree(['a'])
1086
stat_value = os.lstat('a')
1087
packed_stat = dirstate.pack_stat(stat_value)
1089
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1090
self.assertEqual(None, link_or_sha1)
1091
self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
1095
def create_and_test_dir(self, state, entry):
1096
"""Create a directory at 'a' and verify the state finds it.
1098
The state should already be versioning *something* at 'a'. This makes
1099
sure that state.update_entry recognizes it as a directory.
1101
self.build_tree(['a/'])
1102
stat_value = os.lstat('a')
1103
packed_stat = dirstate.pack_stat(stat_value)
1105
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1106
self.assertIs(None, link_or_sha1)
1107
self.assertEqual([('d', '', 0, False, packed_stat)], entry[1])
1111
# FIXME: Add unicode version
1112
def create_and_test_symlink(self, state, entry):
1113
"""Create a symlink at 'a' and verify the state finds it.
1115
The state should already be versioning *something* at 'a'. This makes
1116
sure that state.update_entry recognizes it as a symlink.
1118
This should not be called if this platform does not have symlink
1121
# caller should care about skipping test on platforms without symlinks
1122
os.symlink('path/to/foo', 'a')
1124
stat_value = os.lstat('a')
1125
packed_stat = dirstate.pack_stat(stat_value)
1127
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1128
self.assertEqual('path/to/foo', link_or_sha1)
1129
self.assertEqual([('l', 'path/to/foo', 11, False, packed_stat)],
1133
def test_update_file_to_dir(self):
1134
"""If a file changes to a directory we return None for the sha.
1135
We also update the inventory record.
1137
state, entry = self.get_state_with_a()
1138
# The file sha1 won't be cached unless the file is old
1139
state.adjust_time(+10)
1140
self.create_and_test_file(state, entry)
1142
self.create_and_test_dir(state, entry)
1144
def test_update_file_to_symlink(self):
1145
"""File becomes a symlink"""
1146
self.requireFeature(tests.SymlinkFeature)
1147
state, entry = self.get_state_with_a()
1148
# The file sha1 won't be cached unless the file is old
1149
state.adjust_time(+10)
1150
self.create_and_test_file(state, entry)
1152
self.create_and_test_symlink(state, entry)
1154
def test_update_dir_to_file(self):
1155
"""Directory becoming a file updates the entry."""
1156
state, entry = self.get_state_with_a()
1157
# The file sha1 won't be cached unless the file is old
1158
state.adjust_time(+10)
1159
self.create_and_test_dir(state, entry)
1161
self.create_and_test_file(state, entry)
1163
def test_update_dir_to_symlink(self):
1164
"""Directory becomes a symlink"""
1165
self.requireFeature(tests.SymlinkFeature)
1166
state, entry = self.get_state_with_a()
1167
# The symlink target won't be cached if it isn't old
1168
state.adjust_time(+10)
1169
self.create_and_test_dir(state, entry)
1171
self.create_and_test_symlink(state, entry)
1173
def test_update_symlink_to_file(self):
1174
"""Symlink becomes a file"""
1175
self.requireFeature(tests.SymlinkFeature)
1176
state, entry = self.get_state_with_a()
1177
# The symlink and file info won't be cached unless old
1178
state.adjust_time(+10)
1179
self.create_and_test_symlink(state, entry)
1181
self.create_and_test_file(state, entry)
1183
def test_update_symlink_to_dir(self):
1184
"""Symlink becomes a directory"""
1185
self.requireFeature(tests.SymlinkFeature)
1186
state, entry = self.get_state_with_a()
1187
# The symlink target won't be cached if it isn't old
1188
state.adjust_time(+10)
1189
self.create_and_test_symlink(state, entry)
1191
self.create_and_test_dir(state, entry)
1193
def test__is_executable_win32(self):
1194
state, entry = self.get_state_with_a()
1195
self.build_tree(['a'])
1197
# Make sure we are using the win32 implementation of _is_executable
1198
state._is_executable = state._is_executable_win32
1200
# The file on disk is not executable, but we are marking it as though
1201
# it is. With _is_executable_win32 we ignore what is on disk.
1202
entry[1][0] = ('f', '', 0, True, dirstate.DirState.NULLSTAT)
1204
stat_value = os.lstat('a')
1205
packed_stat = dirstate.pack_stat(stat_value)
1207
state.adjust_time(-10) # Make sure everything is new
1208
self.update_entry(state, entry, abspath='a', stat_value=stat_value)
1210
# The row is updated, but the executable bit stays set.
1211
self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1214
# Make the disk object look old enough to cache (but it won't cache the
1215
# sha as it is a new file).
1216
state.adjust_time(+20)
1217
digest = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1218
self.update_entry(state, entry, abspath='a', stat_value=stat_value)
1219
self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1222
def _prepare_tree(self):
1224
text = 'Hello World\n'
1225
tree = self.make_branch_and_tree('tree')
1226
self.build_tree_contents([('tree/a file', text)])
1227
tree.add('a file', 'a-file-id')
1228
# Note: dirstate does not sha prior to the first commit
1229
# so commit now in order for the test to work
1230
tree.commit('first')
1233
def test_sha1provider_sha1_used(self):
1234
tree, text = self._prepare_tree()
1235
state = dirstate.DirState.from_tree(tree, 'dirstate',
1236
UppercaseSHA1Provider())
1237
self.addCleanup(state.unlock)
1238
expected_sha = osutils.sha_string(text.upper() + "foo")
1239
entry = state._get_entry(0, path_utf8='a file')
1240
state._sha_cutoff_time()
1241
state._cutoff_time += 10
1242
sha1 = self.update_entry(state, entry, 'tree/a file',
1243
os.lstat('tree/a file'))
1244
self.assertEqual(expected_sha, sha1)
1246
def test_sha1provider_stat_and_sha1_used(self):
1247
tree, text = self._prepare_tree()
1249
self.addCleanup(tree.unlock)
1250
state = tree._current_dirstate()
1251
state._sha1_provider = UppercaseSHA1Provider()
1252
# If we used the standard provider, it would look like nothing has
1254
file_ids_changed = [change[0] for change
1255
in tree.iter_changes(tree.basis_tree())]
1256
self.assertEqual(['a-file-id'], file_ids_changed)
1259
class UppercaseSHA1Provider(dirstate.SHA1Provider):
1260
"""A custom SHA1Provider."""
1262
def sha1(self, abspath):
1263
return self.stat_and_sha1(abspath)[1]
1265
def stat_and_sha1(self, abspath):
1266
file_obj = file(abspath, 'rb')
1268
statvalue = os.fstat(file_obj.fileno())
1269
text = ''.join(file_obj.readlines())
1270
sha1 = osutils.sha_string(text.upper() + "foo")
1273
return statvalue, sha1
1276
class TestProcessEntry(test_dirstate.TestCaseWithDirState):
1279
_process_entry = None
1282
super(TestProcessEntry, self).setUp()
1283
orig = dirstate._process_entry
1285
dirstate._process_entry = orig
1286
self.addCleanup(cleanup)
1287
dirstate._process_entry = self._process_entry
1289
def assertChangedFileIds(self, expected, tree):
1292
file_ids = [info[0] for info
1293
in tree.iter_changes(tree.basis_tree())]
1296
self.assertEqual(sorted(expected), sorted(file_ids))
1298
def test_exceptions_raised(self):
1299
# This is a direct test of bug #495023, it relies on osutils.is_inside
1300
# getting called in an inner function. Which makes it a bit brittle,
1301
# but at least it does reproduce the bug.
1302
def is_inside_raises(*args, **kwargs):
1303
raise RuntimeError('stop this')
1304
tree = self.make_branch_and_tree('tree')
1305
self.build_tree(['tree/file', 'tree/dir/', 'tree/dir/sub',
1306
'tree/dir2/', 'tree/dir2/sub2'])
1307
tree.add(['file', 'dir', 'dir/sub', 'dir2', 'dir2/sub2'])
1308
tree.commit('first commit')
1310
self.addCleanup(tree.unlock)
1311
basis_tree = tree.basis_tree()
1312
orig = osutils.is_inside
1313
self.addCleanup(setattr, osutils, 'is_inside', orig)
1314
osutils.is_inside = is_inside_raises
1315
self.assertListRaises(RuntimeError, tree.iter_changes, basis_tree)
1317
def test_simple_changes(self):
1318
tree = self.make_branch_and_tree('tree')
1319
self.build_tree(['tree/file'])
1320
tree.add(['file'], ['file-id'])
1321
self.assertChangedFileIds([tree.get_root_id(), 'file-id'], tree)
1323
self.assertChangedFileIds([], tree)
1325
def test_sha1provider_stat_and_sha1_used(self):
1326
tree = self.make_branch_and_tree('tree')
1327
self.build_tree(['tree/file'])
1328
tree.add(['file'], ['file-id'])
1331
self.addCleanup(tree.unlock)
1332
state = tree._current_dirstate()
1333
state._sha1_provider = UppercaseSHA1Provider()
1334
self.assertChangedFileIds(['file-id'], tree)