~bzr-pqm/bzr/bzr.dev

5557.1.7 by John Arbash Meinel
Merge in the bzr.dev 5582
1
# Copyright (C) 2007-2011 Canonical Ltd
2474.1.7 by John Arbash Meinel
Add some tests for a helper function that lets us
2
#
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.
7
#
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.
12
#
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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2474.1.7 by John Arbash Meinel
Add some tests for a helper function that lets us
16
17
"""Tests for the compiled dirstate helpers."""
18
2474.1.57 by John Arbash Meinel
Move code around to refactor according to our pyrex extension design.
19
import bisect
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
20
import os
3696.5.1 by Robert Collins
Integrate in stat-cache-updates-during-commit with faster iter-changes.
21
import time
2474.1.57 by John Arbash Meinel
Move code around to refactor according to our pyrex extension design.
22
2474.1.7 by John Arbash Meinel
Add some tests for a helper function that lets us
23
from bzrlib import (
2474.1.63 by John Arbash Meinel
Found a small bug in the python version of _read_dirblocks.
24
    dirstate,
3640.2.5 by John Arbash Meinel
Change from using AssertionError to using DirstateCorrupt in a few places
25
    errors,
4132.2.4 by Ian Clatworthy
John's enhancements/fixes to the tests
26
    osutils,
2474.1.7 by John Arbash Meinel
Add some tests for a helper function that lets us
27
    tests,
28
    )
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
29
from bzrlib.tests import (
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
30
    test_dirstate,
5559.2.2 by Martin Pool
Change to using standard load_tests_apply_scenarios.
31
    )
32
from bzrlib.tests.test_osutils import dir_reader_scenarios
33
from bzrlib.tests.scenarios import (
34
    load_tests_apply_scenarios,
35
    multiply_scenarios,
36
    )
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
37
from bzrlib.tests import (
38
    features,
39
    )
5559.2.2 by Martin Pool
Change to using standard load_tests_apply_scenarios.
40
41
42
load_tests = load_tests_apply_scenarios
4241.14.19 by Vincent Ladeuil
Modernize dirstate helpers tests parametrization.
43
44
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
45
compiled_dirstate_helpers_feature = features.ModuleAvailableFeature(
5559.2.2 by Martin Pool
Change to using standard load_tests_apply_scenarios.
46
    'bzrlib._dirstate_helpers_pyx')
47
48
49
# FIXME: we should also parametrize against SHA1Provider !
50
51
ue_scenarios = [('dirstate_Python',
52
    {'update_entry': dirstate.py_update_entry})]
53
if compiled_dirstate_helpers_feature.available():
54
    update_entry = compiled_dirstate_helpers_feature.module.update_entry
55
    ue_scenarios.append(('dirstate_Pyrex', {'update_entry': update_entry}))
56
57
pe_scenarios = [('dirstate_Python',
58
    {'_process_entry': dirstate.ProcessEntryPython})]
59
if compiled_dirstate_helpers_feature.available():
60
    process_entry = compiled_dirstate_helpers_feature.module.ProcessEntryC
61
    pe_scenarios.append(('dirstate_Pyrex', {'_process_entry': process_entry}))
2474.1.7 by John Arbash Meinel
Add some tests for a helper function that lets us
62
63
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
64
class TestBisectPathMixin(object):
2474.1.66 by John Arbash Meinel
Some restructuring.
65
    """Test that _bisect_path_*() returns the expected values.
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
66
2474.1.66 by John Arbash Meinel
Some restructuring.
67
    _bisect_path_* is intended to work like bisect.bisect_*() except it
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
68
    knows it is working on paths that are sorted by ('path', 'to', 'foo')
69
    chunks rather than by raw 'path/to/foo'.
2474.1.68 by John Arbash Meinel
Review feedback from Martin, mostly documentation updates.
70
71
    Test Cases should inherit from this and override ``get_bisect_path`` return
72
    their implementation, and ``get_bisect`` to return the matching
73
    bisect.bisect_* function.
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
74
    """
75
76
    def get_bisect_path(self):
2474.1.66 by John Arbash Meinel
Some restructuring.
77
        """Return an implementation of _bisect_path_*"""
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
78
        raise NotImplementedError
79
80
    def get_bisect(self):
81
        """Return a version of bisect.bisect_*.
82
83
        Also, for the 'exists' check, return the offset to the real values.
84
        For example bisect_left returns the index of an entry, while
85
        bisect_right returns the index *after* an entry
86
87
        :return: (bisect_func, offset)
88
        """
89
        raise NotImplementedError
90
91
    def assertBisect(self, paths, split_paths, path, exists=True):
92
        """Assert that bisect_split works like bisect_left on the split paths.
93
94
        :param paths: A list of path names
95
        :param split_paths: A list of path names that are already split up by directory
96
            ('path/to/foo' => ('path', 'to', 'foo'))
97
        :param path: The path we are indexing.
98
        :param exists: The path should be present, so make sure the
99
            final location actually points to the right value.
100
101
        All other arguments will be passed along.
102
        """
103
        bisect_path = self.get_bisect_path()
104
        self.assertIsInstance(paths, list)
105
        bisect_path_idx = bisect_path(paths, path)
106
        split_path = self.split_for_dirblocks([path])[0]
107
        bisect_func, offset = self.get_bisect()
108
        bisect_split_idx = bisect_func(split_paths, split_path)
109
        self.assertEqual(bisect_split_idx, bisect_path_idx,
110
                         '%s disagreed. %s != %s'
111
                         ' for key %r'
112
                         % (bisect_path.__name__,
113
                            bisect_split_idx, bisect_path_idx, path)
114
                         )
115
        if exists:
116
            self.assertEqual(path, paths[bisect_path_idx+offset])
117
118
    def split_for_dirblocks(self, paths):
119
        dir_split_paths = []
120
        for path in paths:
121
            dirname, basename = os.path.split(path)
122
            dir_split_paths.append((dirname.split('/'), basename))
123
        dir_split_paths.sort()
124
        return dir_split_paths
125
126
    def test_simple(self):
127
        """In the simple case it works just like bisect_left"""
128
        paths = ['', 'a', 'b', 'c', 'd']
129
        split_paths = self.split_for_dirblocks(paths)
130
        for path in paths:
131
            self.assertBisect(paths, split_paths, path, exists=True)
132
        self.assertBisect(paths, split_paths, '_', exists=False)
133
        self.assertBisect(paths, split_paths, 'aa', exists=False)
134
        self.assertBisect(paths, split_paths, 'bb', exists=False)
135
        self.assertBisect(paths, split_paths, 'cc', exists=False)
136
        self.assertBisect(paths, split_paths, 'dd', exists=False)
137
        self.assertBisect(paths, split_paths, 'a/a', exists=False)
138
        self.assertBisect(paths, split_paths, 'b/b', exists=False)
139
        self.assertBisect(paths, split_paths, 'c/c', exists=False)
140
        self.assertBisect(paths, split_paths, 'd/d', exists=False)
141
142
    def test_involved(self):
143
        """This is where bisect_path_* diverges slightly."""
144
        # This is the list of paths and their contents
145
        # a/
146
        #   a/
147
        #     a
148
        #     z
149
        #   a-a/
150
        #     a
151
        #   a-z/
152
        #     z
153
        #   a=a/
154
        #     a
155
        #   a=z/
156
        #     z
157
        #   z/
158
        #     a
159
        #     z
160
        #   z-a
161
        #   z-z
162
        #   z=a
163
        #   z=z
164
        # a-a/
165
        #   a
166
        # a-z/
167
        #   z
168
        # a=a/
169
        #   a
170
        # a=z/
171
        #   z
172
        # This is the exact order that is stored by dirstate
173
        # All children in a directory are mentioned before an children of
174
        # children are mentioned.
175
        # So all the root-directory paths, then all the
176
        # first sub directory, etc.
177
        paths = [# content of '/'
178
                 '', 'a', 'a-a', 'a-z', 'a=a', 'a=z',
179
                 # content of 'a/'
180
                 'a/a', 'a/a-a', 'a/a-z',
181
                 'a/a=a', 'a/a=z',
182
                 'a/z', 'a/z-a', 'a/z-z',
183
                 'a/z=a', 'a/z=z',
184
                 # content of 'a/a/'
185
                 'a/a/a', 'a/a/z',
186
                 # content of 'a/a-a'
187
                 'a/a-a/a',
188
                 # content of 'a/a-z'
189
                 'a/a-z/z',
190
                 # content of 'a/a=a'
191
                 'a/a=a/a',
192
                 # content of 'a/a=z'
193
                 'a/a=z/z',
194
                 # content of 'a/z/'
195
                 'a/z/a', 'a/z/z',
196
                 # content of 'a-a'
197
                 'a-a/a',
198
                 # content of 'a-z'
199
                 'a-z/z',
200
                 # content of 'a=a'
201
                 'a=a/a',
202
                 # content of 'a=z'
203
                 'a=z/z',
204
                ]
205
        split_paths = self.split_for_dirblocks(paths)
206
        sorted_paths = []
207
        for dir_parts, basename in split_paths:
208
            if dir_parts == ['']:
209
                sorted_paths.append(basename)
210
            else:
211
                sorted_paths.append('/'.join(dir_parts + [basename]))
212
213
        self.assertEqual(sorted_paths, paths)
214
215
        for path in paths:
216
            self.assertBisect(paths, split_paths, path, exists=True)
217
218
219
class TestBisectPathLeft(tests.TestCase, TestBisectPathMixin):
4459.2.3 by Vincent Ladeuil
Fix failing benchmarks for dirstate helpers.
220
    """Run all Bisect Path tests against _bisect_path_left."""
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
221
222
    def get_bisect_path(self):
