1
# Copyright (C) 2007-2011 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 (
32
from bzrlib.tests.test_osutils import dir_reader_scenarios
33
from bzrlib.tests.scenarios import (
34
load_tests_apply_scenarios,
39
load_tests = load_tests_apply_scenarios
42
compiled_dirstate_helpers_feature = tests.ModuleAvailableFeature(
43
'bzrlib._dirstate_helpers_pyx')
46
# FIXME: we should also parametrize against SHA1Provider !
48
ue_scenarios = [('dirstate_Python',
49
{'update_entry': dirstate.py_update_entry})]
50
if compiled_dirstate_helpers_feature.available():
51
update_entry = compiled_dirstate_helpers_feature.module.update_entry
52
ue_scenarios.append(('dirstate_Pyrex', {'update_entry': update_entry}))
54
pe_scenarios = [('dirstate_Python',
55
{'_process_entry': dirstate.ProcessEntryPython})]
56
if compiled_dirstate_helpers_feature.available():
57
process_entry = compiled_dirstate_helpers_feature.module.ProcessEntryC
58
pe_scenarios.append(('dirstate_Pyrex', {'_process_entry': process_entry}))
61
class TestBisectPathMixin(object):
62
"""Test that _bisect_path_*() returns the expected values.
64
_bisect_path_* is intended to work like bisect.bisect_*() except it
65
knows it is working on paths that are sorted by ('path', 'to', 'foo')
66
chunks rather than by raw 'path/to/foo'.
68
Test Cases should inherit from this and override ``get_bisect_path`` return
69
their implementation, and ``get_bisect`` to return the matching
70
bisect.bisect_* function.
73
def get_bisect_path(self):
74
"""Return an implementation of _bisect_path_*"""
75
raise NotImplementedError
78
"""Return a version of bisect.bisect_*.
80
Also, for the 'exists' check, return the offset to the real values.
81
For example bisect_left returns the index of an entry, while
82
bisect_right returns the index *after* an entry
84
:return: (bisect_func, offset)
86
raise NotImplementedError
88
def assertBisect(self, paths, split_paths, path, exists=True):
89
"""Assert that bisect_split works like bisect_left on the split paths.
91
:param paths: A list of path names
92
:param split_paths: A list of path names that are already split up by directory
93
('path/to/foo' => ('path', 'to', 'foo'))
94
:param path: The path we are indexing.
95
:param exists: The path should be present, so make sure the
96
final location actually points to the right value.
98
All other arguments will be passed along.
100
bisect_path = self.get_bisect_path()
101
self.assertIsInstance(paths, list)
102
bisect_path_idx = bisect_path(paths, path)
103
split_path = self.split_for_dirblocks([path])[0]
104
bisect_func, offset = self.get_bisect()
105
bisect_split_idx = bisect_func(split_paths, split_path)
106
self.assertEqual(bisect_split_idx, bisect_path_idx,
107
'%s disagreed. %s != %s'
109
% (bisect_path.__name__,
110
bisect_split_idx, bisect_path_idx, path)
113
self.assertEqual(path, paths[bisect_path_idx+offset])
115
def split_for_dirblocks(self, paths):
118
dirname, basename = os.path.split(path)
119
dir_split_paths.append((dirname.split('/'), basename))
120
dir_split_paths.sort()
121
return dir_split_paths
123
def test_simple(self):
124
"""In the simple case it works just like bisect_left"""
125
paths = ['', 'a', 'b', 'c', 'd']
126
split_paths = self.split_for_dirblocks(paths)
128
self.assertBisect(paths, split_paths, path, exists=True)
129
self.assertBisect(paths, split_paths, '_', exists=False)
130
self.assertBisect(paths, split_paths, 'aa', exists=False)
131
self.assertBisect(paths, split_paths, 'bb', exists=False)
132
self.assertBisect(paths, split_paths, 'cc', exists=False)
133
self.assertBisect(paths, split_paths, 'dd', exists=False)
134
self.assertBisect(paths, split_paths, 'a/a', exists=False)
135
self.assertBisect(paths, split_paths, 'b/b', exists=False)
136
self.assertBisect(paths, split_paths, 'c/c', exists=False)
137
self.assertBisect(paths, split_paths, 'd/d', exists=False)
139
def test_involved(self):
140
"""This is where bisect_path_* diverges slightly."""
141
# This is the list of paths and their contents
169
# This is the exact order that is stored by dirstate
170
# All children in a directory are mentioned before an children of
171
# children are mentioned.
172
# So all the root-directory paths, then all the
173
# first sub directory, etc.
174
paths = [# content of '/'
175
'', 'a', 'a-a', 'a-z', 'a=a', 'a=z',
177
'a/a', 'a/a-a', 'a/a-z',
179
'a/z', 'a/z-a', 'a/z-z',
202
split_paths = self.split_for_dirblocks(paths)
204
for dir_parts, basename in split_paths:
205
if dir_parts == ['']:
206
sorted_paths.append(basename)
208
sorted_paths.append('/'.join(dir_parts + [basename]))
210
self.assertEqual(sorted_paths, paths)
213
self.assertBisect(paths, split_paths, path, exists=True)
216
class TestBisectPathLeft(tests.TestCase, TestBisectPathMixin):
217
"""Run all Bisect Path tests against _bisect_path_left."""
219
def get_bisect_path(self):
220
from bzrlib._dirstate_helpers_py import _bisect_path_left
221
return _bisect_path_left
223
def get_bisect(self):
224
return bisect.bisect_left, 0
227
class TestCompiledBisectPathLeft(TestBisectPathLeft):
228
"""Run all Bisect Path tests against _bisect_path_lect"""
230
_test_needs_features = [compiled_dirstate_helpers_feature]
232
def get_bisect_path(self):
233
from bzrlib._dirstate_helpers_pyx import _bisect_path_left
234
return _bisect_path_left
237
class TestBisectPathRight(tests.TestCase, TestBisectPathMixin):
238
"""Run all Bisect Path tests against _bisect_path_right"""
240
def get_bisect_path(self):
241
from bzrlib._dirstate_helpers_py import _bisect_path_right
242
return _bisect_path_right
244
def get_bisect(self):
245
return bisect.bisect_right, -1
248
class TestCompiledBisectPathRight(TestBisectPathRight):
249
"""Run all Bisect Path tests against _bisect_path_right"""
251
_test_needs_features = [compiled_dirstate_helpers_feature]
253
def get_bisect_path(self):
254
from bzrlib._dirstate_helpers_pyx import _bisect_path_right
255
return _bisect_path_right
258
class TestBisectDirblock(tests.TestCase):
259
"""Test that bisect_dirblock() returns the expected values.
261
bisect_dirblock is intended to work like bisect.bisect_left() except it
262
knows it is working on dirblocks and that dirblocks are sorted by ('path',
263
'to', 'foo') chunks rather than by raw 'path/to/foo'.
265
This test is parameterized by calling get_bisect_dirblock(). Child test
266
cases can override this function to test against a different
270
def get_bisect_dirblock(self):
271
"""Return an implementation of bisect_dirblock"""
272
from bzrlib._dirstate_helpers_py import bisect_dirblock
273
return bisect_dirblock
275
def assertBisect(self, dirblocks, split_dirblocks, path, *args, **kwargs):
276
"""Assert that bisect_split works like bisect_left on the split paths.
278
:param dirblocks: A list of (path, [info]) pairs.
279
:param split_dirblocks: A list of ((split, path), [info]) pairs.
280
:param path: The path we are indexing.
282
All other arguments will be passed along.
284
bisect_dirblock = self.get_bisect_dirblock()
285
self.assertIsInstance(dirblocks, list)
286
bisect_split_idx = bisect_dirblock(dirblocks, path, *args, **kwargs)
287
split_dirblock = (path.split('/'), [])
288
bisect_left_idx = bisect.bisect_left(split_dirblocks, split_dirblock,
290
self.assertEqual(bisect_left_idx, bisect_split_idx,
291
'bisect_split disagreed. %s != %s'
293
% (bisect_left_idx, bisect_split_idx, path)
296
def paths_to_dirblocks(self, paths):
297
"""Convert a list of paths into dirblock form.
299
Also, ensure that the paths are in proper sorted order.
301
dirblocks = [(path, []) for path in paths]
302
split_dirblocks = [(path.split('/'), []) for path in paths]
303
self.assertEqual(sorted(split_dirblocks), split_dirblocks)
304
return dirblocks, split_dirblocks
306
def test_simple(self):
307
"""In the simple case it works just like bisect_left"""
308
paths = ['', 'a', 'b', 'c', 'd']
309
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
311
self.assertBisect(dirblocks, split_dirblocks, path)
312
self.assertBisect(dirblocks, split_dirblocks, '_')
313
self.assertBisect(dirblocks, split_dirblocks, 'aa')
314
self.assertBisect(dirblocks, split_dirblocks, 'bb')
315
self.assertBisect(dirblocks, split_dirblocks, 'cc')
316
self.assertBisect(dirblocks, split_dirblocks, 'dd')
317
self.assertBisect(dirblocks, split_dirblocks, 'a/a')
318
self.assertBisect(dirblocks, split_dirblocks, 'b/b')
319
self.assertBisect(dirblocks, split_dirblocks, 'c/c')
320
self.assertBisect(dirblocks, split_dirblocks, 'd/d')
322
def test_involved(self):
323
"""This is where bisect_left diverges slightly."""
325
'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
326
'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
328
'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
329
'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
332
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
334
self.assertBisect(dirblocks, split_dirblocks, path)
336
def test_involved_cached(self):
337
"""This is where bisect_left diverges slightly."""
339
'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
340
'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
342
'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
343
'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
347
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
349
self.assertBisect(dirblocks, split_dirblocks, path, cache=cache)
352
class TestCompiledBisectDirblock(TestBisectDirblock):
353
"""Test that bisect_dirblock() returns the expected values.
355
bisect_dirblock is intended to work like bisect.bisect_left() except it
356
knows it is working on dirblocks and that dirblocks are sorted by ('path',
357
'to', 'foo') chunks rather than by raw 'path/to/foo'.
359
This runs all the normal tests that TestBisectDirblock did, but uses the
363
_test_needs_features = [compiled_dirstate_helpers_feature]
365
def get_bisect_dirblock(self):
366
from bzrlib._dirstate_helpers_pyx import bisect_dirblock
367
return bisect_dirblock
370
class TestCmpByDirs(tests.TestCase):
371
"""Test an implementation of cmp_by_dirs()
373
cmp_by_dirs() compares 2 paths by their directory sections, rather than as
376
Child test cases can override ``get_cmp_by_dirs`` to test a specific
380
def get_cmp_by_dirs(self):
381
"""Get a specific implementation of cmp_by_dirs."""
382
from bzrlib._dirstate_helpers_py import cmp_by_dirs
385
def assertCmpByDirs(self, expected, str1, str2):
386
"""Compare the two strings, in both directions.
388
:param expected: The expected comparison value. -1 means str1 comes
389
first, 0 means they are equal, 1 means str2 comes first
390
:param str1: string to compare
391
:param str2: string to compare
393
cmp_by_dirs = self.get_cmp_by_dirs()
395
self.assertEqual(str1, str2)
396
self.assertEqual(0, cmp_by_dirs(str1, str2))
397
self.assertEqual(0, cmp_by_dirs(str2, str1))
399
self.assertPositive(cmp_by_dirs(str1, str2))
400
self.assertNegative(cmp_by_dirs(str2, str1))
402
self.assertNegative(cmp_by_dirs(str1, str2))
403
self.assertPositive(cmp_by_dirs(str2, str1))
405
def test_cmp_empty(self):
406
"""Compare against the empty string."""
407
self.assertCmpByDirs(0, '', '')
408
self.assertCmpByDirs(1, 'a', '')
409
self.assertCmpByDirs(1, 'ab', '')
410
self.assertCmpByDirs(1, 'abc', '')
411
self.assertCmpByDirs(1, 'abcd', '')
412
self.assertCmpByDirs(1, 'abcde', '')
413
self.assertCmpByDirs(1, 'abcdef', '')
414
self.assertCmpByDirs(1, 'abcdefg', '')
415
self.assertCmpByDirs(1, 'abcdefgh', '')
416
self.assertCmpByDirs(1, 'abcdefghi', '')
417
self.assertCmpByDirs(1, 'test/ing/a/path/', '')
419
def test_cmp_same_str(self):
420
"""Compare the same string"""
421
self.assertCmpByDirs(0, 'a', 'a')
422
self.assertCmpByDirs(0, 'ab', 'ab')
423
self.assertCmpByDirs(0, 'abc', 'abc')
424
self.assertCmpByDirs(0, 'abcd', 'abcd')
425
self.assertCmpByDirs(0, 'abcde', 'abcde')
426
self.assertCmpByDirs(0, 'abcdef', 'abcdef')
427
self.assertCmpByDirs(0, 'abcdefg', 'abcdefg')
428
self.assertCmpByDirs(0, 'abcdefgh', 'abcdefgh')
429
self.assertCmpByDirs(0, 'abcdefghi', 'abcdefghi')
430
self.assertCmpByDirs(0, 'testing a long string', 'testing a long string')
431
self.assertCmpByDirs(0, 'x'*10000, 'x'*10000)
432
self.assertCmpByDirs(0, 'a/b', 'a/b')
433
self.assertCmpByDirs(0, 'a/b/c', 'a/b/c')
434
self.assertCmpByDirs(0, 'a/b/c/d', 'a/b/c/d')
435
self.assertCmpByDirs(0, 'a/b/c/d/e', 'a/b/c/d/e')
437
def test_simple_paths(self):
438
"""Compare strings that act like normal string comparison"""
439
self.assertCmpByDirs(-1, 'a', 'b')
440
self.assertCmpByDirs(-1, 'aa', 'ab')
441
self.assertCmpByDirs(-1, 'ab', 'bb')
442
self.assertCmpByDirs(-1, 'aaa', 'aab')
443
self.assertCmpByDirs(-1, 'aab', 'abb')
444
self.assertCmpByDirs(-1, 'abb', 'bbb')
445
self.assertCmpByDirs(-1, 'aaaa', 'aaab')
446
self.assertCmpByDirs(-1, 'aaab', 'aabb')
447
self.assertCmpByDirs(-1, 'aabb', 'abbb')
448
self.assertCmpByDirs(-1, 'abbb', 'bbbb')
449
self.assertCmpByDirs(-1, 'aaaaa', 'aaaab')
450
self.assertCmpByDirs(-1, 'a/a', 'a/b')
451
self.assertCmpByDirs(-1, 'a/b', 'b/b')
452
self.assertCmpByDirs(-1, 'a/a/a', 'a/a/b')
453
self.assertCmpByDirs(-1, 'a/a/b', 'a/b/b')
454
self.assertCmpByDirs(-1, 'a/b/b', 'b/b/b')
455
self.assertCmpByDirs(-1, 'a/a/a/a', 'a/a/a/b')
456
self.assertCmpByDirs(-1, 'a/a/a/b', 'a/a/b/b')
457
self.assertCmpByDirs(-1, 'a/a/b/b', 'a/b/b/b')
458
self.assertCmpByDirs(-1, 'a/b/b/b', 'b/b/b/b')
459
self.assertCmpByDirs(-1, 'a/a/a/a/a', 'a/a/a/a/b')
461
def test_tricky_paths(self):
462
self.assertCmpByDirs(1, 'ab/cd/ef', 'ab/cc/ef')
463
self.assertCmpByDirs(1, 'ab/cd/ef', 'ab/c/ef')
464
self.assertCmpByDirs(-1, 'ab/cd/ef', 'ab/cd-ef')
465
self.assertCmpByDirs(-1, 'ab/cd', 'ab/cd-')
466
self.assertCmpByDirs(-1, 'ab/cd', 'ab-cd')
468
def test_cmp_unicode_not_allowed(self):
469
cmp_by_dirs = self.get_cmp_by_dirs()
470
self.assertRaises(TypeError, cmp_by_dirs, u'Unicode', 'str')
471
self.assertRaises(TypeError, cmp_by_dirs, 'str', u'Unicode')
472
self.assertRaises(TypeError, cmp_by_dirs, u'Unicode', u'Unicode')
474
def test_cmp_non_ascii(self):
475
self.assertCmpByDirs(-1, '\xc2\xb5', '\xc3\xa5') # u'\xb5', u'\xe5'
476
self.assertCmpByDirs(-1, 'a', '\xc3\xa5') # u'a', u'\xe5'
477
self.assertCmpByDirs(-1, 'b', '\xc2\xb5') # u'b', u'\xb5'
478
self.assertCmpByDirs(-1, 'a/b', 'a/\xc3\xa5') # u'a/b', u'a/\xe5'
479
self.assertCmpByDirs(-1, 'b/a', 'b/\xc2\xb5') # u'b/a', u'b/\xb5'
482
class TestCompiledCmpByDirs(TestCmpByDirs):
483
"""Test the pyrex implementation of cmp_by_dirs"""
485
_test_needs_features = [compiled_dirstate_helpers_feature]
487
def get_cmp_by_dirs(self):
488
from bzrlib._dirstate_helpers_pyx import cmp_by_dirs
492
class TestCmpPathByDirblock(tests.TestCase):
493
"""Test an implementation of _cmp_path_by_dirblock()
495
_cmp_path_by_dirblock() compares two paths using the sort order used by
496
DirState. All paths in the same directory are sorted together.
498
Child test cases can override ``get_cmp_path_by_dirblock`` to test a specific
502
def get_cmp_path_by_dirblock(self):
503
"""Get a specific implementation of _cmp_path_by_dirblock."""
504
from bzrlib._dirstate_helpers_py import _cmp_path_by_dirblock
505
return _cmp_path_by_dirblock
507
def assertCmpPathByDirblock(self, paths):
508
"""Compare all paths and make sure they evaluate to the correct order.
510
This does N^2 comparisons. It is assumed that ``paths`` is properly
513
:param paths: a sorted list of paths to compare
515
# First, make sure the paths being passed in are correct
517
dirname, basename = os.path.split(p)
518
return dirname.split('/'), basename
519
self.assertEqual(sorted(paths, key=_key), paths)
521
cmp_path_by_dirblock = self.get_cmp_path_by_dirblock()
522
for idx1, path1 in enumerate(paths):
523
for idx2, path2 in enumerate(paths):
524
cmp_val = cmp_path_by_dirblock(path1, path2)
526
self.assertTrue(cmp_val < 0,
527
'%s did not state that %r came before %r, cmp=%s'
528
% (cmp_path_by_dirblock.__name__,
529
path1, path2, cmp_val))
531
self.assertTrue(cmp_val > 0,
532
'%s did not state that %r came after %r, cmp=%s'
533
% (cmp_path_by_dirblock.__name__,
534
path1, path2, cmp_val))
536
self.assertTrue(cmp_val == 0,
537
'%s did not state that %r == %r, cmp=%s'
538
% (cmp_path_by_dirblock.__name__,
539
path1, path2, cmp_val))
541
def test_cmp_simple_paths(self):
542
"""Compare against the empty string."""
543
self.assertCmpPathByDirblock(['', 'a', 'ab', 'abc', 'a/b/c', 'b/d/e'])
544
self.assertCmpPathByDirblock(['kl', 'ab/cd', 'ab/ef', 'gh/ij'])
546
def test_tricky_paths(self):
547
self.assertCmpPathByDirblock([
549
'', 'a', 'a-a', 'a=a', 'b',
551
'a/a', 'a/a-a', 'a/a=a', 'a/b',
553
'a/a/a', 'a/a/a-a', 'a/a/a=a',
554
# Contents of 'a/a/a'
555
'a/a/a/a', 'a/a/a/b',
556
# Contents of 'a/a/a-a',
557
'a/a/a-a/a', 'a/a/a-a/b',
558
# Contents of 'a/a/a=a',
559
'a/a/a=a/a', 'a/a/a=a/b',
560
# Contents of 'a/a-a'
562
# Contents of 'a/a-a/a'
563
'a/a-a/a/a', 'a/a-a/a/b',
564
# Contents of 'a/a=a'
575
self.assertCmpPathByDirblock([
577
'', 'a', 'a-a', 'a-z', 'a=a', 'a=z',
579
'a/a', 'a/a-a', 'a/a-z',
581
'a/z', 'a/z-a', 'a/z-z',
605
def test_unicode_not_allowed(self):
606
cmp_path_by_dirblock = self.get_cmp_path_by_dirblock()
607
self.assertRaises(TypeError, cmp_path_by_dirblock, u'Uni', 'str')
608
self.assertRaises(TypeError, cmp_path_by_dirblock, 'str', u'Uni')
609
self.assertRaises(TypeError, cmp_path_by_dirblock, u'Uni', u'Uni')
610
self.assertRaises(TypeError, cmp_path_by_dirblock, u'x/Uni', 'x/str')
611
self.assertRaises(TypeError, cmp_path_by_dirblock, 'x/str', u'x/Uni')
612
self.assertRaises(TypeError, cmp_path_by_dirblock, u'x/Uni', u'x/Uni')
614
def test_nonascii(self):
615
self.assertCmpPathByDirblock([
617
'', 'a', '\xc2\xb5', '\xc3\xa5',
619
'a/a', 'a/\xc2\xb5', 'a/\xc3\xa5',
621
'a/a/a', 'a/a/\xc2\xb5', 'a/a/\xc3\xa5',
622
# content of 'a/\xc2\xb5'
623
'a/\xc2\xb5/a', 'a/\xc2\xb5/\xc2\xb5', 'a/\xc2\xb5/\xc3\xa5',
624
# content of 'a/\xc3\xa5'
625
'a/\xc3\xa5/a', 'a/\xc3\xa5/\xc2\xb5', 'a/\xc3\xa5/\xc3\xa5',
626
# content of '\xc2\xb5'
627
'\xc2\xb5/a', '\xc2\xb5/\xc2\xb5', '\xc2\xb5/\xc3\xa5',
628
# content of '\xc2\xe5'
629
'\xc3\xa5/a', '\xc3\xa5/\xc2\xb5', '\xc3\xa5/\xc3\xa5',
633
class TestCompiledCmpPathByDirblock(TestCmpPathByDirblock):
634
"""Test the pyrex implementation of _cmp_path_by_dirblock"""
636
_test_needs_features = [compiled_dirstate_helpers_feature]
638
def get_cmp_by_dirs(self):
639
from bzrlib._dirstate_helpers_pyx import _cmp_path_by_dirblock
640
return _cmp_path_by_dirblock
643
class TestMemRChr(tests.TestCase):
644
"""Test memrchr functionality"""
646
_test_needs_features = [compiled_dirstate_helpers_feature]
648
def assertMemRChr(self, expected, s, c):
649
from bzrlib._dirstate_helpers_pyx import _py_memrchr
650
self.assertEqual(expected, _py_memrchr(s, c))
652
def test_missing(self):
653
self.assertMemRChr(None, '', 'a')
654
self.assertMemRChr(None, '', 'c')
655
self.assertMemRChr(None, 'abcdefghijklm', 'q')
656
self.assertMemRChr(None, 'aaaaaaaaaaaaaaaaaaaaaaa', 'b')
658
def test_single_entry(self):
659
self.assertMemRChr(0, 'abcdefghijklm', 'a')
660
self.assertMemRChr(1, 'abcdefghijklm', 'b')
661
self.assertMemRChr(2, 'abcdefghijklm', 'c')
662
self.assertMemRChr(10, 'abcdefghijklm', 'k')
663
self.assertMemRChr(11, 'abcdefghijklm', 'l')
664
self.assertMemRChr(12, 'abcdefghijklm', 'm')
666
def test_multiple(self):
667
self.assertMemRChr(10, 'abcdefjklmabcdefghijklm', 'a')
668
self.assertMemRChr(11, 'abcdefjklmabcdefghijklm', 'b')
669
self.assertMemRChr(12, 'abcdefjklmabcdefghijklm', 'c')
670
self.assertMemRChr(20, 'abcdefjklmabcdefghijklm', 'k')
671
self.assertMemRChr(21, 'abcdefjklmabcdefghijklm', 'l')
672
self.assertMemRChr(22, 'abcdefjklmabcdefghijklm', 'm')
673
self.assertMemRChr(22, 'aaaaaaaaaaaaaaaaaaaaaaa', 'a')
675
def test_with_nulls(self):
676
self.assertMemRChr(10, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'a')
677
self.assertMemRChr(11, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'b')
678
self.assertMemRChr(12, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'c')
679
self.assertMemRChr(20, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'k')
680
self.assertMemRChr(21, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'l')
681
self.assertMemRChr(22, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'm')
682
self.assertMemRChr(22, 'aaa\0\0\0aaaaaaa\0\0\0aaaaaaa', 'a')
683
self.assertMemRChr(9, '\0\0\0\0\0\0\0\0\0\0', '\0')
686
class TestReadDirblocks(test_dirstate.TestCaseWithDirState):
687
"""Test an implementation of _read_dirblocks()
689
_read_dirblocks() reads in all of the dirblock information from the disk
692
Child test cases can override ``get_read_dirblocks`` to test a specific
696
# inherits scenarios from test_dirstate
698
def get_read_dirblocks(self):
699
from bzrlib._dirstate_helpers_py import _read_dirblocks
700
return _read_dirblocks
702
def test_smoketest(self):
703
"""Make sure that we can create and read back a simple file."""
704
tree, state, expected = self.create_basic_dirstate()
706
state._read_header_if_needed()
707
self.assertEqual(dirstate.DirState.NOT_IN_MEMORY,
708
state._dirblock_state)
709
read_dirblocks = self.get_read_dirblocks()
710
read_dirblocks(state)
711
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
712
state._dirblock_state)
714
def test_trailing_garbage(self):
715
tree, state, expected = self.create_basic_dirstate()
716
# On Unix, we can write extra data as long as we haven't read yet, but
717
# on Win32, if you've opened the file with FILE_SHARE_READ, trying to
718
# open it in append mode will fail.
720
f = open('dirstate', 'ab')
722
# Add bogus trailing garbage
727
e = self.assertRaises(errors.DirstateCorrupt,
728
state._read_dirblocks_if_needed)
729
# Make sure we mention the bogus characters in the error
730
self.assertContainsRe(str(e), 'bogus')
733
class TestCompiledReadDirblocks(TestReadDirblocks):
734
"""Test the pyrex implementation of _read_dirblocks"""
736
_test_needs_features = [compiled_dirstate_helpers_feature]
738
def get_read_dirblocks(self):
739
from bzrlib._dirstate_helpers_pyx import _read_dirblocks
740
return _read_dirblocks
743
class TestUsingCompiledIfAvailable(tests.TestCase):
744
"""Check that any compiled functions that are available are the default.
746
It is possible to have typos, etc in the import line, such that
747
_dirstate_helpers_pyx is actually available, but the compiled functions are
751
def test_bisect_dirblock(self):
752
if compiled_dirstate_helpers_feature.available():
753
from bzrlib._dirstate_helpers_pyx import bisect_dirblock
755
from bzrlib._dirstate_helpers_py import bisect_dirblock
756
self.assertIs(bisect_dirblock, dirstate.bisect_dirblock)
758
def test__bisect_path_left(self):
759
if compiled_dirstate_helpers_feature.available():
760
from bzrlib._dirstate_helpers_pyx import _bisect_path_left
762
from bzrlib._dirstate_helpers_py import _bisect_path_left
763
self.assertIs(_bisect_path_left, dirstate._bisect_path_left)
765
def test__bisect_path_right(self):
766
if compiled_dirstate_helpers_feature.available():
767
from bzrlib._dirstate_helpers_pyx import _bisect_path_right
769
from bzrlib._dirstate_helpers_py import _bisect_path_right
770
self.assertIs(_bisect_path_right, dirstate._bisect_path_right)
772
def test_cmp_by_dirs(self):
773
if compiled_dirstate_helpers_feature.available():
774
from bzrlib._dirstate_helpers_pyx import cmp_by_dirs
776
from bzrlib._dirstate_helpers_py import cmp_by_dirs
777
self.assertIs(cmp_by_dirs, dirstate.cmp_by_dirs)
779
def test__read_dirblocks(self):
780
if compiled_dirstate_helpers_feature.available():
781
from bzrlib._dirstate_helpers_pyx import _read_dirblocks
783
from bzrlib._dirstate_helpers_py import _read_dirblocks
784
self.assertIs(_read_dirblocks, dirstate._read_dirblocks)
786
def test_update_entry(self):
787
if compiled_dirstate_helpers_feature.available():
788
from bzrlib._dirstate_helpers_pyx import update_entry
790
from bzrlib.dirstate import update_entry
791
self.assertIs(update_entry, dirstate.update_entry)
793
def test_process_entry(self):
794
if compiled_dirstate_helpers_feature.available():
795
from bzrlib._dirstate_helpers_pyx import ProcessEntryC
796
self.assertIs(ProcessEntryC, dirstate._process_entry)
798
from bzrlib.dirstate import ProcessEntryPython
799
self.assertIs(ProcessEntryPython, dirstate._process_entry)
802
class TestUpdateEntry(test_dirstate.TestCaseWithDirState):
803
"""Test the DirState.update_entry functions"""
805
scenarios = multiply_scenarios(
806
dir_reader_scenarios(), ue_scenarios)
812
super(TestUpdateEntry, self).setUp()
813
self.overrideAttr(dirstate, 'update_entry', self.update_entry)
815
def get_state_with_a(self):
816
"""Create a DirState tracking a single object named 'a'"""
817
state = test_dirstate.InstrumentedDirState.initialize('dirstate')
818
self.addCleanup(state.unlock)
819
state.add('a', 'a-id', 'file', None, '')
820
entry = state._get_entry(0, path_utf8='a')
823
def test_observed_sha1_cachable(self):
824
state, entry = self.get_state_with_a()
825
atime = time.time() - 10
826
self.build_tree(['a'])
827
statvalue = os.lstat('a')
828
statvalue = test_dirstate._FakeStat(statvalue.st_size, atime, atime,
829
statvalue.st_dev, statvalue.st_ino, statvalue.st_mode)
830
state._observed_sha1(entry, "foo", statvalue)
831
self.assertEqual('foo', entry[1][0][1])
832
packed_stat = dirstate.pack_stat(statvalue)
833
self.assertEqual(packed_stat, entry[1][0][4])
835
def test_observed_sha1_not_cachable(self):
836
state, entry = self.get_state_with_a()
837
oldval = entry[1][0][1]
838
oldstat = entry[1][0][4]
839
self.build_tree(['a'])
840
statvalue = os.lstat('a')
841
state._observed_sha1(entry, "foo", statvalue)
842
self.assertEqual(oldval, entry[1][0][1])
843
self.assertEqual(oldstat, entry[1][0][4])
845
def test_update_entry(self):
846
state, _ = self.get_state_with_a()
847
tree = self.make_branch_and_tree('tree')
849
empty_revid = tree.commit('empty')
850
self.build_tree(['tree/a'])
851
tree.add(['a'], ['a-id'])
852
with_a_id = tree.commit('with_a')
853
self.addCleanup(tree.unlock)
854
state.set_parent_trees(
855
[(empty_revid, tree.branch.repository.revision_tree(empty_revid))],
857
entry = state._get_entry(0, path_utf8='a')
858
self.build_tree(['a'])
859
# Add one where we don't provide the stat or sha already
860
self.assertEqual(('', 'a', 'a-id'), entry[0])
861
self.assertEqual(('f', '', 0, False, dirstate.DirState.NULLSTAT),
863
# Flush the buffers to disk
865
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
866
state._dirblock_state)
868
stat_value = os.lstat('a')
869
packed_stat = dirstate.pack_stat(stat_value)
870
link_or_sha1 = self.update_entry(state, entry, abspath='a',
871
stat_value=stat_value)
872
self.assertEqual(None, link_or_sha1)
874
# The dirblock entry should not have computed or cached the file's
875
# sha1, but it did update the files' st_size. However, this is not
876
# worth writing a dirstate file for, so we leave the state UNMODIFIED
877
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
879
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
880
state._dirblock_state)
881
mode = stat_value.st_mode
882
self.assertEqual([('is_exec', mode, False)], state._log)
885
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
886
state._dirblock_state)
888
# Roll the clock back so the file is guaranteed to look too new. We
889
# should still not compute the sha1.
890
state.adjust_time(-10)
893
link_or_sha1 = self.update_entry(state, entry, abspath='a',
894
stat_value=stat_value)
895
self.assertEqual([('is_exec', mode, False)], state._log)
896
self.assertEqual(None, link_or_sha1)
897
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
898
state._dirblock_state)
899
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
903
# If it is cachable (the clock has moved forward) but new it still
904
# won't calculate the sha or cache it.
905
state.adjust_time(+20)
907
link_or_sha1 = dirstate.update_entry(state, entry, abspath='a',
908
stat_value=stat_value)
909
self.assertEqual(None, link_or_sha1)
910
self.assertEqual([('is_exec', mode, False)], state._log)
911
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
913
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
914
state._dirblock_state)
916
# If the file is no longer new, and the clock has been moved forward
917
# sufficiently, it will cache the sha.
919
state.set_parent_trees(
920
[(with_a_id, tree.branch.repository.revision_tree(with_a_id))],
922
entry = state._get_entry(0, path_utf8='a')
924
link_or_sha1 = self.update_entry(state, entry, abspath='a',
925
stat_value=stat_value)
926
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
928
self.assertEqual([('is_exec', mode, False), ('sha1', 'a')],
930
self.assertEqual(('f', link_or_sha1, 14, False, packed_stat),
933
# Subsequent calls will just return the cached value
935
link_or_sha1 = self.update_entry(state, entry, abspath='a',
936
stat_value=stat_value)
937
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
939
self.assertEqual([], state._log)
940
self.assertEqual(('f', link_or_sha1, 14, False, packed_stat),
943
def test_update_entry_symlink(self):
944
"""Update entry should read symlinks."""
945
self.requireFeature(tests.SymlinkFeature)
946
state, entry = self.get_state_with_a()
948
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
949
state._dirblock_state)
950
os.symlink('target', 'a')
952
state.adjust_time(-10) # Make the symlink look new
953
stat_value = os.lstat('a')
954
packed_stat = dirstate.pack_stat(stat_value)
955
link_or_sha1 = self.update_entry(state, entry, abspath='a',
956
stat_value=stat_value)
957
self.assertEqual('target', link_or_sha1)
958
self.assertEqual([('read_link', 'a', '')], state._log)
959
# Dirblock is not updated (the link is too new)
960
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
962
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
963
state._dirblock_state)
965
# Because the stat_value looks new, we should re-read the target
966
link_or_sha1 = self.update_entry(state, entry, abspath='a',
967
stat_value=stat_value)
968
self.assertEqual('target', link_or_sha1)
969
self.assertEqual([('read_link', 'a', ''),
970
('read_link', 'a', ''),
972
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
974
state.adjust_time(+20) # Skip into the future, all files look old
975
link_or_sha1 = self.update_entry(state, entry, abspath='a',
976
stat_value=stat_value)
977
self.assertEqual('target', link_or_sha1)
978
# We need to re-read the link because only now can we cache it
979
self.assertEqual([('read_link', 'a', ''),
980
('read_link', 'a', ''),
981
('read_link', 'a', ''),
983
self.assertEqual([('l', 'target', 6, False, packed_stat)],
986
# Another call won't re-read the link
987
self.assertEqual([('read_link', 'a', ''),
988
('read_link', 'a', ''),
989
('read_link', 'a', ''),
991
link_or_sha1 = self.update_entry(state, entry, abspath='a',
992
stat_value=stat_value)
993
self.assertEqual('target', link_or_sha1)
994
self.assertEqual([('l', 'target', 6, False, packed_stat)],
997
def do_update_entry(self, state, entry, abspath):
998
stat_value = os.lstat(abspath)
999
return self.update_entry(state, entry, abspath, stat_value)
1001
def test_update_entry_dir(self):
1002
state, entry = self.get_state_with_a()
1003
self.build_tree(['a/'])
1004
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1006
def test_update_entry_dir_unchanged(self):
1007
state, entry = self.get_state_with_a()
1008
self.build_tree(['a/'])
1009
state.adjust_time(+20)
1010
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1011
# a/ used to be a file, but is now a directory, worth saving
1012
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1013
state._dirblock_state)
1015
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1016
state._dirblock_state)
1017
# No changes to a/ means not worth saving.
1018
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1019
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1020
state._dirblock_state)
1021
# Change the last-modified time for the directory
1022
t = time.time() - 100.0
1024
os.utime('a', (t, t))
1026
# It looks like Win32 + FAT doesn't allow to change times on a dir.
1027
raise tests.TestSkipped("can't update mtime of a dir on FAT")
1028
saved_packed_stat = entry[1][0][-1]
1029
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1030
# We *do* go ahead and update the information in the dirblocks, but we
1031
# don't bother setting IN_MEMORY_MODIFIED because it is trivial to
1033
self.assertNotEqual(saved_packed_stat, entry[1][0][-1])
1034
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1035
state._dirblock_state)
1037
def test_update_entry_file_unchanged(self):
1038
state, _ = self.get_state_with_a()
1039
tree = self.make_branch_and_tree('tree')
1041
self.build_tree(['tree/a'])
1042
tree.add(['a'], ['a-id'])
1043
with_a_id = tree.commit('witha')
1044
self.addCleanup(tree.unlock)
1045
state.set_parent_trees(
1046
[(with_a_id, tree.branch.repository.revision_tree(with_a_id))],
1048
entry = state._get_entry(0, path_utf8='a')
1049
self.build_tree(['a'])
1050
sha1sum = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1051
state.adjust_time(+20)
1052
self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1053
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1054
state._dirblock_state)
1056
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1057
state._dirblock_state)
1058
self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1059
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1060
state._dirblock_state)
1062
def test_update_entry_tree_reference(self):
1063
state = test_dirstate.InstrumentedDirState.initialize('dirstate')
1064
self.addCleanup(state.unlock)
1065
state.add('r', 'r-id', 'tree-reference', None, '')
1066
self.build_tree(['r/'])
1067
entry = state._get_entry(0, path_utf8='r')
1068
self.do_update_entry(state, entry, 'r')
1069
entry = state._get_entry(0, path_utf8='r')
1070
self.assertEqual('t', entry[1][0][0])
1072
def create_and_test_file(self, state, entry):
1073
"""Create a file at 'a' and verify the state finds it during update.
1075
The state should already be versioning *something* at 'a'. This makes
1076
sure that state.update_entry recognizes it as a file.
1078
self.build_tree(['a'])
1079
stat_value = os.lstat('a')
1080
packed_stat = dirstate.pack_stat(stat_value)
1082
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1083
self.assertEqual(None, link_or_sha1)
1084
self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
1088
def create_and_test_dir(self, state, entry):
1089
"""Create a directory at 'a' and verify the state finds it.
1091
The state should already be versioning *something* at 'a'. This makes
1092
sure that state.update_entry recognizes it as a directory.
1094
self.build_tree(['a/'])
1095
stat_value = os.lstat('a')
1096
packed_stat = dirstate.pack_stat(stat_value)
1098
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1099
self.assertIs(None, link_or_sha1)
1100
self.assertEqual([('d', '', 0, False, packed_stat)], entry[1])
1104
# FIXME: Add unicode version
1105
def create_and_test_symlink(self, state, entry):
1106
"""Create a symlink at 'a' and verify the state finds it.
1108
The state should already be versioning *something* at 'a'. This makes
1109
sure that state.update_entry recognizes it as a symlink.
1111
This should not be called if this platform does not have symlink
1114
# caller should care about skipping test on platforms without symlinks
1115
os.symlink('path/to/foo', 'a')
1117
stat_value = os.lstat('a')
1118
packed_stat = dirstate.pack_stat(stat_value)
1120
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1121
self.assertEqual('path/to/foo', link_or_sha1)
1122
self.assertEqual([('l', 'path/to/foo', 11, False, packed_stat)],
1126
def test_update_file_to_dir(self):
1127
"""If a file changes to a directory we return None for the sha.
1128
We also update the inventory record.
1130
state, entry = self.get_state_with_a()
1131
# The file sha1 won't be cached unless the file is old
1132
state.adjust_time(+10)
1133
self.create_and_test_file(state, entry)
1135
self.create_and_test_dir(state, entry)
1137
def test_update_file_to_symlink(self):
1138
"""File becomes a symlink"""
1139
self.requireFeature(tests.SymlinkFeature)
1140
state, entry = self.get_state_with_a()
1141
# The file sha1 won't be cached unless the file is old
1142
state.adjust_time(+10)
1143
self.create_and_test_file(state, entry)
1145
self.create_and_test_symlink(state, entry)
1147
def test_update_dir_to_file(self):
1148
"""Directory becoming a file updates the entry."""
1149
state, entry = self.get_state_with_a()
1150
# The file sha1 won't be cached unless the file is old
1151
state.adjust_time(+10)
1152
self.create_and_test_dir(state, entry)
1154
self.create_and_test_file(state, entry)
1156
def test_update_dir_to_symlink(self):
1157
"""Directory becomes a symlink"""
1158
self.requireFeature(tests.SymlinkFeature)
1159
state, entry = self.get_state_with_a()
1160
# The symlink target won't be cached if it isn't old
1161
state.adjust_time(+10)
1162
self.create_and_test_dir(state, entry)
1164
self.create_and_test_symlink(state, entry)
1166
def test_update_symlink_to_file(self):
1167
"""Symlink becomes a file"""
1168
self.requireFeature(tests.SymlinkFeature)
1169
state, entry = self.get_state_with_a()
1170
# The symlink and file info won't be cached unless old
1171
state.adjust_time(+10)
1172
self.create_and_test_symlink(state, entry)
1174
self.create_and_test_file(state, entry)
1176
def test_update_symlink_to_dir(self):
1177
"""Symlink becomes a directory"""
1178
self.requireFeature(tests.SymlinkFeature)
1179
state, entry = self.get_state_with_a()
1180
# The symlink target won't be cached if it isn't old
1181
state.adjust_time(+10)
1182
self.create_and_test_symlink(state, entry)
1184
self.create_and_test_dir(state, entry)
1186
def test__is_executable_win32(self):
1187
state, entry = self.get_state_with_a()
1188
self.build_tree(['a'])
1190
# Make sure we are using the win32 implementation of _is_executable
1191
state._is_executable = state._is_executable_win32
1193
# The file on disk is not executable, but we are marking it as though
1194
# it is. With _is_executable_win32 we ignore what is on disk.
1195
entry[1][0] = ('f', '', 0, True, dirstate.DirState.NULLSTAT)
1197
stat_value = os.lstat('a')
1198
packed_stat = dirstate.pack_stat(stat_value)
1200
state.adjust_time(-10) # Make sure everything is new
1201
self.update_entry(state, entry, abspath='a', stat_value=stat_value)
1203
# The row is updated, but the executable bit stays set.
1204
self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1207
# Make the disk object look old enough to cache (but it won't cache the
1208
# sha as it is a new file).
1209
state.adjust_time(+20)
1210
digest = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1211
self.update_entry(state, entry, abspath='a', stat_value=stat_value)
1212
self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1215
def _prepare_tree(self):
1217
text = 'Hello World\n'
1218
tree = self.make_branch_and_tree('tree')
1219
self.build_tree_contents([('tree/a file', text)])
1220
tree.add('a file', 'a-file-id')
1221
# Note: dirstate does not sha prior to the first commit
1222
# so commit now in order for the test to work
1223
tree.commit('first')
1226
def test_sha1provider_sha1_used(self):
1227
tree, text = self._prepare_tree()
1228
state = dirstate.DirState.from_tree(tree, 'dirstate',
1229
UppercaseSHA1Provider())
1230
self.addCleanup(state.unlock)
1231
expected_sha = osutils.sha_string(text.upper() + "foo")
1232
entry = state._get_entry(0, path_utf8='a file')
1233
state._sha_cutoff_time()
1234
state._cutoff_time += 10
1235
sha1 = self.update_entry(state, entry, 'tree/a file',
1236
os.lstat('tree/a file'))
1237
self.assertEqual(expected_sha, sha1)
1239
def test_sha1provider_stat_and_sha1_used(self):
1240
tree, text = self._prepare_tree()
1242
self.addCleanup(tree.unlock)
1243
state = tree._current_dirstate()
1244
state._sha1_provider = UppercaseSHA1Provider()
1245
# If we used the standard provider, it would look like nothing has
1247
file_ids_changed = [change[0] for change
1248
in tree.iter_changes(tree.basis_tree())]
1249
self.assertEqual(['a-file-id'], file_ids_changed)
1252
class UppercaseSHA1Provider(dirstate.SHA1Provider):
1253
"""A custom SHA1Provider."""
1255
def sha1(self, abspath):
1256
return self.stat_and_sha1(abspath)[1]
1258
def stat_and_sha1(self, abspath):
1259
file_obj = file(abspath, 'rb')
1261
statvalue = os.fstat(file_obj.fileno())
1262
text = ''.join(file_obj.readlines())
1263
sha1 = osutils.sha_string(text.upper() + "foo")
1266
return statvalue, sha1
1269
class TestProcessEntry(test_dirstate.TestCaseWithDirState):
1271
scenarios = multiply_scenarios(dir_reader_scenarios(), pe_scenarios)
1274
_process_entry = None
1277
super(TestProcessEntry, self).setUp()
1278
self.overrideAttr(dirstate, '_process_entry', self._process_entry)
1280
def assertChangedFileIds(self, expected, tree):
1283
file_ids = [info[0] for info
1284
in tree.iter_changes(tree.basis_tree())]
1287
self.assertEqual(sorted(expected), sorted(file_ids))
1289
def test_exceptions_raised(self):
1290
# This is a direct test of bug #495023, it relies on osutils.is_inside
1291
# getting called in an inner function. Which makes it a bit brittle,
1292
# but at least it does reproduce the bug.
1293
tree = self.make_branch_and_tree('tree')
1294
self.build_tree(['tree/file', 'tree/dir/', 'tree/dir/sub',
1295
'tree/dir2/', 'tree/dir2/sub2'])
1296
tree.add(['file', 'dir', 'dir/sub', 'dir2', 'dir2/sub2'])
1297
tree.commit('first commit')
1299
self.addCleanup(tree.unlock)
1300
basis_tree = tree.basis_tree()
1301
def is_inside_raises(*args, **kwargs):
1302
raise RuntimeError('stop this')
1303
self.overrideAttr(osutils, 'is_inside', is_inside_raises)
1304
self.assertListRaises(RuntimeError, tree.iter_changes, basis_tree)
1306
def test_simple_changes(self):
1307
tree = self.make_branch_and_tree('tree')
1308
self.build_tree(['tree/file'])
1309
tree.add(['file'], ['file-id'])
1310
self.assertChangedFileIds([tree.get_root_id(), 'file-id'], tree)
1312
self.assertChangedFileIds([], tree)
1314
def test_sha1provider_stat_and_sha1_used(self):
1315
tree = self.make_branch_and_tree('tree')
1316
self.build_tree(['tree/file'])
1317
tree.add(['file'], ['file-id'])
1320
self.addCleanup(tree.unlock)
1321
state = tree._current_dirstate()
1322
state._sha1_provider = UppercaseSHA1Provider()
1323
self.assertChangedFileIds(['file-id'], tree)