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."""
33
from .test_osutils import dir_reader_scenarios
34
from .scenarios import (
35
load_tests_apply_scenarios,
43
load_tests = load_tests_apply_scenarios
46
compiled_dirstate_helpers_feature = features.ModuleAvailableFeature(
47
'breezy._dirstate_helpers_pyx')
50
# FIXME: we should also parametrize against SHA1Provider !
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
ue_scenarios.append(('dirstate_Pyrex', {'update_entry': update_entry}))
58
pe_scenarios = [('dirstate_Python',
59
{'_process_entry': dirstate.ProcessEntryPython})]
60
if compiled_dirstate_helpers_feature.available():
61
process_entry = compiled_dirstate_helpers_feature.module.ProcessEntryC
62
pe_scenarios.append(('dirstate_Pyrex', {'_process_entry': process_entry}))
64
helper_scenarios = [('dirstate_Python', {'helpers': _dirstate_helpers_py})]
65
if compiled_dirstate_helpers_feature.available():
66
helper_scenarios.append(('dirstate_Pyrex',
67
{'helpers': compiled_dirstate_helpers_feature.module}))
70
class TestBisectPathMixin(object):
71
"""Test that _bisect_path_*() returns the expected values.
73
_bisect_path_* is intended to work like bisect.bisect_*() except it
74
knows it is working on paths that are sorted by ('path', 'to', 'foo')
75
chunks rather than by raw 'path/to/foo'.
77
Test Cases should inherit from this and override ``get_bisect_path`` return
78
their implementation, and ``get_bisect`` to return the matching
79
bisect.bisect_* function.
82
def get_bisect_path(self):
83
"""Return an implementation of _bisect_path_*"""
84
raise NotImplementedError
87
"""Return a version of bisect.bisect_*.
89
Also, for the 'exists' check, return the offset to the real values.
90
For example bisect_left returns the index of an entry, while
91
bisect_right returns the index *after* an entry
93
:return: (bisect_func, offset)
95
raise NotImplementedError
97
def assertBisect(self, paths, split_paths, path, exists=True):
98
"""Assert that bisect_split works like bisect_left on the split paths.
100
:param paths: A list of path names
101
:param split_paths: A list of path names that are already split up by directory
102
('path/to/foo' => ('path', 'to', 'foo'))
103
:param path: The path we are indexing.
104
:param exists: The path should be present, so make sure the
105
final location actually points to the right value.
107
All other arguments will be passed along.
109
bisect_path = self.get_bisect_path()
110
self.assertIsInstance(paths, list)
111
bisect_path_idx = bisect_path(paths, path)
112
split_path = self.split_for_dirblocks([path])[0]
113
bisect_func, offset = self.get_bisect()
114
bisect_split_idx = bisect_func(split_paths, split_path)
115
self.assertEqual(bisect_split_idx, bisect_path_idx,
116
'%s disagreed. %s != %s'
118
% (bisect_path.__name__,
119
bisect_split_idx, bisect_path_idx, path)
122
self.assertEqual(path, paths[bisect_path_idx+offset])
124
def split_for_dirblocks(self, paths):
127
dirname, basename = os.path.split(path)
128
dir_split_paths.append((dirname.split('/'), basename))
129
dir_split_paths.sort()
130
return dir_split_paths
132
def test_simple(self):
133
"""In the simple case it works just like bisect_left"""
134
paths = ['', 'a', 'b', 'c', 'd']
135
split_paths = self.split_for_dirblocks(paths)
137
self.assertBisect(paths, split_paths, path, exists=True)
138
self.assertBisect(paths, split_paths, '_', exists=False)
139
self.assertBisect(paths, split_paths, 'aa', exists=False)
140
self.assertBisect(paths, split_paths, 'bb', exists=False)
141
self.assertBisect(paths, split_paths, 'cc', exists=False)
142
self.assertBisect(paths, split_paths, 'dd', exists=False)
143
self.assertBisect(paths, split_paths, 'a/a', exists=False)
144
self.assertBisect(paths, split_paths, 'b/b', exists=False)
145
self.assertBisect(paths, split_paths, 'c/c', exists=False)
146
self.assertBisect(paths, split_paths, 'd/d', exists=False)
148
def test_involved(self):
149
"""This is where bisect_path_* diverges slightly."""
150
# This is the list of paths and their contents
178
# This is the exact order that is stored by dirstate
179
# All children in a directory are mentioned before an children of
180
# children are mentioned.
181
# So all the root-directory paths, then all the
182
# first sub directory, etc.
183
paths = [# content of '/'
184
'', 'a', 'a-a', 'a-z', 'a=a', 'a=z',
186
'a/a', 'a/a-a', 'a/a-z',
188
'a/z', 'a/z-a', 'a/z-z',
211
split_paths = self.split_for_dirblocks(paths)
213
for dir_parts, basename in split_paths:
214
if dir_parts == ['']:
215
sorted_paths.append(basename)
217
sorted_paths.append('/'.join(dir_parts + [basename]))
219
self.assertEqual(sorted_paths, paths)
222
self.assertBisect(paths, split_paths, path, exists=True)
225
class TestBisectPathLeft(tests.TestCase, TestBisectPathMixin):
226
"""Run all Bisect Path tests against _bisect_path_left."""
228
def get_bisect_path(self):
229
from breezy._dirstate_helpers_py import _bisect_path_left
230
return _bisect_path_left
232
def get_bisect(self):
233
return bisect.bisect_left, 0
236
class TestCompiledBisectPathLeft(TestBisectPathLeft):
237
"""Run all Bisect Path tests against _bisect_path_lect"""
239
_test_needs_features = [compiled_dirstate_helpers_feature]
241
def get_bisect_path(self):
242
from breezy._dirstate_helpers_pyx import _bisect_path_left
243
return _bisect_path_left
246
class TestBisectPathRight(tests.TestCase, TestBisectPathMixin):
247
"""Run all Bisect Path tests against _bisect_path_right"""
249
def get_bisect_path(self):
250
from breezy._dirstate_helpers_py import _bisect_path_right
251
return _bisect_path_right
253
def get_bisect(self):
254
return bisect.bisect_right, -1
257
class TestCompiledBisectPathRight(TestBisectPathRight):
258
"""Run all Bisect Path tests against _bisect_path_right"""
260
_test_needs_features = [compiled_dirstate_helpers_feature]
262
def get_bisect_path(self):
263
from breezy._dirstate_helpers_pyx import _bisect_path_right
264
return _bisect_path_right
267
class TestBisectDirblock(tests.TestCase):
268
"""Test that bisect_dirblock() returns the expected values.
270
bisect_dirblock is intended to work like bisect.bisect_left() except it
271
knows it is working on dirblocks and that dirblocks are sorted by ('path',
272
'to', 'foo') chunks rather than by raw 'path/to/foo'.
274
This test is parameterized by calling get_bisect_dirblock(). Child test
275
cases can override this function to test against a different
279
def get_bisect_dirblock(self):
280
"""Return an implementation of bisect_dirblock"""
281
from breezy._dirstate_helpers_py import bisect_dirblock
282
return bisect_dirblock
284
def assertBisect(self, dirblocks, split_dirblocks, path, *args, **kwargs):
285
"""Assert that bisect_split works like bisect_left on the split paths.
287
:param dirblocks: A list of (path, [info]) pairs.
288
:param split_dirblocks: A list of ((split, path), [info]) pairs.
289
:param path: The path we are indexing.
291
All other arguments will be passed along.
293
bisect_dirblock = self.get_bisect_dirblock()
294
self.assertIsInstance(dirblocks, list)
295
bisect_split_idx = bisect_dirblock(dirblocks, path, *args, **kwargs)
296
split_dirblock = (path.split('/'), [])
297
bisect_left_idx = bisect.bisect_left(split_dirblocks, split_dirblock,
299
self.assertEqual(bisect_left_idx, bisect_split_idx,
300
'bisect_split disagreed. %s != %s'
302
% (bisect_left_idx, bisect_split_idx, path)
305
def paths_to_dirblocks(self, paths):
306
"""Convert a list of paths into dirblock form.
308
Also, ensure that the paths are in proper sorted order.
310
dirblocks = [(path, []) for path in paths]
311
split_dirblocks = [(path.split('/'), []) for path in paths]
312
self.assertEqual(sorted(split_dirblocks), split_dirblocks)
313
return dirblocks, split_dirblocks
315
def test_simple(self):
316
"""In the simple case it works just like bisect_left"""
317
paths = ['', 'a', 'b', 'c', 'd']
318
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
320
self.assertBisect(dirblocks, split_dirblocks, path)
321
self.assertBisect(dirblocks, split_dirblocks, '_')
322
self.assertBisect(dirblocks, split_dirblocks, 'aa')
323
self.assertBisect(dirblocks, split_dirblocks, 'bb')
324
self.assertBisect(dirblocks, split_dirblocks, 'cc')
325
self.assertBisect(dirblocks, split_dirblocks, 'dd')
326
self.assertBisect(dirblocks, split_dirblocks, 'a/a')
327
self.assertBisect(dirblocks, split_dirblocks, 'b/b')
328
self.assertBisect(dirblocks, split_dirblocks, 'c/c')
329
self.assertBisect(dirblocks, split_dirblocks, 'd/d')
331
def test_involved(self):
332
"""This is where bisect_left diverges slightly."""
334
'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
335
'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
337
'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
338
'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
341
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
343
self.assertBisect(dirblocks, split_dirblocks, path)
345
def test_involved_cached(self):
346
"""This is where bisect_left diverges slightly."""
348
'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
349
'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
351
'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
352
'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
356
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
358
self.assertBisect(dirblocks, split_dirblocks, path, cache=cache)
361
class TestCompiledBisectDirblock(TestBisectDirblock):
362
"""Test that bisect_dirblock() returns the expected values.
364
bisect_dirblock is intended to work like bisect.bisect_left() except it
365
knows it is working on dirblocks and that dirblocks are sorted by ('path',
366
'to', 'foo') chunks rather than by raw 'path/to/foo'.
368
This runs all the normal tests that TestBisectDirblock did, but uses the
372
_test_needs_features = [compiled_dirstate_helpers_feature]
374
def get_bisect_dirblock(self):
375
from breezy._dirstate_helpers_pyx import bisect_dirblock
376
return bisect_dirblock
379
class TestLtByDirs(tests.TestCase):
380
"""Test an implementation of lt_by_dirs()
382
lt_by_dirs() compares 2 paths by their directory sections, rather than as
385
Child test cases can override ``get_lt_by_dirs`` to test a specific
389
def get_lt_by_dirs(self):
390
"""Get a specific implementation of lt_by_dirs."""
391
from breezy._dirstate_helpers_py import lt_by_dirs
394
def assertCmpByDirs(self, expected, str1, str2):
395
"""Compare the two strings, in both directions.
397
:param expected: The expected comparison value. -1 means str1 comes
398
first, 0 means they are equal, 1 means str2 comes first
399
:param str1: string to compare
400
:param str2: string to compare
402
lt_by_dirs = self.get_lt_by_dirs()
404
self.assertEqual(str1, str2)
405
self.assertFalse(lt_by_dirs(str1, str2))
406
self.assertFalse(lt_by_dirs(str2, str1))
408
self.assertFalse(lt_by_dirs(str1, str2))
409
self.assertTrue(lt_by_dirs(str2, str1))
411
self.assertTrue(lt_by_dirs(str1, str2))
412
self.assertFalse(lt_by_dirs(str2, str1))
414
def test_cmp_empty(self):
415
"""Compare against the empty string."""
416
self.assertCmpByDirs(0, '', '')
417
self.assertCmpByDirs(1, 'a', '')
418
self.assertCmpByDirs(1, 'ab', '')
419
self.assertCmpByDirs(1, 'abc', '')
420
self.assertCmpByDirs(1, 'abcd', '')
421
self.assertCmpByDirs(1, 'abcde', '')
422
self.assertCmpByDirs(1, 'abcdef', '')
423
self.assertCmpByDirs(1, 'abcdefg', '')
424
self.assertCmpByDirs(1, 'abcdefgh', '')
425
self.assertCmpByDirs(1, 'abcdefghi', '')
426
self.assertCmpByDirs(1, 'test/ing/a/path/', '')
428
def test_cmp_same_str(self):
429
"""Compare the same string"""
430
self.assertCmpByDirs(0, 'a', 'a')
431
self.assertCmpByDirs(0, 'ab', 'ab')
432
self.assertCmpByDirs(0, 'abc', 'abc')
433
self.assertCmpByDirs(0, 'abcd', 'abcd')
434
self.assertCmpByDirs(0, 'abcde', 'abcde')
435
self.assertCmpByDirs(0, 'abcdef', 'abcdef')
436
self.assertCmpByDirs(0, 'abcdefg', 'abcdefg')
437
self.assertCmpByDirs(0, 'abcdefgh', 'abcdefgh')
438
self.assertCmpByDirs(0, 'abcdefghi', 'abcdefghi')
439
self.assertCmpByDirs(0, 'testing a long string', 'testing a long string')
440
self.assertCmpByDirs(0, 'x'*10000, 'x'*10000)
441
self.assertCmpByDirs(0, 'a/b', 'a/b')
442
self.assertCmpByDirs(0, 'a/b/c', 'a/b/c')
443
self.assertCmpByDirs(0, 'a/b/c/d', 'a/b/c/d')
444
self.assertCmpByDirs(0, 'a/b/c/d/e', 'a/b/c/d/e')
446
def test_simple_paths(self):
447
"""Compare strings that act like normal string comparison"""
448
self.assertCmpByDirs(-1, 'a', 'b')
449
self.assertCmpByDirs(-1, 'aa', 'ab')
450
self.assertCmpByDirs(-1, 'ab', 'bb')
451
self.assertCmpByDirs(-1, 'aaa', 'aab')
452
self.assertCmpByDirs(-1, 'aab', 'abb')
453
self.assertCmpByDirs(-1, 'abb', 'bbb')
454
self.assertCmpByDirs(-1, 'aaaa', 'aaab')
455
self.assertCmpByDirs(-1, 'aaab', 'aabb')
456
self.assertCmpByDirs(-1, 'aabb', 'abbb')
457
self.assertCmpByDirs(-1, 'abbb', 'bbbb')
458
self.assertCmpByDirs(-1, 'aaaaa', 'aaaab')
459
self.assertCmpByDirs(-1, 'a/a', 'a/b')
460
self.assertCmpByDirs(-1, 'a/b', 'b/b')
461
self.assertCmpByDirs(-1, 'a/a/a', 'a/a/b')
462
self.assertCmpByDirs(-1, 'a/a/b', 'a/b/b')
463
self.assertCmpByDirs(-1, 'a/b/b', 'b/b/b')
464
self.assertCmpByDirs(-1, 'a/a/a/a', 'a/a/a/b')
465
self.assertCmpByDirs(-1, 'a/a/a/b', 'a/a/b/b')
466
self.assertCmpByDirs(-1, 'a/a/b/b', 'a/b/b/b')
467
self.assertCmpByDirs(-1, 'a/b/b/b', 'b/b/b/b')
468
self.assertCmpByDirs(-1, 'a/a/a/a/a', 'a/a/a/a/b')
470
def test_tricky_paths(self):
471
self.assertCmpByDirs(1, 'ab/cd/ef', 'ab/cc/ef')
472
self.assertCmpByDirs(1, 'ab/cd/ef', 'ab/c/ef')
473
self.assertCmpByDirs(-1, 'ab/cd/ef', 'ab/cd-ef')
474
self.assertCmpByDirs(-1, 'ab/cd', 'ab/cd-')
475
self.assertCmpByDirs(-1, 'ab/cd', 'ab-cd')
477
def test_cmp_unicode_not_allowed(self):
478
lt_by_dirs = self.get_lt_by_dirs()
479
self.assertRaises(TypeError, lt_by_dirs, u'Unicode', 'str')
480
self.assertRaises(TypeError, lt_by_dirs, 'str', u'Unicode')
481
self.assertRaises(TypeError, lt_by_dirs, u'Unicode', u'Unicode')
483
def test_cmp_non_ascii(self):
484
self.assertCmpByDirs(-1, '\xc2\xb5', '\xc3\xa5') # u'\xb5', u'\xe5'
485
self.assertCmpByDirs(-1, 'a', '\xc3\xa5') # u'a', u'\xe5'
486
self.assertCmpByDirs(-1, 'b', '\xc2\xb5') # u'b', u'\xb5'
487
self.assertCmpByDirs(-1, 'a/b', 'a/\xc3\xa5') # u'a/b', u'a/\xe5'
488
self.assertCmpByDirs(-1, 'b/a', 'b/\xc2\xb5') # u'b/a', u'b/\xb5'
491
class TestCompiledLtByDirs(TestLtByDirs):
492
"""Test the pyrex implementation of lt_by_dirs"""
494
_test_needs_features = [compiled_dirstate_helpers_feature]
496
def get_lt_by_dirs(self):
497
from breezy._dirstate_helpers_pyx import lt_by_dirs
501
class TestLtPathByDirblock(tests.TestCase):
502
"""Test an implementation of _lt_path_by_dirblock()
504
_lt_path_by_dirblock() compares two paths using the sort order used by
505
DirState. All paths in the same directory are sorted together.
507
Child test cases can override ``get_lt_path_by_dirblock`` to test a specific
511
def get_lt_path_by_dirblock(self):
512
"""Get a specific implementation of _lt_path_by_dirblock."""
513
from breezy._dirstate_helpers_py import _lt_path_by_dirblock
514
return _lt_path_by_dirblock
516
def assertLtPathByDirblock(self, paths):
517
"""Compare all paths and make sure they evaluate to the correct order.
519
This does N^2 comparisons. It is assumed that ``paths`` is properly
522
:param paths: a sorted list of paths to compare
524
# First, make sure the paths being passed in are correct
526
dirname, basename = os.path.split(p)
527
return dirname.split('/'), basename
528
self.assertEqual(sorted(paths, key=_key), paths)
530
lt_path_by_dirblock = self.get_lt_path_by_dirblock()
531
for idx1, path1 in enumerate(paths):
532
for idx2, path2 in enumerate(paths):
533
lt_result = lt_path_by_dirblock(path1, path2)
534
self.assertEqual(idx1 < idx2, lt_result,
535
'%s did not state that %r < %r, lt=%s'
536
% (lt_path_by_dirblock.__name__,
537
path1, path2, lt_result))
539
def test_cmp_simple_paths(self):
540
"""Compare against the empty string."""
541
self.assertLtPathByDirblock(['', 'a', 'ab', 'abc', 'a/b/c', 'b/d/e'])
542
self.assertLtPathByDirblock(['kl', 'ab/cd', 'ab/ef', 'gh/ij'])
544
def test_tricky_paths(self):
545
self.assertLtPathByDirblock([
547
'', 'a', 'a-a', 'a=a', 'b',
549
'a/a', 'a/a-a', 'a/a=a', 'a/b',
551
'a/a/a', 'a/a/a-a', 'a/a/a=a',
552
# Contents of 'a/a/a'
553
'a/a/a/a', 'a/a/a/b',
554
# Contents of 'a/a/a-a',
555
'a/a/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'
560
# Contents of 'a/a-a/a'
561
'a/a-a/a/a', 'a/a-a/a/b',
562
# Contents of 'a/a=a'
573
self.assertLtPathByDirblock([
575
'', 'a', 'a-a', 'a-z', 'a=a', 'a=z',
577
'a/a', 'a/a-a', 'a/a-z',
579
'a/z', 'a/z-a', 'a/z-z',
603
def test_unicode_not_allowed(self):
604
lt_path_by_dirblock = self.get_lt_path_by_dirblock()
605
self.assertRaises(TypeError, lt_path_by_dirblock, u'Uni', 'str')
606
self.assertRaises(TypeError, lt_path_by_dirblock, 'str', u'Uni')
607
self.assertRaises(TypeError, lt_path_by_dirblock, u'Uni', u'Uni')
608
self.assertRaises(TypeError, lt_path_by_dirblock, u'x/Uni', 'x/str')
609
self.assertRaises(TypeError, lt_path_by_dirblock, 'x/str', u'x/Uni')
610
self.assertRaises(TypeError, lt_path_by_dirblock, u'x/Uni', u'x/Uni')
612
def test_nonascii(self):
613
self.assertLtPathByDirblock([
615
'', 'a', '\xc2\xb5', '\xc3\xa5',
617
'a/a', 'a/\xc2\xb5', 'a/\xc3\xa5',
619
'a/a/a', 'a/a/\xc2\xb5', 'a/a/\xc3\xa5',
620
# content of 'a/\xc2\xb5'
621
'a/\xc2\xb5/a', 'a/\xc2\xb5/\xc2\xb5', 'a/\xc2\xb5/\xc3\xa5',
622
# content of 'a/\xc3\xa5'
623
'a/\xc3\xa5/a', 'a/\xc3\xa5/\xc2\xb5', 'a/\xc3\xa5/\xc3\xa5',
624
# content of '\xc2\xb5'
625
'\xc2\xb5/a', '\xc2\xb5/\xc2\xb5', '\xc2\xb5/\xc3\xa5',
626
# content of '\xc2\xe5'
627
'\xc3\xa5/a', '\xc3\xa5/\xc2\xb5', '\xc3\xa5/\xc3\xa5',
631
class TestCompiledLtPathByDirblock(TestLtPathByDirblock):
632
"""Test the pyrex implementation of _lt_path_by_dirblock"""
634
_test_needs_features = [compiled_dirstate_helpers_feature]
636
def get_lt_path_by_dirblock(self):
637
from breezy._dirstate_helpers_pyx import _lt_path_by_dirblock
638
return _lt_path_by_dirblock
641
class TestMemRChr(tests.TestCase):
642
"""Test memrchr functionality"""
644
_test_needs_features = [compiled_dirstate_helpers_feature]
646
def assertMemRChr(self, expected, s, c):
647
from breezy._dirstate_helpers_pyx import _py_memrchr
648
self.assertEqual(expected, _py_memrchr(s, c))
650
def test_missing(self):
651
self.assertMemRChr(None, '', 'a')
652
self.assertMemRChr(None, '', 'c')
653
self.assertMemRChr(None, 'abcdefghijklm', 'q')
654
self.assertMemRChr(None, 'aaaaaaaaaaaaaaaaaaaaaaa', 'b')
656
def test_single_entry(self):
657
self.assertMemRChr(0, 'abcdefghijklm', 'a')
658
self.assertMemRChr(1, 'abcdefghijklm', 'b')
659
self.assertMemRChr(2, 'abcdefghijklm', 'c')
660
self.assertMemRChr(10, 'abcdefghijklm', 'k')
661
self.assertMemRChr(11, 'abcdefghijklm', 'l')
662
self.assertMemRChr(12, 'abcdefghijklm', 'm')
664
def test_multiple(self):
665
self.assertMemRChr(10, 'abcdefjklmabcdefghijklm', 'a')
666
self.assertMemRChr(11, 'abcdefjklmabcdefghijklm', 'b')
667
self.assertMemRChr(12, 'abcdefjklmabcdefghijklm', 'c')
668
self.assertMemRChr(20, 'abcdefjklmabcdefghijklm', 'k')
669
self.assertMemRChr(21, 'abcdefjklmabcdefghijklm', 'l')
670
self.assertMemRChr(22, 'abcdefjklmabcdefghijklm', 'm')
671
self.assertMemRChr(22, 'aaaaaaaaaaaaaaaaaaaaaaa', 'a')
673
def test_with_nulls(self):
674
self.assertMemRChr(10, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'a')
675
self.assertMemRChr(11, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'b')
676
self.assertMemRChr(12, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'c')
677
self.assertMemRChr(20, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'k')
678
self.assertMemRChr(21, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'l')
679
self.assertMemRChr(22, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'm')
680
self.assertMemRChr(22, 'aaa\0\0\0aaaaaaa\0\0\0aaaaaaa', 'a')
681
self.assertMemRChr(9, '\0\0\0\0\0\0\0\0\0\0', '\0')
684
class TestReadDirblocks(test_dirstate.TestCaseWithDirState):
685
"""Test an implementation of _read_dirblocks()
687
_read_dirblocks() reads in all of the dirblock information from the disk
690
Child test cases can override ``get_read_dirblocks`` to test a specific
694
# inherits scenarios from test_dirstate
696
def get_read_dirblocks(self):
697
from breezy._dirstate_helpers_py import _read_dirblocks
698
return _read_dirblocks
700
def test_smoketest(self):
701
"""Make sure that we can create and read back a simple file."""
702
tree, state, expected = self.create_basic_dirstate()
704
state._read_header_if_needed()
705
self.assertEqual(dirstate.DirState.NOT_IN_MEMORY,
706
state._dirblock_state)
707
read_dirblocks = self.get_read_dirblocks()
708
read_dirblocks(state)
709
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
710
state._dirblock_state)
712
def test_trailing_garbage(self):
713
tree, state, expected = self.create_basic_dirstate()
714
# On Unix, we can write extra data as long as we haven't read yet, but
715
# on Win32, if you've opened the file with FILE_SHARE_READ, trying to
716
# open it in append mode will fail.
718
f = open('dirstate', 'ab')
720
# Add bogus trailing garbage
725
e = self.assertRaises(errors.DirstateCorrupt,
726
state._read_dirblocks_if_needed)
727
# Make sure we mention the bogus characters in the error
728
self.assertContainsRe(str(e), 'bogus')
731
class TestCompiledReadDirblocks(TestReadDirblocks):
732
"""Test the pyrex implementation of _read_dirblocks"""
734
_test_needs_features = [compiled_dirstate_helpers_feature]
736
def get_read_dirblocks(self):
737
from breezy._dirstate_helpers_pyx import _read_dirblocks
738
return _read_dirblocks
741
class TestUsingCompiledIfAvailable(tests.TestCase):
742
"""Check that any compiled functions that are available are the default.
744
It is possible to have typos, etc in the import line, such that
745
_dirstate_helpers_pyx is actually available, but the compiled functions are
749
def test_bisect_dirblock(self):
750
if compiled_dirstate_helpers_feature.available():
751
from breezy._dirstate_helpers_pyx import bisect_dirblock
753
from breezy._dirstate_helpers_py import bisect_dirblock
754
self.assertIs(bisect_dirblock, dirstate.bisect_dirblock)
756
def test__bisect_path_left(self):
757
if compiled_dirstate_helpers_feature.available():
758
from breezy._dirstate_helpers_pyx import _bisect_path_left
760
from breezy._dirstate_helpers_py import _bisect_path_left
761
self.assertIs(_bisect_path_left, dirstate._bisect_path_left)
763
def test__bisect_path_right(self):
764
if compiled_dirstate_helpers_feature.available():
765
from breezy._dirstate_helpers_pyx import _bisect_path_right
767
from breezy._dirstate_helpers_py import _bisect_path_right
768
self.assertIs(_bisect_path_right, dirstate._bisect_path_right)
770
def test_lt_by_dirs(self):
771
if compiled_dirstate_helpers_feature.available():
772
from breezy._dirstate_helpers_pyx import lt_by_dirs
774
from breezy._dirstate_helpers_py import lt_by_dirs
775
self.assertIs(lt_by_dirs, dirstate.lt_by_dirs)
777
def test__read_dirblocks(self):
778
if compiled_dirstate_helpers_feature.available():
779
from breezy._dirstate_helpers_pyx import _read_dirblocks
781
from breezy._dirstate_helpers_py import _read_dirblocks
782
self.assertIs(_read_dirblocks, dirstate._read_dirblocks)
784
def test_update_entry(self):
785
if compiled_dirstate_helpers_feature.available():
786
from breezy._dirstate_helpers_pyx import update_entry
788
from breezy.dirstate import update_entry
789
self.assertIs(update_entry, dirstate.update_entry)
791
def test_process_entry(self):
792
if compiled_dirstate_helpers_feature.available():
793
from breezy._dirstate_helpers_pyx import ProcessEntryC
794
self.assertIs(ProcessEntryC, dirstate._process_entry)
796
from breezy.dirstate import ProcessEntryPython
797
self.assertIs(ProcessEntryPython, dirstate._process_entry)
800
class TestUpdateEntry(test_dirstate.TestCaseWithDirState):
801
"""Test the DirState.update_entry functions"""
803
scenarios = multiply_scenarios(
804
dir_reader_scenarios(), ue_scenarios)
810
super(TestUpdateEntry, self).setUp()
811
self.overrideAttr(dirstate, 'update_entry', self.update_entry)
813
def get_state_with_a(self):
814
"""Create a DirState tracking a single object named 'a'"""
815
state = test_dirstate.InstrumentedDirState.initialize('dirstate')
816
self.addCleanup(state.unlock)
817
state.add('a', 'a-id', 'file', None, '')
818
entry = state._get_entry(0, path_utf8='a')
821
def test_observed_sha1_cachable(self):
822
state, entry = self.get_state_with_a()
824
atime = time.time() - 10
825
self.build_tree(['a'])
826
statvalue = test_dirstate._FakeStat.from_stat(os.lstat('a'))
827
statvalue.st_mtime = statvalue.st_ctime = atime
828
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
829
state._dirblock_state)
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])
834
self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
835
state._dirblock_state)
837
def test_observed_sha1_not_cachable(self):
838
state, entry = self.get_state_with_a()
840
oldval = entry[1][0][1]
841
oldstat = entry[1][0][4]
842
self.build_tree(['a'])
843
statvalue = os.lstat('a')
844
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
845
state._dirblock_state)
846
state._observed_sha1(entry, "foo", statvalue)
847
self.assertEqual(oldval, entry[1][0][1])
848
self.assertEqual(oldstat, entry[1][0][4])
849
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
850
state._dirblock_state)
852
def test_update_entry(self):
853
state, _ = self.get_state_with_a()
854
tree = self.make_branch_and_tree('tree')
856
empty_revid = tree.commit('empty')
857
self.build_tree(['tree/a'])
858
tree.add(['a'], ['a-id'])
859
with_a_id = tree.commit('with_a')
860
self.addCleanup(tree.unlock)
861
state.set_parent_trees(
862
[(empty_revid, tree.branch.repository.revision_tree(empty_revid))],
864
entry = state._get_entry(0, path_utf8='a')
865
self.build_tree(['a'])
866
# Add one where we don't provide the stat or sha already
867
self.assertEqual(('', 'a', 'a-id'), entry[0])
868
self.assertEqual(('f', '', 0, False, dirstate.DirState.NULLSTAT),
870
# Flush the buffers to disk
872
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
873
state._dirblock_state)
875
stat_value = os.lstat('a')
876
packed_stat = dirstate.pack_stat(stat_value)
877
link_or_sha1 = self.update_entry(state, entry, abspath='a',
878
stat_value=stat_value)
879
self.assertEqual(None, link_or_sha1)
881
# The dirblock entry should not have computed or cached the file's
882
# sha1, but it did update the files' st_size. However, this is not
883
# worth writing a dirstate file for, so we leave the state UNMODIFIED
884
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
886
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
887
state._dirblock_state)
888
mode = stat_value.st_mode
889
self.assertEqual([('is_exec', mode, False)], state._log)
892
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
893
state._dirblock_state)
895
# Roll the clock back so the file is guaranteed to look too new. We
896
# should still not compute the sha1.
897
state.adjust_time(-10)
900
link_or_sha1 = self.update_entry(state, entry, abspath='a',
901
stat_value=stat_value)
902
self.assertEqual([('is_exec', mode, False)], state._log)
903
self.assertEqual(None, link_or_sha1)
904
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
905
state._dirblock_state)
906
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
910
# If it is cachable (the clock has moved forward) but new it still
911
# won't calculate the sha or cache it.
912
state.adjust_time(+20)
914
link_or_sha1 = dirstate.update_entry(state, entry, abspath='a',
915
stat_value=stat_value)
916
self.assertEqual(None, link_or_sha1)
917
self.assertEqual([('is_exec', mode, False)], state._log)
918
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
920
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
921
state._dirblock_state)
923
# If the file is no longer new, and the clock has been moved forward
924
# sufficiently, it will cache the sha.
926
state.set_parent_trees(
927
[(with_a_id, tree.branch.repository.revision_tree(with_a_id))],
929
entry = state._get_entry(0, path_utf8='a')
931
link_or_sha1 = self.update_entry(state, entry, abspath='a',
932
stat_value=stat_value)
933
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
935
self.assertEqual([('is_exec', mode, False), ('sha1', 'a')],
937
self.assertEqual(('f', link_or_sha1, 14, False, packed_stat),
940
# Subsequent calls will just return the cached value
942
link_or_sha1 = self.update_entry(state, entry, abspath='a',
943
stat_value=stat_value)
944
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
946
self.assertEqual([], state._log)
947
self.assertEqual(('f', link_or_sha1, 14, False, packed_stat),
950
def test_update_entry_symlink(self):
951
"""Update entry should read symlinks."""
952
self.requireFeature(features.SymlinkFeature)
953
state, entry = self.get_state_with_a()
955
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
956
state._dirblock_state)
957
os.symlink('target', 'a')
959
state.adjust_time(-10) # Make the symlink look new
960
stat_value = os.lstat('a')
961
packed_stat = dirstate.pack_stat(stat_value)
962
link_or_sha1 = self.update_entry(state, entry, abspath='a',
963
stat_value=stat_value)
964
self.assertEqual('target', link_or_sha1)
965
self.assertEqual([('read_link', 'a', '')], state._log)
966
# Dirblock is not updated (the link is too new)
967
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
969
# The file entry turned into a symlink, that is considered
970
# HASH modified worthy.
971
self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
972
state._dirblock_state)
974
# Because the stat_value looks new, we should re-read the target
976
link_or_sha1 = self.update_entry(state, entry, abspath='a',
977
stat_value=stat_value)
978
self.assertEqual('target', link_or_sha1)
979
self.assertEqual([('read_link', 'a', '')], state._log)
980
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
983
state.adjust_time(+20) # Skip into the future, all files look old
985
link_or_sha1 = self.update_entry(state, entry, abspath='a',
986
stat_value=stat_value)
987
# The symlink stayed a symlink. So while it is new enough to cache, we
988
# don't bother setting the flag, because it is not really worth saving
989
# (when we stat the symlink, we'll have paged in the target.)
990
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
991
state._dirblock_state)
992
self.assertEqual('target', link_or_sha1)
993
# We need to re-read the link because only now can we cache it
994
self.assertEqual([('read_link', 'a', '')], state._log)
995
self.assertEqual([('l', 'target', 6, False, packed_stat)],
999
# Another call won't re-read the link
1000
self.assertEqual([], state._log)
1001
link_or_sha1 = self.update_entry(state, entry, abspath='a',
1002
stat_value=stat_value)
1003
self.assertEqual('target', link_or_sha1)
1004
self.assertEqual([('l', 'target', 6, False, packed_stat)],
1007
def do_update_entry(self, state, entry, abspath):
1008
stat_value = os.lstat(abspath)
1009
return self.update_entry(state, entry, abspath, stat_value)
1011
def test_update_entry_dir(self):
1012
state, entry = self.get_state_with_a()
1013
self.build_tree(['a/'])
1014
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1016
def test_update_entry_dir_unchanged(self):
1017
state, entry = self.get_state_with_a()
1018
self.build_tree(['a/'])
1019
state.adjust_time(+20)
1020
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1021
# a/ used to be a file, but is now a directory, worth saving
1022
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1023
state._dirblock_state)
1025
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1026
state._dirblock_state)
1027
# No changes to a/ means not worth saving.
1028
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1029
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1030
state._dirblock_state)
1031
# Change the last-modified time for the directory
1032
t = time.time() - 100.0
1034
os.utime('a', (t, t))
1036
# It looks like Win32 + FAT doesn't allow to change times on a dir.
1037
raise tests.TestSkipped("can't update mtime of a dir on FAT")
1038
saved_packed_stat = entry[1][0][-1]
1039
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1040
# We *do* go ahead and update the information in the dirblocks, but we
1041
# don't bother setting IN_MEMORY_MODIFIED because it is trivial to
1043
self.assertNotEqual(saved_packed_stat, entry[1][0][-1])
1044
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1045
state._dirblock_state)
1047
def test_update_entry_file_unchanged(self):
1048
state, _ = self.get_state_with_a()
1049
tree = self.make_branch_and_tree('tree')
1051
self.build_tree(['tree/a'])
1052
tree.add(['a'], ['a-id'])
1053
with_a_id = tree.commit('witha')
1054
self.addCleanup(tree.unlock)
1055
state.set_parent_trees(
1056
[(with_a_id, tree.branch.repository.revision_tree(with_a_id))],
1058
entry = state._get_entry(0, path_utf8='a')
1059
self.build_tree(['a'])
1060
sha1sum = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1061
state.adjust_time(+20)
1062
self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1063
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1064
state._dirblock_state)
1066
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1067
state._dirblock_state)
1068
self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1069
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1070
state._dirblock_state)
1072
def test_update_entry_tree_reference(self):
1073
state = test_dirstate.InstrumentedDirState.initialize('dirstate')
1074
self.addCleanup(state.unlock)
1075
state.add('r', 'r-id', 'tree-reference', None, '')
1076
self.build_tree(['r/'])
1077
entry = state._get_entry(0, path_utf8='r')
1078
self.do_update_entry(state, entry, 'r')
1079
entry = state._get_entry(0, path_utf8='r')
1080
self.assertEqual('t', entry[1][0][0])
1082
def create_and_test_file(self, state, entry):
1083
"""Create a file at 'a' and verify the state finds it during update.
1085
The state should already be versioning *something* at 'a'. This makes
1086
sure that state.update_entry recognizes it as a file.
1088
self.build_tree(['a'])
1089
stat_value = os.lstat('a')
1090
packed_stat = dirstate.pack_stat(stat_value)
1092
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1093
self.assertEqual(None, link_or_sha1)
1094
self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
1098
def create_and_test_dir(self, state, entry):
1099
"""Create a directory at 'a' and verify the state finds it.
1101
The state should already be versioning *something* at 'a'. This makes
1102
sure that state.update_entry recognizes it as a directory.
1104
self.build_tree(['a/'])
1105
stat_value = os.lstat('a')
1106
packed_stat = dirstate.pack_stat(stat_value)
1108
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1109
self.assertIs(None, link_or_sha1)
1110
self.assertEqual([('d', '', 0, False, packed_stat)], entry[1])
1114
# FIXME: Add unicode version
1115
def create_and_test_symlink(self, state, entry):
1116
"""Create a symlink at 'a' and verify the state finds it.
1118
The state should already be versioning *something* at 'a'. This makes
1119
sure that state.update_entry recognizes it as a symlink.
1121
This should not be called if this platform does not have symlink
1124
# caller should care about skipping test on platforms without symlinks
1125
os.symlink('path/to/foo', 'a')
1127
stat_value = os.lstat('a')
1128
packed_stat = dirstate.pack_stat(stat_value)
1130
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1131
self.assertEqual('path/to/foo', link_or_sha1)
1132
self.assertEqual([('l', 'path/to/foo', 11, False, packed_stat)],
1136
def test_update_file_to_dir(self):
1137
"""If a file changes to a directory we return None for the sha.
1138
We also update the inventory record.
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_dir(state, entry)
1147
def test_update_file_to_symlink(self):
1148
"""File becomes a symlink"""
1149
self.requireFeature(features.SymlinkFeature)
1150
state, entry = self.get_state_with_a()
1151
# The file sha1 won't be cached unless the file is old
1152
state.adjust_time(+10)
1153
self.create_and_test_file(state, entry)
1155
self.create_and_test_symlink(state, entry)
1157
def test_update_dir_to_file(self):
1158
"""Directory becoming a file updates the entry."""
1159
state, entry = self.get_state_with_a()
1160
# The file sha1 won't be cached unless the file is old
1161
state.adjust_time(+10)
1162
self.create_and_test_dir(state, entry)
1164
self.create_and_test_file(state, entry)
1166
def test_update_dir_to_symlink(self):
1167
"""Directory becomes a symlink"""
1168
self.requireFeature(features.SymlinkFeature)
1169
state, entry = self.get_state_with_a()
1170
# The symlink target won't be cached if it isn't old
1171
state.adjust_time(+10)
1172
self.create_and_test_dir(state, entry)
1174
self.create_and_test_symlink(state, entry)
1176
def test_update_symlink_to_file(self):
1177
"""Symlink becomes a file"""
1178
self.requireFeature(features.SymlinkFeature)
1179
state, entry = self.get_state_with_a()
1180
# The symlink and file info won't be cached unless old
1181
state.adjust_time(+10)
1182
self.create_and_test_symlink(state, entry)
1184
self.create_and_test_file(state, entry)
1186
def test_update_symlink_to_dir(self):
1187
"""Symlink becomes a directory"""
1188
self.requireFeature(features.SymlinkFeature)
1189
state, entry = self.get_state_with_a()
1190
# The symlink target won't be cached if it isn't old
1191
state.adjust_time(+10)
1192
self.create_and_test_symlink(state, entry)
1194
self.create_and_test_dir(state, entry)
1196
def test__is_executable_win32(self):
1197
state, entry = self.get_state_with_a()
1198
self.build_tree(['a'])
1200
# Make sure we are using the win32 implementation of _is_executable
1201
state._is_executable = state._is_executable_win32
1203
# The file on disk is not executable, but we are marking it as though
1204
# it is. With _is_executable_win32 we ignore what is on disk.
1205
entry[1][0] = ('f', '', 0, True, dirstate.DirState.NULLSTAT)
1207
stat_value = os.lstat('a')
1208
packed_stat = dirstate.pack_stat(stat_value)
1210
state.adjust_time(-10) # Make sure everything is new
1211
self.update_entry(state, entry, abspath='a', stat_value=stat_value)
1213
# The row is updated, but the executable bit stays set.
1214
self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1217
# Make the disk object look old enough to cache (but it won't cache the
1218
# sha as it is a new file).
1219
state.adjust_time(+20)
1220
digest = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1221
self.update_entry(state, entry, abspath='a', stat_value=stat_value)
1222
self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1225
def _prepare_tree(self):
1227
text = 'Hello World\n'
1228
tree = self.make_branch_and_tree('tree')
1229
self.build_tree_contents([('tree/a file', text)])
1230
tree.add('a file', 'a-file-id')
1231
# Note: dirstate does not sha prior to the first commit
1232
# so commit now in order for the test to work
1233
tree.commit('first')
1236
def test_sha1provider_sha1_used(self):
1237
tree, text = self._prepare_tree()
1238
state = dirstate.DirState.from_tree(tree, 'dirstate',
1239
UppercaseSHA1Provider())
1240
self.addCleanup(state.unlock)
1241
expected_sha = osutils.sha_string(text.upper() + "foo")
1242
entry = state._get_entry(0, path_utf8='a file')
1243
state._sha_cutoff_time()
1244
state._cutoff_time += 10
1245
sha1 = self.update_entry(state, entry, 'tree/a file',
1246
os.lstat('tree/a file'))
1247
self.assertEqual(expected_sha, sha1)
1249
def test_sha1provider_stat_and_sha1_used(self):
1250
tree, text = self._prepare_tree()
1252
self.addCleanup(tree.unlock)
1253
state = tree._current_dirstate()
1254
state._sha1_provider = UppercaseSHA1Provider()
1255
# If we used the standard provider, it would look like nothing has
1257
file_ids_changed = [change[0] for change
1258
in tree.iter_changes(tree.basis_tree())]
1259
self.assertEqual(['a-file-id'], file_ids_changed)
1262
class UppercaseSHA1Provider(dirstate.SHA1Provider):
1263
"""A custom SHA1Provider."""
1265
def sha1(self, abspath):
1266
return self.stat_and_sha1(abspath)[1]
1268
def stat_and_sha1(self, abspath):
1269
file_obj = file(abspath, 'rb')
1271
statvalue = os.fstat(file_obj.fileno())
1272
text = ''.join(file_obj.readlines())
1273
sha1 = osutils.sha_string(text.upper() + "foo")
1276
return statvalue, sha1
1279
class TestProcessEntry(test_dirstate.TestCaseWithDirState):
1281
scenarios = multiply_scenarios(dir_reader_scenarios(), pe_scenarios)
1284
_process_entry = None
1287
super(TestProcessEntry, self).setUp()
1288
self.overrideAttr(dirstate, '_process_entry', self._process_entry)
1290
def assertChangedFileIds(self, expected, tree):
1293
file_ids = [info[0] for info
1294
in tree.iter_changes(tree.basis_tree())]
1297
self.assertEqual(sorted(expected), sorted(file_ids))
1299
def test_exceptions_raised(self):
1300
# This is a direct test of bug #495023, it relies on osutils.is_inside
1301
# getting called in an inner function. Which makes it a bit brittle,
1302
# but at least it does reproduce the bug.
1303
tree = self.make_branch_and_tree('tree')
1304
self.build_tree(['tree/file', 'tree/dir/', 'tree/dir/sub',
1305
'tree/dir2/', 'tree/dir2/sub2'])
1306
tree.add(['file', 'dir', 'dir/sub', 'dir2', 'dir2/sub2'])
1307
tree.commit('first commit')
1309
self.addCleanup(tree.unlock)
1310
basis_tree = tree.basis_tree()
1311
def is_inside_raises(*args, **kwargs):
1312
raise RuntimeError('stop this')
1313
self.overrideAttr(osutils, 'is_inside', is_inside_raises)
1314
self.assertListRaises(RuntimeError, tree.iter_changes, basis_tree)
1316
def test_simple_changes(self):
1317
tree = self.make_branch_and_tree('tree')
1318
self.build_tree(['tree/file'])
1319
tree.add(['file'], ['file-id'])
1320
self.assertChangedFileIds([tree.get_root_id(), 'file-id'], tree)
1322
self.assertChangedFileIds([], tree)
1324
def test_sha1provider_stat_and_sha1_used(self):
1325
tree = self.make_branch_and_tree('tree')
1326
self.build_tree(['tree/file'])
1327
tree.add(['file'], ['file-id'])
1330
self.addCleanup(tree.unlock)
1331
state = tree._current_dirstate()
1332
state._sha1_provider = UppercaseSHA1Provider()
1333
self.assertChangedFileIds(['file-id'], tree)
1336
class TestPackStat(tests.TestCase):
1337
"""Check packed representaton of stat values is robust on all inputs"""
1339
scenarios = helper_scenarios
1341
def pack(self, statlike_tuple):
1342
return self.helpers.pack_stat(os.stat_result(statlike_tuple))
1345
def unpack_field(packed_string, stat_field):
1346
return _dirstate_helpers_py._unpack_stat(packed_string)[stat_field]
1348
def test_result(self):
1349
self.assertEqual("AAAQAAAAABAAAAARAAAAAgAAAAEAAIHk",
1350
self.pack((33252, 1, 2, 0, 0, 0, 4096, 15.5, 16.5, 17.5)))
1352
def test_giant_inode(self):
1353
packed = self.pack((33252, 0xF80000ABC, 0, 0, 0, 0, 0, 0, 0, 0))
1354
self.assertEqual(0x80000ABC, self.unpack_field(packed, "st_ino"))
1356
def test_giant_size(self):
1357
packed = self.pack((33252, 0, 0, 0, 0, 0, (1 << 33) + 4096, 0, 0, 0))
1358
self.assertEqual(4096, self.unpack_field(packed, "st_size"))
1360
def test_fractional_mtime(self):
1361
packed = self.pack((33252, 0, 0, 0, 0, 0, 0, 0, 16.9375, 0))
1362
self.assertEqual(16, self.unpack_field(packed, "st_mtime"))
1364
def test_ancient_mtime(self):
1365
packed = self.pack((33252, 0, 0, 0, 0, 0, 0, 0, -11644473600.0, 0))
1366
self.assertEqual(1240428288, self.unpack_field(packed, "st_mtime"))
1368
def test_distant_mtime(self):
1369
packed = self.pack((33252, 0, 0, 0, 0, 0, 0, 0, 64060588800.0, 0))
1370
self.assertEqual(3931046656, self.unpack_field(packed, "st_mtime"))
1372
def test_fractional_ctime(self):
1373
packed = self.pack((33252, 0, 0, 0, 0, 0, 0, 0, 0, 17.5625))
1374
self.assertEqual(17, self.unpack_field(packed, "st_ctime"))
1376
def test_ancient_ctime(self):
1377
packed = self.pack((33252, 0, 0, 0, 0, 0, 0, 0, 0, -11644473600.0))
1378
self.assertEqual(1240428288, self.unpack_field(packed, "st_ctime"))
1380
def test_distant_ctime(self):
1381
packed = self.pack((33252, 0, 0, 0, 0, 0, 0, 0, 0, 64060588800.0))
1382
self.assertEqual(3931046656, self.unpack_field(packed, "st_ctime"))
1384
def test_negative_dev(self):
1385
packed = self.pack((33252, 0, -0xFFFFFCDE, 0, 0, 0, 0, 0, 0, 0))
1386
self.assertEqual(0x322, self.unpack_field(packed, "st_dev"))