4459.2.2 by Vincent Ladeuil
Use the same method or function names for _dirstate_helpers in pyrex and
223
        from bzrlib._dirstate_helpers_py import _bisect_path_left
224
        return _bisect_path_left
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
225
226
    def get_bisect(self):
227
        return bisect.bisect_left, 0
228
229
230
class TestCompiledBisectPathLeft(TestBisectPathLeft):
4459.2.3 by Vincent Ladeuil
Fix failing benchmarks for dirstate helpers.
231
    """Run all Bisect Path tests against _bisect_path_lect"""
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
232
4913.2.20 by John Arbash Meinel
Change all of the compiled_foo to compiled_foo_feature
233
    _test_needs_features = [compiled_dirstate_helpers_feature]
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
234
235
    def get_bisect_path(self):
4459.2.2 by Vincent Ladeuil
Use the same method or function names for _dirstate_helpers in pyrex and
236
        from bzrlib._dirstate_helpers_pyx import _bisect_path_left
237
        return _bisect_path_left
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
238
239
240
class TestBisectPathRight(tests.TestCase, TestBisectPathMixin):
4459.2.3 by Vincent Ladeuil
Fix failing benchmarks for dirstate helpers.
241
    """Run all Bisect Path tests against _bisect_path_right"""
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
242
243
    def get_bisect_path(self):
4459.2.2 by Vincent Ladeuil
Use the same method or function names for _dirstate_helpers in pyrex and
244
        from bzrlib._dirstate_helpers_py import _bisect_path_right
245
        return _bisect_path_right
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
246
247
    def get_bisect(self):
248
        return bisect.bisect_right, -1
249
250
251
class TestCompiledBisectPathRight(TestBisectPathRight):
4459.2.3 by Vincent Ladeuil
Fix failing benchmarks for dirstate helpers.
252
    """Run all Bisect Path tests against _bisect_path_right"""
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
253
4913.2.20 by John Arbash Meinel
Change all of the compiled_foo to compiled_foo_feature
254
    _test_needs_features = [compiled_dirstate_helpers_feature]
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
255
256
    def get_bisect_path(self):
4459.2.2 by Vincent Ladeuil
Use the same method or function names for _dirstate_helpers in pyrex and
257
        from bzrlib._dirstate_helpers_pyx import _bisect_path_right
258
        return _bisect_path_right
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
259
260
261
class TestBisectDirblock(tests.TestCase):
262
    """Test that bisect_dirblock() returns the expected values.
263
264
    bisect_dirblock is intended to work like bisect.bisect_left() except it
265
    knows it is working on dirblocks and that dirblocks are sorted by ('path',
266
    'to', 'foo') chunks rather than by raw 'path/to/foo'.
2474.1.68 by John Arbash Meinel
Review feedback from Martin, mostly documentation updates.
267
268
    This test is parameterized by calling get_bisect_dirblock(). Child test
269
    cases can override this function to test against a different
270
    implementation.
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
271
    """
272
273
    def get_bisect_dirblock(self):
274
        """Return an implementation of bisect_dirblock"""
4459.2.2 by Vincent Ladeuil
Use the same method or function names for _dirstate_helpers in pyrex and
275
        from bzrlib._dirstate_helpers_py import bisect_dirblock
276
        return bisect_dirblock
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
277
278
    def assertBisect(self, dirblocks, split_dirblocks, path, *args, **kwargs):
279
        """Assert that bisect_split works like bisect_left on the split paths.
280
281
        :param dirblocks: A list of (path, [info]) pairs.
282
        :param split_dirblocks: A list of ((split, path), [info]) pairs.
283
        :param path: The path we are indexing.
284
285
        All other arguments will be passed along.
286
        """
287
        bisect_dirblock = self.get_bisect_dirblock()
288
        self.assertIsInstance(dirblocks, list)
289
        bisect_split_idx = bisect_dirblock(dirblocks, path, *args, **kwargs)
290
        split_dirblock = (path.split('/'), [])
291
        bisect_left_idx = bisect.bisect_left(split_dirblocks, split_dirblock,
292
                                             *args)
293
        self.assertEqual(bisect_left_idx, bisect_split_idx,
294
                         'bisect_split disagreed. %s != %s'
295
                         ' for key %r'
296
                         % (bisect_left_idx, bisect_split_idx, path)
297
                         )
298
299
    def paths_to_dirblocks(self, paths):
300
        """Convert a list of paths into dirblock form.
301
302
        Also, ensure that the paths are in proper sorted order.
303
        """
304
        dirblocks = [(path, []) for path in paths]
305
        split_dirblocks = [(path.split('/'), []) for path in paths]
306
        self.assertEqual(sorted(split_dirblocks), split_dirblocks)
307
        return dirblocks, split_dirblocks
308
309
    def test_simple(self):
310
        """In the simple case it works just like bisect_left"""
311
        paths = ['', 'a', 'b', 'c', 'd']
312
        dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
313
        for path in paths:
314
            self.assertBisect(dirblocks, split_dirblocks, path)
315
        self.assertBisect(dirblocks, split_dirblocks, '_')
316
        self.assertBisect(dirblocks, split_dirblocks, 'aa')
317
        self.assertBisect(dirblocks, split_dirblocks, 'bb')
318
        self.assertBisect(dirblocks, split_dirblocks, 'cc')
319
        self.assertBisect(dirblocks, split_dirblocks, 'dd')
320
        self.assertBisect(dirblocks, split_dirblocks, 'a/a')
321
        self.assertBisect(dirblocks, split_dirblocks, 'b/b')
322
        self.assertBisect(dirblocks, split_dirblocks, 'c/c')
323
        self.assertBisect(dirblocks, split_dirblocks, 'd/d')
324
325
    def test_involved(self):
326
        """This is where bisect_left diverges slightly."""
327
        paths = ['', 'a',
328
                 'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
329
                 'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
330
                 'a-a', 'a-z',
331
                 'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
332
                 'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
333
                 'z-a', 'z-z',
334
                ]
335
        dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
336
        for path in paths:
337
            self.assertBisect(dirblocks, split_dirblocks, path)
338
339
    def test_involved_cached(self):
340
        """This is where bisect_left diverges slightly."""
341
        paths = ['', 'a',
342
                 'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
343
                 'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
344
                 'a-a', 'a-z',
345
                 'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
346
                 'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
347
                 'z-a', 'z-z',
348
                ]
349
        cache = {}
350
        dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
351
        for path in paths:
352
            self.assertBisect(dirblocks, split_dirblocks, path, cache=cache)
353
354
355
class TestCompiledBisectDirblock(TestBisectDirblock):
356
    """Test that bisect_dirblock() returns the expected values.
357
358
    bisect_dirblock is intended to work like bisect.bisect_left() except it
359
    knows it is working on dirblocks and that dirblocks are sorted by ('path',
360
    'to', 'foo') chunks rather than by raw 'path/to/foo'.
361
362
    This runs all the normal tests that TestBisectDirblock did, but uses the
363
    compiled version.
364
    """
365
4913.2.20 by John Arbash Meinel
Change all of the compiled_foo to compiled_foo_feature
366
    _test_needs_features = [compiled_dirstate_helpers_feature]
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
367
368
    def get_bisect_dirblock(self):
4459.2.2 by Vincent Ladeuil
Use the same method or function names for _dirstate_helpers in pyrex and
369
        from bzrlib._dirstate_helpers_pyx import bisect_dirblock
370
        return bisect_dirblock
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
371
372
2474.1.57 by John Arbash Meinel
Move code around to refactor according to our pyrex extension design.
373
class TestCmpByDirs(tests.TestCase):
2474.1.68 by John Arbash Meinel
Review feedback from Martin, mostly documentation updates.
374
    """Test an implementation of cmp_by_dirs()
375
376
    cmp_by_dirs() compares 2 paths by their directory sections, rather than as
377
    plain strings.
378
379
    Child test cases can override ``get_cmp_by_dirs`` to test a specific
380
    implementation.
381
    """
2474.1.57 by John Arbash Meinel
Move code around to refactor according to our pyrex extension design.
382
383
    def get_cmp_by_dirs(self):
384
        """Get a specific implementation of cmp_by_dirs."""
4459.2.2 by Vincent Ladeuil
Use the same method or function names for _dirstate_helpers in pyrex and
385
        from bzrlib._dirstate_helpers_py import cmp_by_dirs
386
        return cmp_by_dirs
2474.1.57 by John Arbash Meinel
Move code around to refactor according to our pyrex extension design.
387
388
    def assertCmpByDirs(self, expected, str1, str2):
389
        """Compare the two strings, in both directions.
390
391
        :param expected: The expected comparison value. -1 means str1 comes
392
            first, 0 means they are equal, 1 means str2 comes first
393
        :param str1: string to compare
394
        :param str2: string to compare
395
        """
396
        cmp_by_dirs = self.get_cmp_by_dirs()
397
        if expected == 0:
398
            self.assertEqual(str1, str2)
399
            self.assertEqual(0, cmp_by_dirs(str1, str2))
400
            self.assertEqual(0, cmp_by_dirs(str2, str1))
401
        elif expected > 0:
402
            self.assertPositive(cmp_by_dirs(str1, str2))
403
            self.assertNegative(cmp_by_dirs(str2, str1))
404
        else:
405
            self.assertNegative(cmp_by_dirs(str1, str2))
406
            self.assertPositive(cmp_by_dirs(str2, str1))
407
408
    def test_cmp_empty(self):
409
        """Compare against the empty string."""
410
        self.assertCmpByDirs(0, '', '')
411
        self.assertCmpByDirs(1, 'a', '')
412
        self.assertCmpByDirs(1, 'ab', '')
413
        self.assertCmpByDirs(1, 'abc', '')
414
        self.assertCmpByDirs(1, 'abcd', '')
415
        self.assertCmpByDirs(1, 'abcde', '')
416
        self.assertCmpByDirs(1, 'abcdef', '')
417
        self.assertCmpByDirs(1, 'abcdefg', '')
418
        self.assertCmpByDirs(1, 'abcdefgh', '')
419
        self.assertCmpByDirs(1, 'abcdefghi', '')
420
        self.assertCmpByDirs(1, 'test/ing/a/path/', '')
421
422
    def test_cmp_same_str(self):
423
        """Compare the same string"""
424
        self.assertCmpByDirs(0, 'a', 'a')
425
        self.assertCmpByDirs(0, 'ab', 'ab')
426
        self.assertCmpByDirs(0, 'abc', 'abc')
427
        self.assertCmpByDirs(0, 'abcd', 'abcd')
428
        self.assertCmpByDirs(0, 'abcde', 'abcde')
429
        self.assertCmpByDirs(0, 'abcdef', 'abcdef')
430
        self.assertCmpByDirs(0, 'abcdefg', 'abcdefg')
431
        self.assertCmpByDirs(0, 'abcdefgh', 'abcdefgh')
432
        self.assertCmpByDirs(0, 'abcdefghi', 'abcdefghi')
433
        self.assertCmpByDirs(0, 'testing a long string', 'testing a long string')
434
        self.assertCmpByDirs(0, 'x'*10000, 'x'*10000)
435
        self.assertCmpByDirs(0, 'a/b', 'a/b')
436
        self.assertCmpByDirs(0, 'a/b/c', 'a/b/c')
437
        self.assertCmpByDirs(0, 'a/b/c/d', 'a/b/c/d')
438
        self.assertCmpByDirs(0, 'a/b/c/d/e', 'a/b/c/d/e')
439
440
    def test_simple_paths(self):
441
        """Compare strings that act like normal string comparison"""
442
        self.assertCmpByDirs(-1, 'a', 'b')
443
        self.assertCmpByDirs(-1, 'aa', 'ab')
444
        self.assertCmpByDirs(-1, 'ab', 'bb')
445
        self.assertCmpByDirs(-1, 'aaa', 'aab')
446
        self.assertCmpByDirs(-1, 'aab', 'abb')
447
        self.assertCmpByDirs(-1, 'abb', 'bbb')
448
        self.assertCmpByDirs(-1, 'aaaa', 'aaab')
449
        self.assertCmpByDirs(-1, 'aaab', 'aabb')
450
        self.assertCmpByDirs(-1, 'aabb', 'abbb')
451
        self.assertCmpByDirs(-1, 'abbb', 'bbbb')
452
        self.assertCmpByDirs(-1, 'aaaaa', 'aaaab')
453
        self.assertCmpByDirs(-1, 'a/a', 'a/b')
454
        self.assertCmpByDirs(-1, 'a/b', 'b/b')
455
        self.assertCmpByDirs(-1, 'a/a/a', 'a/a/b')
456
        self.assertCmpByDirs(-1, 'a/a/b', 'a/b/b')
457
        self.assertCmpByDirs(-1, 'a/b/b', 'b/b/b')
458
        self.assertCmpByDirs(-1, 'a/a/a/a', 'a/a/a/b')
459
        self.assertCmpByDirs(-1, 'a/a/a/b', 'a/a/b/b')
460
        self.assertCmpByDirs(-1, 'a/a/b/b', 'a/b/b/b')
461
        self.assertCmpByDirs(-1, 'a/b/b/b', 'b/b/b/b')
462
        self.assertCmpByDirs(-1, 'a/a/a/a/a', 'a/a/a/a/b')
463
464
    def test_tricky_paths(self):
465
        self.assertCmpByDirs(1, 'ab/cd/ef', 'ab/cc/ef')
466
        self.assertCmpByDirs(1, 'ab/cd/ef', 'ab/c/ef')
467
        self.assertCmpByDirs(-1, 'ab/cd/ef', 'ab/cd-ef')
468
        self.assertCmpByDirs(-1, 'ab/cd', 'ab/cd-')
469
        self.assertCmpByDirs(-1, 'ab/cd', 'ab-cd')
470
2474.1.70 by John Arbash Meinel
Lot's of fixes from Martin's comments.
471
    def test_cmp_unicode_not_allowed(self):
472
        cmp_by_dirs = self.get_cmp_by_dirs()
473
        self.assertRaises(TypeError, cmp_by_dirs, u'Unicode', 'str')
474
        self.assertRaises(TypeError, cmp_by_dirs, 'str', u'Unicode')
475
        self.assertRaises(TypeError, cmp_by_dirs, u'Unicode', u'Unicode')
476
477
    def test_cmp_non_ascii(self):
478
        self.assertCmpByDirs(-1, '\xc2\xb5', '\xc3\xa5') # u'\xb5', u'\xe5'
479
        self.assertCmpByDirs(-1, 'a', '\xc3\xa5') # u'a', u'\xe5'
480
        self.assertCmpByDirs(-1, 'b', '\xc2\xb5') # u'b', u'\xb5'
481
        self.assertCmpByDirs(-1, 'a/b', 'a/\xc3\xa5') # u'a/b', u'a/\xe5'
482
        self.assertCmpByDirs(-1, 'b/a', 'b/\xc2\xb5') # u'b/a', u'b/\xb5'
483
2474.1.57 by John Arbash Meinel
Move code around to refactor according to our pyrex extension design.
484
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
485
class TestCompiledCmpByDirs(TestCmpByDirs):
486
    """Test the pyrex implementation of cmp_by_dirs"""
2474.1.7 by John Arbash Meinel
Add some tests for a helper function that lets us
487
4913.2.20 by John Arbash Meinel
Change all of the compiled_foo to compiled_foo_feature
488
    _test_needs_features = [compiled_dirstate_helpers_feature]
2474.1.7 by John Arbash Meinel
Add some tests for a helper function that lets us
489
2474.1.41 by John Arbash Meinel
Change the name of cmp_dirblock_strings to cmp_by_dirs
490
    def get_cmp_by_dirs(self):
4459.2.2 by Vincent Ladeuil
Use the same method or function names for _dirstate_helpers in pyrex and
491
        from bzrlib._dirstate_helpers_pyx import cmp_by_dirs
492
        return cmp_by_dirs
2474.1.57 by John Arbash Meinel
Move code around to refactor according to our pyrex extension design.
493
494
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
495
class TestCmpPathByDirblock(tests.TestCase):
2474.1.68 by John Arbash Meinel
Review feedback from Martin, mostly documentation updates.
496
    """Test an implementation of _cmp_path_by_dirblock()
497
498
    _cmp_path_by_dirblock() compares two paths using the sort order used by
499
    DirState. All paths in the same directory are sorted together.
500
501
    Child test cases can override ``get_cmp_path_by_dirblock`` to test a specific
502
    implementation.
503
    """
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
504
505
    def get_cmp_path_by_dirblock(self):
2474.1.66 by John Arbash Meinel
Some restructuring.
506
        """Get a specific implementation of _cmp_path_by_dirblock."""
4459.2.2 by Vincent Ladeuil
Use the same method or function names for _dirstate_helpers in pyrex and
507
        from bzrlib._dirstate_helpers_py import _cmp_path_by_dirblock
508
        return _cmp_path_by_dirblock
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
509
510
    def assertCmpPathByDirblock(self, paths):
511
        """Compare all paths and make sure they evaluate to the correct order.
512
513
        This does N^2 comparisons. It is assumed that ``paths`` is properly
514
        sorted list.
515
516
        :param paths: a sorted list of paths to compare
517
        """
518
        # First, make sure the paths being passed in are correct
519
        def _key(p):
520
            dirname, basename = os.path.split(p)
521
            return dirname.split('/'), basename
522
        self.assertEqual(sorted(paths, key=_key), paths)
523
524
        cmp_path_by_dirblock = self.get_cmp_path_by_dirblock()
525
        for idx1, path1 in enumerate(paths):
526
            for idx2, path2 in enumerate(paths):
527
                cmp_val = cmp_path_by_dirblock(path1, path2)
528
                if idx1 < idx2:
529
                    self.assertTrue(cmp_val < 0,
530
                        '%s did not state that %r came before %r, cmp=%s'
531
                        % (cmp_path_by_dirblock.__name__,
532
                           path1, path2, cmp_val))
533
                elif idx1 > idx2:
534
                    self.assertTrue(cmp_val > 0,
535
                        '%s did not state that %r came after %r, cmp=%s'
536
                        % (cmp_path_by_dirblock.__name__,
537
                           path1, path2, cmp_val))
538
                else: # idx1 == idx2
539
                    self.assertTrue(cmp_val == 0,
540
                        '%s did not state that %r == %r, cmp=%s'
541
                        % (cmp_path_by_dirblock.__name__,
542
                           path1, path2, cmp_val))
543
544
    def test_cmp_simple_paths(self):
545
        """Compare against the empty string."""
546
        self.assertCmpPathByDirblock(['', 'a', 'ab', 'abc', 'a/b/c', 'b/d/e'])
547
        self.assertCmpPathByDirblock(['kl', 'ab/cd', 'ab/ef', 'gh/ij'])
548
549
    def test_tricky_paths(self):
550
        self.assertCmpPathByDirblock([
551
            # Contents of ''
552
            '', 'a', 'a-a', 'a=a', 'b',
553
            # Contents of 'a'
554
            'a/a', 'a/a-a', 'a/a=a', 'a/b',
555
            # Contents of 'a/a'
556
            'a/a/a', 'a/a/a-a', 'a/a/a=a',
557
            # Contents of 'a/a/a'
558
            'a/a/a/a', 'a/a/a/b',
559
            # Contents of 'a/a/a-a',
560
            'a/a/a-a/a', 'a/a/a-a/b',
561
            # Contents of 'a/a/a=a',
562
            'a/a/a=a/a', 'a/a/a=a/b',
563
            # Contents of 'a/a-a'
564
            'a/a-a/a',
565
            # Contents of 'a/a-a/a'
566
            'a/a-a/a/a', 'a/a-a/a/b',
567
            # Contents of 'a/a=a'
568
            'a/a=a/a',
569
            # Contents of 'a/b'
570
            'a/b/a', 'a/b/b',
571
            # Contents of 'a-a',
572
            'a-a/a', 'a-a/b',
573
            # Contents of 'a=a',
574
            'a=a/a', 'a=a/b',
575
            # Contents of 'b',
576
            'b/a', 'b/b',
577
            ])
578
        self.assertCmpPathByDirblock([
579
                 # content of '/'
580
                 '', 'a', 'a-a', 'a-z', 'a=a', 'a=z',
581
                 # content of 'a/'
582
                 'a/a', 'a/a-a', 'a/a-z',
583
                 'a/a=a', 'a/a=z',
584
                 'a/z', 'a/z-a', 'a/z-z',
585
                 'a/z=a', 'a/z=z',
586
                 # content of 'a/a/'
587
                 'a/a/a', 'a/a/z',
588
                 # content of 'a/a-a'
589
                 'a/a-a/a',
590
                 # content of 'a/a-z'
591
                 'a/a-z/z',
592
                 # content of 'a/a=a'
593
                 'a/a=a/a',
594
                 # content of 'a/a=z'
595
                 'a/a=z/z',
596
                 # content of 'a/z/'
597
                 'a/z/a', 'a/z/z',
598
                 # content of 'a-a'
599
                 'a-a/a',
600
                 # content of 'a-z'
601
                 'a-z/z',
602
                 # content of 'a=a'
603
                 'a=a/a',
604
                 # content of 'a=z'
605
                 'a=z/z',
606
                ])
607
2474.1.70 by John Arbash Meinel
Lot's of fixes from Martin's comments.
608
    def test_unicode_not_allowed(self):
609
        cmp_path_by_dirblock = self.get_cmp_path_by_dirblock()
610
        self.assertRaises(TypeError, cmp_path_by_dirblock, u'Uni', 'str')
611
        self.assertRaises(TypeError, cmp_path_by_dirblock, 'str', u'Uni')
612
        self.assertRaises(TypeError, cmp_path_by_dirblock, u'Uni', u'Uni')
613
        self.assertRaises(TypeError, cmp_path_by_dirblock, u'x/Uni', 'x/str')
614
        self.assertRaises(TypeError, cmp_path_by_dirblock, 'x/str', u'x/Uni')
615
        self.assertRaises(TypeError, cmp_path_by_dirblock, u'x/Uni', u'x/Uni')
616
617
    def test_nonascii(self):
618
        self.assertCmpPathByDirblock([
619
            # content of '/'
620
            '', 'a', '\xc2\xb5', '\xc3\xa5',
621
            # content of 'a'
622
            'a/a', 'a/\xc2\xb5', 'a/\xc3\xa5',
623
            # content of 'a/a'
624
            'a/a/a', 'a/a/\xc2\xb5', 'a/a/\xc3\xa5',
625
            # content of 'a/\xc2\xb5'
626
            'a/\xc2\xb5/a', 'a/\xc2\xb5/\xc2\xb5', 'a/\xc2\xb5/\xc3\xa5',
627
            # content of 'a/\xc3\xa5'
628
            'a/\xc3\xa5/a', 'a/\xc3\xa5/\xc2\xb5', 'a/\xc3\xa5/\xc3\xa5',
629
            # content of '\xc2\xb5'
630
            '\xc2\xb5/a', '\xc2\xb5/\xc2\xb5', '\xc2\xb5/\xc3\xa5',
631
            # content of '\xc2\xe5'
632
            '\xc3\xa5/a', '\xc3\xa5/\xc2\xb5', '\xc3\xa5/\xc3\xa5',
633
            ])
634
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
635
636
class TestCompiledCmpPathByDirblock(TestCmpPathByDirblock):
2474.1.66 by John Arbash Meinel
Some restructuring.
637
    """Test the pyrex implementation of _cmp_path_by_dirblock"""
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
638
4913.2.20 by John Arbash Meinel
Change all of the compiled_foo to compiled_foo_feature
639
    _test_needs_features = [compiled_dirstate_helpers_feature]
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
640
641
    def get_cmp_by_dirs(self):
4459.2.2 by Vincent Ladeuil
Use the same method or function names for _dirstate_helpers in pyrex and
642
        from bzrlib._dirstate_helpers_pyx import _cmp_path_by_dirblock
643
        return _cmp_path_by_dirblock
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
644
645
646
class TestMemRChr(tests.TestCase):
2474.1.66 by John Arbash Meinel
Some restructuring.
647
    """Test memrchr functionality"""
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
648
4913.2.20 by John Arbash Meinel
Change all of the compiled_foo to compiled_foo_feature
649
    _test_needs_features = [compiled_dirstate_helpers_feature]
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
650
651
    def assertMemRChr(self, expected, s, c):
4459.2.1 by Vincent Ladeuil
Use a consistent scheme for naming pyrex source files.
652
        from bzrlib._dirstate_helpers_pyx import _py_memrchr
2474.1.58 by John Arbash Meinel
(broken) Try to properly implement DirState._bisect*
653
        self.assertEqual(expected, _py_memrchr(s, c))
654
655
    def test_missing(self):
656
        self.assertMemRChr(None, '', 'a')
657
        self.assertMemRChr(None, '', 'c')
658
        self.assertMemRChr(None, 'abcdefghijklm', 'q')
659
        self.assertMemRChr(None, 'aaaaaaaaaaaaaaaaaaaaaaa', 'b')
660
661
    def test_single_entry(self):
662
        self.assertMemRChr(0, 'abcdefghijklm', 'a')
663
        self.assertMemRChr(1, 'abcdefghijklm', 'b')
664
        self.assertMemRChr(2, 'abcdefghijklm', 'c')
665
        self.assertMemRChr(10, 'abcdefghijklm', 'k')
666
        self.assertMemRChr(11, 'abcdefghijklm', 'l')
667
        self.assertMemRChr(12, 'abcdefghijklm', 'm')
668
669
    def test_multiple(self):
670
        self.assertMemRChr(10, 'abcdefjklmabcdefghijklm', 'a')
671
        self.assertMemRChr(11, 'abcdefjklmabcdefghijklm', 'b')
672
        self.assertMemRChr(12, 'abcdefjklmabcdefghijklm', 'c')
673
        self.assertMemRChr(20, 'abcdefjklmabcdefghijklm', 'k')
674
        self.assertMemRChr(21, 'abcdefjklmabcdefghijklm', 'l')
675
        self.assertMemRChr(22, 'abcdefjklmabcdefghijklm', 'm')
676
        self.assertMemRChr(22, 'aaaaaaaaaaaaaaaaaaaaaaa', 'a')
677
678
    def test_with_nulls(self):
679
        self.assertMemRChr(10, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'a')
680
        self.assertMemRChr(11, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'b')
681
        self.assertMemRChr(12, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'c')
682
        self.assertMemRChr(20, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'k')
683
        self.assertMemRChr(21, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'l')
684
        self.assertMemRChr(22, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'm')
685
        self.assertMemRChr(22, 'aaa\0\0\0aaaaaaa\0\0\0aaaaaaa', 'a')
686
        self.assertMemRChr(9, '\0\0\0\0\0\0\0\0\0\0', '\0')
2474.1.63 by John Arbash Meinel
Found a small bug in the python version of _read_dirblocks.
687
688
689
class TestReadDirblocks(test_dirstate.TestCaseWithDirState):
2474.1.68 by John Arbash Meinel
Review feedback from Martin, mostly documentation updates.
690
    """Test an implementation of _read_dirblocks()
691
692
    _read_dirblocks() reads in all of the dirblock information from the disk
693
    file.
694
695
    Child test cases can override ``get_read_dirblocks`` to test a specific
696
    implementation.
697
    """
2474.1.63 by John Arbash Meinel
Found a small bug in the python version of _read_dirblocks.
698
5559.2.2 by Martin Pool
Change to using standard load_tests_apply_scenarios.
699
    # inherits scenarios from test_dirstate
700
2474.1.63 by John Arbash Meinel
Found a small bug in the python version of _read_dirblocks.
701
    def get_read_dirblocks(self):
4459.2.2 by Vincent Ladeuil
Use the same method or function names for _dirstate_helpers in pyrex and
702
        from bzrlib._dirstate_helpers_py import _read_dirblocks
703
        return _read_dirblocks
2474.1.63 by John Arbash Meinel
Found a small bug in the python version of _read_dirblocks.
704
705
    def test_smoketest(self):
706
        """Make sure that we can create and read back a simple file."""
707
        tree, state, expected = self.create_basic_dirstate()
708
        del tree
709
        state._read_header_if_needed()
710
        self.assertEqual(dirstate.DirState.NOT_IN_MEMORY,
711
                         state._dirblock_state)
712
        read_dirblocks = self.get_read_dirblocks()
713
        read_dirblocks(state)
714
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
715
                         state._dirblock_state)
716
3640.2.1 by John Arbash Meinel
More safety checks around PyString_FromStringAndSize,
717
    def test_trailing_garbage(self):
718
        tree, state, expected = self.create_basic_dirstate()
5278.1.2 by Martin Pool
Don't say 'Linux' except when specifically talking about the kernel
719
        # On Unix, we can write extra data as long as we haven't read yet, but
4789.21.1 by John Arbash Meinel
It seems that on Win32, if you open a file with FILE_SHARE_READ
720
        # on Win32, if you've opened the file with FILE_SHARE_READ, trying to
721
        # open it in append mode will fail.
722
        state.unlock()
3640.2.1 by John Arbash Meinel
More safety checks around PyString_FromStringAndSize,
723
        f = open('dirstate', 'ab')
724
        try:
725
            # Add bogus trailing garbage
726
            f.write('bogus\n')
727
        finally:
728
            f.close()
4789.21.1 by John Arbash Meinel
It seems that on Win32, if you open a file with FILE_SHARE_READ
729
            state.lock_read()
3640.2.5 by John Arbash Meinel
Change from using AssertionError to using DirstateCorrupt in a few places
730
        e = self.assertRaises(errors.DirstateCorrupt,
731
                              state._read_dirblocks_if_needed)
3640.2.1 by John Arbash Meinel
More safety checks around PyString_FromStringAndSize,
732
        # Make sure we mention the bogus characters in the error
733
        self.assertContainsRe(str(e), 'bogus')
734
2474.1.63 by John Arbash Meinel
Found a small bug in the python version of _read_dirblocks.
735
736
class TestCompiledReadDirblocks(TestReadDirblocks):
2474.1.68 by John Arbash Meinel
Review feedback from Martin, mostly documentation updates.
737
    """Test the pyrex implementation of _read_dirblocks"""
2474.1.63 by John Arbash Meinel
Found a small bug in the python version of _read_dirblocks.
738
4913.2.20 by John Arbash Meinel
Change all of the compiled_foo to compiled_foo_feature
739
    _test_needs_features = [compiled_dirstate_helpers_feature]
2474.1.63 by John Arbash Meinel
Found a small bug in the python version of _read_dirblocks.
740
741
    def get_read_dirblocks(self):
4459.2.2 by Vincent Ladeuil
Use the same method or function names for _dirstate_helpers in pyrex and
742
        from bzrlib._dirstate_helpers_pyx import _read_dirblocks
743
        return _read_dirblocks
2474.1.66 by John Arbash Meinel
Some restructuring.
744
745
746
class TestUsingCompiledIfAvailable(tests.TestCase):
747
    """Check that any compiled functions that are available are the default.
748
749
    It is possible to have typos, etc in the import line, such that
4459.2.1 by Vincent Ladeuil
Use a consistent scheme for naming pyrex source files.
750
    _dirstate_helpers_pyx is actually available, but the compiled functions are
2474.1.66 by John Arbash Meinel
Some restructuring.
751
    not being used.
752
    """
753
754
    def test_bisect_dirblock(self):
4913.2.20 by John Arbash Meinel
Change all of the compiled_foo to compiled_foo_feature
755
        if compiled_dirstate_helpers_feature.available():
4459.2.2 by Vincent Ladeuil
Use the same method or function names for _dirstate_helpers in pyrex and
756
            from bzrlib._dirstate_helpers_pyx import bisect_dirblock
2474.1.66 by John Arbash Meinel
Some restructuring.
757
        else:
4459.2.2 by Vincent Ladeuil
Use the same method or function names for _dirstate_helpers in pyrex and
758
            from bzrlib._dirstate_helpers_py import bisect_dirblock
759
        self.assertIs(bisect_dirblock, dirstate.bisect_dirblock)
2474.1.66 by John Arbash Meinel
Some restructuring.
760
761
    def test__bisect_path_left(self):
4913.2.20 by John Arbash Meinel
Change all of the compiled_foo to compiled_foo_feature
762
        if compiled_dirstate_helpers_feature.available():
4459.2.2 by Vincent Ladeuil
Use the same method or function names for _dirstate_helpers in pyrex and
763
            from bzrlib._dirstate_helpers_pyx import _bisect_path_left
2474.1.66 by John Arbash Meinel
Some restructuring.
764
        else:
4459.2.2 by Vincent Ladeuil
Use the same method or function names for _dirstate_helpers in pyrex and
765
            from bzrlib._dirstate_helpers_py import _bisect_path_left
766
        self.assertIs(_bisect_path_left, dirstate._bisect_path_left)
2474.1.66 by John Arbash Meinel
Some restructuring.
767
768
    def test__bisect_path_right(self):
4913.2.20 by John Arbash Meinel
Change all of the compiled_foo to compiled_foo_feature
769
        if compiled_dirstate_helpers_feature.available():
4459.2.2 by Vincent Ladeuil
Use the same method or function names for _dirstate_helpers in pyrex and
770
            from bzrlib._dirstate_helpers_pyx import _bisect_path_right
2474.1.66 by John Arbash Meinel
Some restructuring.
771
        else:
4459.2.2 by Vincent Ladeuil
Use the same method or function names for _dirstate_helpers in pyrex and
772
            from bzrlib._dirstate_helpers_py import _bisect_path_right
773
        self.assertIs(_bisect_path_right, dirstate._bisect_path_right)
2474.1.66 by John Arbash Meinel
Some restructuring.
774
775
    def test_cmp_by_dirs(self):
4913.2.20 by John Arbash Meinel
Change all of the compiled_foo to compiled_foo_feature
776
        if compiled_dirstate_helpers_feature.available():
4459.2.2 by Vincent Ladeuil
Use the same method or function names for _dirstate_helpers in pyrex and
777
            from bzrlib._dirstate_helpers_pyx import cmp_by_dirs
2474.1.66 by John Arbash Meinel
Some restructuring.
778
        else:
4459.2.2 by Vincent Ladeuil
Use the same method or function names for _dirstate_helpers in pyrex and
779
            from bzrlib._dirstate_helpers_py import cmp_by_dirs
780
        self.assertIs(cmp_by_dirs, dirstate.cmp_by_dirs)
2474.1.66 by John Arbash Meinel
Some restructuring.
781
782
    def test__read_dirblocks(self):
4913.2.20 by John Arbash Meinel
Change all of the compiled_foo to compiled_foo_feature
783
        if compiled_dirstate_helpers_feature.available():
4459.2.2 by Vincent Ladeuil
Use the same method or function names for _dirstate_helpers in pyrex and
784
            from bzrlib._dirstate_helpers_pyx import _read_dirblocks
2474.1.66 by John Arbash Meinel
Some restructuring.
785
        else:
4459.2.2 by Vincent Ladeuil
Use the same method or function names for _dirstate_helpers in pyrex and
786
            from bzrlib._dirstate_helpers_py import _read_dirblocks
787
        self.assertIs(_read_dirblocks, dirstate._read_dirblocks)
2474.1.66 by John Arbash Meinel
Some restructuring.
788
3696.4.11 by Robert Collins
Some Cification of iter_changes, and making the python one actually work.
789
    def test_update_entry(self):
4913.2.20 by John Arbash Meinel
Change all of the compiled_foo to compiled_foo_feature
790
        if compiled_dirstate_helpers_feature.available():
4459.2.1 by Vincent Ladeuil
Use a consistent scheme for naming pyrex source files.
791
            from bzrlib._dirstate_helpers_pyx import update_entry
3696.4.11 by Robert Collins
Some Cification of iter_changes, and making the python one actually work.
792
        else:
4459.2.2 by Vincent Ladeuil
Use the same method or function names for _dirstate_helpers in pyrex and
793
            from bzrlib.dirstate import update_entry
794
        self.assertIs(update_entry, dirstate.update_entry)
3696.4.11 by Robert Collins
Some Cification of iter_changes, and making the python one actually work.
795
3696.4.5 by Robert Collins
Simple 'compiled with pyrex' ProcessEntry class. faster.
796
    def test_process_entry(self):
4913.2.20 by John Arbash Meinel
Change all of the compiled_foo to compiled_foo_feature
797
        if compiled_dirstate_helpers_feature.available():
4459.2.1 by Vincent Ladeuil
Use a consistent scheme for naming pyrex source files.
798
            from bzrlib._dirstate_helpers_pyx import ProcessEntryC
3696.4.5 by Robert Collins
Simple 'compiled with pyrex' ProcessEntry class. faster.
799
            self.assertIs(ProcessEntryC, dirstate._process_entry)
800
        else:
801
            from bzrlib.dirstate import ProcessEntryPython
802
            self.assertIs(ProcessEntryPython, dirstate._process_entry)
803
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
804
805
class TestUpdateEntry(test_dirstate.TestCaseWithDirState):
806
    """Test the DirState.update_entry functions"""
807
5559.2.2 by Martin Pool
Change to using standard load_tests_apply_scenarios.
808
    scenarios = multiply_scenarios(
809
        dir_reader_scenarios(), ue_scenarios)
810
4241.14.19 by Vincent Ladeuil
Modernize dirstate helpers tests parametrization.
811
    # Set by load_tests
812
    update_entry = None
813
814
    def setUp(self):
815
        super(TestUpdateEntry, self).setUp()
4985.1.5 by Vincent Ladeuil
Deploying the new overrideAttr facility further reduces the complexity
816
        self.overrideAttr(dirstate, 'update_entry', self.update_entry)
4241.14.19 by Vincent Ladeuil
Modernize dirstate helpers tests parametrization.
817
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
818
    def get_state_with_a(self):
819
        """Create a DirState tracking a single object named 'a'"""
820
        state = test_dirstate.InstrumentedDirState.initialize('dirstate')
821
        self.addCleanup(state.unlock)
822
        state.add('a', 'a-id', 'file', None, '')
823
        entry = state._get_entry(0, path_utf8='a')
824
        return state, entry
825
3696.5.1 by Robert Collins
Integrate in stat-cache-updates-during-commit with faster iter-changes.
826
    def test_observed_sha1_cachable(self):
827
        state, entry = self.get_state_with_a()
5807.4.4 by John Arbash Meinel
Assert the transition from _observed_sha1.
828
        state.save()
3696.5.1 by Robert Collins
Integrate in stat-cache-updates-during-commit with faster iter-changes.
829
        atime = time.time() - 10
830
        self.build_tree(['a'])
5807.4.4 by John Arbash Meinel
Assert the transition from _observed_sha1.
831
        statvalue = test_dirstate._FakeStat.from_stat(os.lstat('a'))
832
        statvalue.st_mtime = statvalue.st_ctime = atime
833
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
834
                         state._dirblock_state)
3696.5.1 by Robert Collins
Integrate in stat-cache-updates-during-commit with faster iter-changes.
835
        state._observed_sha1(entry, "foo", statvalue)
836
        self.assertEqual('foo', entry[1][0][1])
837
        packed_stat = dirstate.pack_stat(statvalue)
838
        self.assertEqual(packed_stat, entry[1][0][4])
5807.4.4 by John Arbash Meinel
Assert the transition from _observed_sha1.
839
        self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
840
                         state._dirblock_state)
3696.5.1 by Robert Collins
Integrate in stat-cache-updates-during-commit with faster iter-changes.
841
842
    def test_observed_sha1_not_cachable(self):
843
        state, entry = self.get_state_with_a()
5807.4.4 by John Arbash Meinel
Assert the transition from _observed_sha1.
844
        state.save()
3696.5.1 by Robert Collins
Integrate in stat-cache-updates-during-commit with faster iter-changes.
845
        oldval = entry[1][0][1]
846
        oldstat = entry[1][0][4]
847
        self.build_tree(['a'])
848
        statvalue = os.lstat('a')
5807.4.4 by John Arbash Meinel
Assert the transition from _observed_sha1.
849
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
850
                         state._dirblock_state)
3696.5.1 by Robert Collins
Integrate in stat-cache-updates-during-commit with faster iter-changes.
851
        state._observed_sha1(entry, "foo", statvalue)
852
        self.assertEqual(oldval, entry[1][0][1])
853
        self.assertEqual(oldstat, entry[1][0][4])
5807.4.4 by John Arbash Meinel
Assert the transition from _observed_sha1.
854
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
855
                         state._dirblock_state)
3696.5.1 by Robert Collins
Integrate in stat-cache-updates-during-commit with faster iter-changes.
856
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
857
    def test_update_entry(self):
3696.5.2 by Robert Collins
Integrate less aggressive sha logic with C iter-changes.
858
        state, _ = self.get_state_with_a()
859
        tree = self.make_branch_and_tree('tree')
860
        tree.lock_write()
861
        empty_revid = tree.commit('empty')
862
        self.build_tree(['tree/a'])
863
        tree.add(['a'], ['a-id'])
864
        with_a_id = tree.commit('with_a')
865
        self.addCleanup(tree.unlock)
866
        state.set_parent_trees(
867
            [(empty_revid, tree.branch.repository.revision_tree(empty_revid))],
868
            [])
869
        entry = state._get_entry(0, path_utf8='a')
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
870
        self.build_tree(['a'])
871
        # Add one where we don't provide the stat or sha already
872
        self.assertEqual(('', 'a', 'a-id'), entry[0])
3696.5.2 by Robert Collins
Integrate less aggressive sha logic with C iter-changes.
873
        self.assertEqual(('f', '', 0, False, dirstate.DirState.NULLSTAT),
874
                         entry[1][0])
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
875
        # Flush the buffers to disk
876
        state.save()
877
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
878
                         state._dirblock_state)
879
880
        stat_value = os.lstat('a')
881
        packed_stat = dirstate.pack_stat(stat_value)
882
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
883
                                          stat_value=stat_value)
3696.5.2 by Robert Collins
Integrate less aggressive sha logic with C iter-changes.
884
        self.assertEqual(None, link_or_sha1)
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
885
5802.3.1 by John Arbash Meinel
Fix bug #765881. Having a file added on disk was skipping
886
        # The dirblock entry should not have computed or cached the file's
887
        # sha1, but it did update the files' st_size. However, this is not
888
        # worth writing a dirstate file for, so we leave the state UNMODIFIED
3696.5.2 by Robert Collins
Integrate less aggressive sha logic with C iter-changes.
889
        self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
890
                         entry[1][0])
5802.3.1 by John Arbash Meinel
Fix bug #765881. Having a file added on disk was skipping
891
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
892
                         state._dirblock_state)
893
        mode = stat_value.st_mode
3696.5.2 by Robert Collins
Integrate less aggressive sha logic with C iter-changes.
894
        self.assertEqual([('is_exec', mode, False)], state._log)
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
895
896
        state.save()
897
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
898
                         state._dirblock_state)
899
5802.3.1 by John Arbash Meinel
Fix bug #765881. Having a file added on disk was skipping
900
        # Roll the clock back so the file is guaranteed to look too new. We
901
        # should still not compute the sha1.
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
902
        state.adjust_time(-10)
3696.5.2 by Robert Collins
Integrate less aggressive sha logic with C iter-changes.
903
        del state._log[:]
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
904
905
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
906
                                          stat_value=stat_value)
3696.5.2 by Robert Collins
Integrate less aggressive sha logic with C iter-changes.
907
        self.assertEqual([('is_exec', mode, False)], state._log)
908
        self.assertEqual(None, link_or_sha1)
5802.3.1 by John Arbash Meinel
Fix bug #765881. Having a file added on disk was skipping
909
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
910
                         state._dirblock_state)
3696.5.2 by Robert Collins
Integrate less aggressive sha logic with C iter-changes.
911
        self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
912
                         entry[1][0])
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
913
        state.save()
914
3696.5.2 by Robert Collins
Integrate less aggressive sha logic with C iter-changes.
915
        # If it is cachable (the clock has moved forward) but new it still
916
        # won't calculate the sha or cache it.
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
917
        state.adjust_time(+20)
3696.5.2 by Robert Collins
Integrate less aggressive sha logic with C iter-changes.
918
        del state._log[:]
919
        link_or_sha1 = dirstate.update_entry(state, entry, abspath='a',
920
                                          stat_value=stat_value)
921
        self.assertEqual(None, link_or_sha1)
922
        self.assertEqual([('is_exec', mode, False)], state._log)
923
        self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
924
                         entry[1][0])
5802.3.1 by John Arbash Meinel
Fix bug #765881. Having a file added on disk was skipping
925
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
926
                         state._dirblock_state)
3696.5.2 by Robert Collins
Integrate less aggressive sha logic with C iter-changes.
927
928
        # If the file is no longer new, and the clock has been moved forward
929
        # sufficiently, it will cache the sha.
930
        del state._log[:]
931
        state.set_parent_trees(
932
            [(with_a_id, tree.branch.repository.revision_tree(with_a_id))],
933
            [])
934
        entry = state._get_entry(0, path_utf8='a')
935
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
936
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
937
                                          stat_value=stat_value)
938
        self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
939
                         link_or_sha1)
3696.5.2 by Robert Collins
Integrate less aggressive sha logic with C iter-changes.
940
        self.assertEqual([('is_exec', mode, False), ('sha1', 'a')],
941
                          state._log)
942
        self.assertEqual(('f', link_or_sha1, 14, False, packed_stat),
943
                         entry[1][0])
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
944
945
        # Subsequent calls will just return the cached value
3696.5.2 by Robert Collins
Integrate less aggressive sha logic with C iter-changes.
946
        del state._log[:]
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
947
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
948
                                          stat_value=stat_value)
949
        self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
950
                         link_or_sha1)
3696.5.2 by Robert Collins
Integrate less aggressive sha logic with C iter-changes.
951
        self.assertEqual([], state._log)
952
        self.assertEqual(('f', link_or_sha1, 14, False, packed_stat),
953
                         entry[1][0])
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
954
955
    def test_update_entry_symlink(self):
956
        """Update entry should read symlinks."""
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
957
        self.requireFeature(features.SymlinkFeature)
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
958
        state, entry = self.get_state_with_a()
959
        state.save()
960
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
961
                         state._dirblock_state)
962
        os.symlink('target', 'a')
963
964
        state.adjust_time(-10) # Make the symlink look new
965
        stat_value = os.lstat('a')
966
        packed_stat = dirstate.pack_stat(stat_value)
967
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
968
                                          stat_value=stat_value)
969
        self.assertEqual('target', link_or_sha1)
970
        self.assertEqual([('read_link', 'a', '')], state._log)
971
        # Dirblock is not updated (the link is too new)
972
        self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
973
                         entry[1])
5807.4.6 by John Arbash Meinel
Merge newer bzr.dev and resolve conflicts.
974
        # The file entry turned into a symlink, that is considered
975
        # HASH modified worthy.
5807.4.3 by John Arbash Meinel
Get the test suite to run clean for bt.test_dirstate bt.test__dirstate bt.test_workingtree
976
        self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
977
                         state._dirblock_state)
978
979
        # Because the stat_value looks new, we should re-read the target
5807.4.6 by John Arbash Meinel
Merge newer bzr.dev and resolve conflicts.
980
        del state._log[:]
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
981
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
982
                                          stat_value=stat_value)
983
        self.assertEqual('target', link_or_sha1)
5807.4.6 by John Arbash Meinel
Merge newer bzr.dev and resolve conflicts.
984
        self.assertEqual([('read_link', 'a', '')], state._log)
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
985
        self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
986
                         entry[1])
5807.4.6 by John Arbash Meinel
Merge newer bzr.dev and resolve conflicts.
987
        state.save()
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
988
        state.adjust_time(+20) # Skip into the future, all files look old
5807.4.6 by John Arbash Meinel
Merge newer bzr.dev and resolve conflicts.
989
        del state._log[:]
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
990
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
991
                                          stat_value=stat_value)
5807.4.6 by John Arbash Meinel
Merge newer bzr.dev and resolve conflicts.
992
        # The symlink stayed a symlink. So while it is new enough to cache, we
993
        # don't bother setting the flag, because it is not really worth saving
994
        # (when we stat the symlink, we'll have paged in the target.)
995
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
996
                         state._dirblock_state)
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
997
        self.assertEqual('target', link_or_sha1)
998
        # We need to re-read the link because only now can we cache it
5807.4.6 by John Arbash Meinel
Merge newer bzr.dev and resolve conflicts.
999
        self.assertEqual([('read_link', 'a', '')], state._log)
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
1000
        self.assertEqual([('l', 'target', 6, False, packed_stat)],
1001
                         entry[1])
1002
5807.4.6 by John Arbash Meinel
Merge newer bzr.dev and resolve conflicts.
1003
        del state._log[:]
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
1004
        # Another call won't re-read the link
5807.4.6 by John Arbash Meinel
Merge newer bzr.dev and resolve conflicts.
1005
        self.assertEqual([], state._log)
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
1006
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
1007
                                          stat_value=stat_value)
1008
        self.assertEqual('target', link_or_sha1)
1009
        self.assertEqual([('l', 'target', 6, False, packed_stat)],
1010
                         entry[1])
1011
1012
    def do_update_entry(self, state, entry, abspath):
1013
        stat_value = os.lstat(abspath)
1014
        return self.update_entry(state, entry, abspath, stat_value)
1015
1016
    def test_update_entry_dir(self):
1017
        state, entry = self.get_state_with_a()
1018
        self.build_tree(['a/'])
1019
        self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1020
1021
    def test_update_entry_dir_unchanged(self):
1022
        state, entry = self.get_state_with_a()
1023
        self.build_tree(['a/'])
1024
        state.adjust_time(+20)
1025
        self.assertIs(None, self.do_update_entry(state, entry, 'a'))
5802.3.1 by John Arbash Meinel
Fix bug #765881. Having a file added on disk was skipping
1026
        # a/ used to be a file, but is now a directory, worth saving
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
1027
        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1028
                         state._dirblock_state)
1029
        state.save()
1030
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1031
                         state._dirblock_state)
5802.3.1 by John Arbash Meinel
Fix bug #765881. Having a file added on disk was skipping
1032
        # No changes to a/ means not worth saving.
1033
        self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1034
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1035
                         state._dirblock_state)
1036
        # Change the last-modified time for the directory
1037
        t = time.time() - 100.0
5813.2.1 by John Arbash Meinel
On babune, the test fails because we can't set mtime on a dir.
1038
        try:
1039
            os.utime('a', (t, t))
1040
        except OSError:
1041
            # It looks like Win32 + FAT doesn't allow to change times on a dir.
1042
            raise tests.TestSkipped("can't update mtime of a dir on FAT")
5802.3.1 by John Arbash Meinel
Fix bug #765881. Having a file added on disk was skipping
1043
        saved_packed_stat = entry[1][0][-1]
1044
        self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1045
        # We *do* go ahead and update the information in the dirblocks, but we
1046
        # don't bother setting IN_MEMORY_MODIFIED because it is trivial to
1047
        # recompute.
1048
        self.assertNotEqual(saved_packed_stat, entry[1][0][-1])
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
1049
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1050
                         state._dirblock_state)
1051
1052
    def test_update_entry_file_unchanged(self):
3696.5.2 by Robert Collins
Integrate less aggressive sha logic with C iter-changes.
1053
        state, _ = self.get_state_with_a()
1054
        tree = self.make_branch_and_tree('tree')
1055
        tree.lock_write()
1056
        self.build_tree(['tree/a'])
1057
        tree.add(['a'], ['a-id'])
1058
        with_a_id = tree.commit('witha')
1059
        self.addCleanup(tree.unlock)
1060
        state.set_parent_trees(
1061
            [(with_a_id, tree.branch.repository.revision_tree(with_a_id))],
1062
            [])
1063
        entry = state._get_entry(0, path_utf8='a')
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
1064
        self.build_tree(['a'])
1065
        sha1sum = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1066
        state.adjust_time(+20)
1067
        self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1068
        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1069
                         state._dirblock_state)
1070
        state.save()
1071
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1072
                         state._dirblock_state)
1073
        self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1074
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1075
                         state._dirblock_state)
1076
4100.2.6 by Aaron Bentley
Add tests for update_entry with tree references
1077
    def test_update_entry_tree_reference(self):
1078
        state = test_dirstate.InstrumentedDirState.initialize('dirstate')
1079
        self.addCleanup(state.unlock)
1080
        state.add('r', 'r-id', 'tree-reference', None, '')
1081
        self.build_tree(['r/'])
1082
        entry = state._get_entry(0, path_utf8='r')
1083
        self.do_update_entry(state, entry, 'r')
1084
        entry = state._get_entry(0, path_utf8='r')
1085
        self.assertEqual('t', entry[1][0][0])
1086
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
1087
    def create_and_test_file(self, state, entry):
3696.5.2 by Robert Collins
Integrate less aggressive sha logic with C iter-changes.
1088
        """Create a file at 'a' and verify the state finds it during update.
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
1089
1090
        The state should already be versioning *something* at 'a'. This makes
1091
        sure that state.update_entry recognizes it as a file.
1092
        """
1093
        self.build_tree(['a'])
1094
        stat_value = os.lstat('a')
1095
        packed_stat = dirstate.pack_stat(stat_value)
1096
1097
        link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
3696.5.2 by Robert Collins
Integrate less aggressive sha logic with C iter-changes.
1098
        self.assertEqual(None, link_or_sha1)
1099
        self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
1100
                         entry[1])
1101
        return packed_stat
1102
1103
    def create_and_test_dir(self, state, entry):
1104
        """Create a directory at 'a' and verify the state finds it.
1105
1106
        The state should already be versioning *something* at 'a'. This makes
1107
        sure that state.update_entry recognizes it as a directory.
1108
        """
1109
        self.build_tree(['a/'])
1110
        stat_value = os.lstat('a')
1111
        packed_stat = dirstate.pack_stat(stat_value)
1112
1113
        link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1114
        self.assertIs(None, link_or_sha1)
1115
        self.assertEqual([('d', '', 0, False, packed_stat)], entry[1])
1116
1117
        return packed_stat
1118
4241.14.19 by Vincent Ladeuil
Modernize dirstate helpers tests parametrization.
1119
    # FIXME: Add unicode version
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
1120
    def create_and_test_symlink(self, state, entry):
1121
        """Create a symlink at 'a' and verify the state finds it.
1122
1123
        The state should already be versioning *something* at 'a'. This makes
1124
        sure that state.update_entry recognizes it as a symlink.
1125
1126
        This should not be called if this platform does not have symlink
1127
        support.
1128
        """
1129
        # caller should care about skipping test on platforms without symlinks
1130
        os.symlink('path/to/foo', 'a')
1131
1132
        stat_value = os.lstat('a')
1133
        packed_stat = dirstate.pack_stat(stat_value)
1134
1135
        link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1136
        self.assertEqual('path/to/foo', link_or_sha1)
1137
        self.assertEqual([('l', 'path/to/foo', 11, False, packed_stat)],
1138
                         entry[1])
1139
        return packed_stat
1140
1141
    def test_update_file_to_dir(self):
1142
        """If a file changes to a directory we return None for the sha.
1143
        We also update the inventory record.
1144
        """
1145
        state, entry = self.get_state_with_a()
1146
        # The file sha1 won't be cached unless the file is old
1147
        state.adjust_time(+10)
1148
        self.create_and_test_file(state, entry)
1149
        os.remove('a')
1150
        self.create_and_test_dir(state, entry)
1151
1152
    def test_update_file_to_symlink(self):
1153
        """File becomes a symlink"""
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
1154
        self.requireFeature(features.SymlinkFeature)
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
1155
        state, entry = self.get_state_with_a()
1156
        # The file sha1 won't be cached unless the file is old
1157
        state.adjust_time(+10)
1158
        self.create_and_test_file(state, entry)
1159
        os.remove('a')
1160
        self.create_and_test_symlink(state, entry)
1161
1162
    def test_update_dir_to_file(self):
1163
        """Directory becoming a file updates the entry."""
1164
        state, entry = self.get_state_with_a()
1165
        # The file sha1 won't be cached unless the file is old
1166
        state.adjust_time(+10)
1167
        self.create_and_test_dir(state, entry)
1168
        os.rmdir('a')
1169
        self.create_and_test_file(state, entry)
1170
1171
    def test_update_dir_to_symlink(self):
1172
        """Directory becomes a symlink"""
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
1173
        self.requireFeature(features.SymlinkFeature)
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
1174
        state, entry = self.get_state_with_a()
1175
        # The symlink target won't be cached if it isn't old
1176
        state.adjust_time(+10)
1177
        self.create_and_test_dir(state, entry)
1178
        os.rmdir('a')
1179
        self.create_and_test_symlink(state, entry)
1180
1181
    def test_update_symlink_to_file(self):
1182
        """Symlink becomes a file"""
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
1183
        self.requireFeature(features.SymlinkFeature)
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
1184
        state, entry = self.get_state_with_a()
1185
        # The symlink and file info won't be cached unless old
1186
        state.adjust_time(+10)
1187
        self.create_and_test_symlink(state, entry)
1188
        os.remove('a')
1189
        self.create_and_test_file(state, entry)
1190
1191
    def test_update_symlink_to_dir(self):
1192
        """Symlink becomes a directory"""
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
1193
        self.requireFeature(features.SymlinkFeature)
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
1194
        state, entry = self.get_state_with_a()
1195
        # The symlink target won't be cached if it isn't old
1196
        state.adjust_time(+10)
1197
        self.create_and_test_symlink(state, entry)
1198
        os.remove('a')
1199
        self.create_and_test_dir(state, entry)
1200
1201
    def test__is_executable_win32(self):
1202
        state, entry = self.get_state_with_a()
1203
        self.build_tree(['a'])
1204
1205
        # Make sure we are using the win32 implementation of _is_executable
1206
        state._is_executable = state._is_executable_win32
1207
1208
        # The file on disk is not executable, but we are marking it as though
1209
        # it is. With _is_executable_win32 we ignore what is on disk.
1210
        entry[1][0] = ('f', '', 0, True, dirstate.DirState.NULLSTAT)
1211
1212
        stat_value = os.lstat('a')
1213
        packed_stat = dirstate.pack_stat(stat_value)
1214
1215
        state.adjust_time(-10) # Make sure everything is new
1216
        self.update_entry(state, entry, abspath='a', stat_value=stat_value)
1217
1218
        # The row is updated, but the executable bit stays set.
1219
        self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1220
                         entry[1])
1221
4241.14.19 by Vincent Ladeuil
Modernize dirstate helpers tests parametrization.
1222
        # Make the disk object look old enough to cache (but it won't cache the
1223
        # sha as it is a new file).
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
1224
        state.adjust_time(+20)
1225
        digest = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1226
        self.update_entry(state, entry, abspath='a', stat_value=stat_value)
3696.5.2 by Robert Collins
Integrate less aggressive sha logic with C iter-changes.
1227
        self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1228
            entry[1])
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
1229
4132.2.4 by Ian Clatworthy
John's enhancements/fixes to the tests
1230
    def _prepare_tree(self):
1231
        # Create a tree
1232
        text = 'Hello World\n'
1233
        tree = self.make_branch_and_tree('tree')
1234
        self.build_tree_contents([('tree/a file', text)])
1235
        tree.add('a file', 'a-file-id')
1236
        # Note: dirstate does not sha prior to the first commit
1237
        # so commit now in order for the test to work
1238
        tree.commit('first')
1239
        return tree, text
1240
1241
    def test_sha1provider_sha1_used(self):
1242
        tree, text = self._prepare_tree()
1243
        state = dirstate.DirState.from_tree(tree, 'dirstate',
4132.2.5 by Ian Clatworthy
feedback from poolie - use SHA, not Sha, in class names
1244
            UppercaseSHA1Provider())
4132.2.4 by Ian Clatworthy
John's enhancements/fixes to the tests
1245
        self.addCleanup(state.unlock)
1246
        expected_sha = osutils.sha_string(text.upper() + "foo")
1247
        entry = state._get_entry(0, path_utf8='a file')
1248
        state._sha_cutoff_time()
1249
        state._cutoff_time += 10
4241.14.19 by Vincent Ladeuil
Modernize dirstate helpers tests parametrization.
1250
        sha1 = self.update_entry(state, entry, 'tree/a file',
1251
                                 os.lstat('tree/a file'))
4132.2.4 by Ian Clatworthy
John's enhancements/fixes to the tests
1252
        self.assertEqual(expected_sha, sha1)
1253
1254
    def test_sha1provider_stat_and_sha1_used(self):
1255
        tree, text = self._prepare_tree()
1256
        tree.lock_write()
1257
        self.addCleanup(tree.unlock)
1258
        state = tree._current_dirstate()
4132.2.5 by Ian Clatworthy
feedback from poolie - use SHA, not Sha, in class names
1259
        state._sha1_provider = UppercaseSHA1Provider()
4132.2.4 by Ian Clatworthy
John's enhancements/fixes to the tests
1260
        # If we used the standard provider, it would look like nothing has
1261
        # changed
4241.14.19 by Vincent Ladeuil
Modernize dirstate helpers tests parametrization.
1262
        file_ids_changed = [change[0] for change
1263
                            in tree.iter_changes(tree.basis_tree())]
4132.2.4 by Ian Clatworthy
John's enhancements/fixes to the tests
1264
        self.assertEqual(['a-file-id'], file_ids_changed)
1265
1266
4159.1.2 by Ian Clatworthy
make sha1_provider a parameter to DirState() - tweak tests
1267
class UppercaseSHA1Provider(dirstate.SHA1Provider):
4132.2.5 by Ian Clatworthy
feedback from poolie - use SHA, not Sha, in class names
1268
    """A custom SHA1Provider."""
4132.2.4 by Ian Clatworthy
John's enhancements/fixes to the tests
1269
1270
    def sha1(self, abspath):
1271
        return self.stat_and_sha1(abspath)[1]
1272
1273
    def stat_and_sha1(self, abspath):
1274
        file_obj = file(abspath, 'rb')
1275
        try:
1276
            statvalue = os.fstat(file_obj.fileno())
1277
            text = ''.join(file_obj.readlines())
1278
            sha1 = osutils.sha_string(text.upper() + "foo")
1279
        finally:
1280
            file_obj.close()
1281
        return statvalue, sha1
1282
3696.4.1 by Robert Collins
Refactor to allow a pluggable dirstate update_entry with interface tests.
1283
4241.14.19 by Vincent Ladeuil
Modernize dirstate helpers tests parametrization.
1284
class TestProcessEntry(test_dirstate.TestCaseWithDirState):
1285
5559.2.2 by Martin Pool
Change to using standard load_tests_apply_scenarios.
1286
    scenarios = multiply_scenarios(dir_reader_scenarios(), pe_scenarios)
1287
4241.14.19 by Vincent Ladeuil
Modernize dirstate helpers tests parametrization.
1288
    # Set by load_tests
1289
    _process_entry = None
4132.2.4 by Ian Clatworthy
John's enhancements/fixes to the tests
1290
1291
    def setUp(self):
4241.14.19 by Vincent Ladeuil
Modernize dirstate helpers tests parametrization.
1292
        super(TestProcessEntry, self).setUp()
4985.1.5 by Vincent Ladeuil
Deploying the new overrideAttr facility further reduces the complexity
1293
        self.overrideAttr(dirstate, '_process_entry', self._process_entry)
4132.2.4 by Ian Clatworthy
John's enhancements/fixes to the tests
1294
1295
    def assertChangedFileIds(self, expected, tree):
1296
        tree.lock_read()
1297
        try:
1298
            file_ids = [info[0] for info
1299
                        in tree.iter_changes(tree.basis_tree())]
1300
        finally:
1301
            tree.unlock()
1302
        self.assertEqual(sorted(expected), sorted(file_ids))
1303
4634.117.1 by John Arbash Meinel
Fix bug #495023, _update_current_block should not supress exceptions.
1304
    def test_exceptions_raised(self):
1305
        # This is a direct test of bug #495023, it relies on osutils.is_inside
1306
        # getting called in an inner function. Which makes it a bit brittle,
1307
        # but at least it does reproduce the bug.
1308
        tree = self.make_branch_and_tree('tree')
1309
        self.build_tree(['tree/file', 'tree/dir/', 'tree/dir/sub',
1310
                         'tree/dir2/', 'tree/dir2/sub2'])
1311
        tree.add(['file', 'dir', 'dir/sub', 'dir2', 'dir2/sub2'])
1312
        tree.commit('first commit')
1313
        tree.lock_read()
1314
        self.addCleanup(tree.unlock)
1315
        basis_tree = tree.basis_tree()
4985.1.5 by Vincent Ladeuil
Deploying the new overrideAttr facility further reduces the complexity
1316
        def is_inside_raises(*args, **kwargs):
1317
            raise RuntimeError('stop this')
1318
        self.overrideAttr(osutils, 'is_inside', is_inside_raises)
4634.117.1 by John Arbash Meinel
Fix bug #495023, _update_current_block should not supress exceptions.
1319
        self.assertListRaises(RuntimeError, tree.iter_changes, basis_tree)
1320
4132.2.4 by Ian Clatworthy
John's enhancements/fixes to the tests
1321
    def test_simple_changes(self):
1322
        tree = self.make_branch_and_tree('tree')
1323
        self.build_tree(['tree/file'])
1324
        tree.add(['file'], ['file-id'])
1325
        self.assertChangedFileIds([tree.get_root_id(), 'file-id'], tree)
1326
        tree.commit('one')
1327
        self.assertChangedFileIds([], tree)
1328
1329
    def test_sha1provider_stat_and_sha1_used(self):
1330
        tree = self.make_branch_and_tree('tree')
1331
        self.build_tree(['tree/file'])
1332
        tree.add(['file'], ['file-id'])
1333
        tree.commit('one')
1334
        tree.lock_write()
1335
        self.addCleanup(tree.unlock)
1336
        state = tree._current_dirstate()
4132.2.5 by Ian Clatworthy
feedback from poolie - use SHA, not Sha, in class names
1337
        state._sha1_provider = UppercaseSHA1Provider()
4132.2.4 by Ian Clatworthy
John's enhancements/fixes to the tests
1338
        self.assertChangedFileIds(['file-id'], tree)
1339