/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test__dirstate_helpers.py

  • Committer: Robert Collins
  • Date: 2010-05-06 23:41:35 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100506234135-yivbzczw1sejxnxc
Lock methods on ``Tree``, ``Branch`` and ``Repository`` are now
expected to return an object which can be used to unlock them. This reduces
duplicate code when using cleanups. The previous 'tokens's returned by
``Branch.lock_write`` and ``Repository.lock_write`` are now attributes
on the result of the lock_write. ``repository.RepositoryWriteLockResult``
and ``branch.BranchWriteLockResult`` document this. (Robert Collins)

``log._get_info_for_log_files`` now takes an add_cleanup callable.
(Robert Collins)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2011 Canonical Ltd
 
1
# Copyright (C) 2007-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
20
20
import os
21
21
import time
22
22
 
23
 
from .. import (
 
23
from bzrlib import (
 
24
    dirstate,
 
25
    errors,
24
26
    osutils,
25
27
    tests,
26
28
    )
27
 
from ..bzr import (
28
 
    dirstate,
29
 
    _dirstate_helpers_py,
30
 
    )
31
 
from . import (
 
29
from bzrlib.tests import (
32
30
    test_dirstate,
33
 
    )
34
 
from .test_osutils import dir_reader_scenarios
35
 
from .scenarios import (
36
 
    load_tests_apply_scenarios,
37
 
    multiply_scenarios,
38
 
    )
39
 
from . import (
40
 
    features,
41
 
    )
42
 
 
43
 
 
44
 
load_tests = load_tests_apply_scenarios
45
 
 
46
 
 
47
 
compiled_dirstate_helpers_feature = features.ModuleAvailableFeature(
48
 
    'breezy.bzr._dirstate_helpers_pyx')
49
 
 
50
 
 
51
 
# FIXME: we should also parametrize against SHA1Provider !
52
 
 
53
 
ue_scenarios = [('dirstate_Python',
54
 
                 {'update_entry': dirstate.py_update_entry})]
55
 
if compiled_dirstate_helpers_feature.available():
56
 
    update_entry = compiled_dirstate_helpers_feature.module.update_entry
57
 
    ue_scenarios.append(('dirstate_Pyrex', {'update_entry': update_entry}))
58
 
 
59
 
pe_scenarios = [('dirstate_Python',
60
 
                 {'_process_entry': dirstate.ProcessEntryPython})]
61
 
if compiled_dirstate_helpers_feature.available():
62
 
    process_entry = compiled_dirstate_helpers_feature.module.ProcessEntryC
63
 
    pe_scenarios.append(('dirstate_Pyrex', {'_process_entry': process_entry}))
64
 
 
65
 
helper_scenarios = [('dirstate_Python', {'helpers': _dirstate_helpers_py})]
66
 
if compiled_dirstate_helpers_feature.available():
67
 
    helper_scenarios.append(('dirstate_Pyrex',
68
 
                             {'helpers': compiled_dirstate_helpers_feature.module}))
 
31
    test_osutils,
 
32
    )
 
33
 
 
34
try:
 
35
    from bzrlib import _dirstate_helpers_pyx
 
36
    has_dirstate_helpers_pyx = True
 
37
except ImportError:
 
38
    has_dirstate_helpers_pyx = False
 
39
 
 
40
 
 
41
compiled_dirstate_helpers_feature = tests.ModuleAvailableFeature(
 
42
                                'bzrlib._dirstate_helpers_pyx')
 
43
 
 
44
 
 
45
def load_tests(basic_tests, module, loader):
 
46
    # FIXME: we should also parametrize against SHA1Provider !
 
47
    suite = loader.suiteClass()
 
48
    remaining_tests = basic_tests
 
49
 
 
50
    dir_reader_scenarios = test_osutils.dir_reader_scenarios()
 
51
 
 
52
    ue_scenarios = [('dirstate_Python',
 
53
                     {'update_entry': dirstate.py_update_entry})]
 
54
    if compiled_dirstate_helpers_feature.available():
 
55
        update_entry = compiled_dirstate_helpers_feature.module.update_entry
 
56
        pyrex_scenario = ('dirstate_Pyrex', {'update_entry': update_entry})
 
57
        ue_scenarios.append(pyrex_scenario)
 
58
    process_entry_tests, remaining_tests = tests.split_suite_by_condition(
 
59
        remaining_tests, tests.condition_isinstance(TestUpdateEntry))
 
60
    tests.multiply_tests(process_entry_tests,
 
61
                         tests.multiply_scenarios(dir_reader_scenarios,
 
62
                                                  ue_scenarios),
 
63
                         suite)
 
64
 
 
65
    pe_scenarios = [('dirstate_Python',
 
66
                     {'_process_entry': dirstate.ProcessEntryPython})]
 
67
    if compiled_dirstate_helpers_feature.available():
 
68
        process_entry = compiled_dirstate_helpers_feature.module.ProcessEntryC
 
69
        pyrex_scenario = ('dirstate_Pyrex', {'_process_entry': process_entry})
 
70
        pe_scenarios.append(pyrex_scenario)
 
71
    process_entry_tests, remaining_tests = tests.split_suite_by_condition(
 
72
        remaining_tests, tests.condition_isinstance(TestProcessEntry))
 
73
    tests.multiply_tests(process_entry_tests,
 
74
                         tests.multiply_scenarios(dir_reader_scenarios,
 
75
                                                  pe_scenarios),
 
76
                         suite)
 
77
 
 
78
    dir_reader_tests, remaining_tests = tests.split_suite_by_condition(
 
79
        remaining_tests, tests.condition_isinstance(
 
80
            test_dirstate.TestCaseWithDirState))
 
81
    tests.multiply_tests(dir_reader_tests, dir_reader_scenarios, suite)
 
82
    suite.addTest(remaining_tests)
 
83
 
 
84
    return suite
69
85
 
70
86
 
71
87
class TestBisectPathMixin(object):
120
136
                            bisect_split_idx, bisect_path_idx, path)
121
137
                         )
122
138
        if exists:
123
 
            self.assertEqual(path, paths[bisect_path_idx + offset])
 
139
            self.assertEqual(path, paths[bisect_path_idx+offset])
124
140
 
125
141
    def split_for_dirblocks(self, paths):
126
142
        dir_split_paths = []
127
143
        for path in paths:
128
144
            dirname, basename = os.path.split(path)
129
 
            dir_split_paths.append((dirname.split(b'/'), basename))
 
145
            dir_split_paths.append((dirname.split('/'), basename))
130
146
        dir_split_paths.sort()
131
147
        return dir_split_paths
132
148
 
133
149
    def test_simple(self):
134
150
        """In the simple case it works just like bisect_left"""
135
 
        paths = [b'', b'a', b'b', b'c', b'd']
 
151
        paths = ['', 'a', 'b', 'c', 'd']
136
152
        split_paths = self.split_for_dirblocks(paths)
137
153
        for path in paths:
138
154
            self.assertBisect(paths, split_paths, path, exists=True)
139
 
        self.assertBisect(paths, split_paths, b'_', exists=False)
140
 
        self.assertBisect(paths, split_paths, b'aa', exists=False)
141
 
        self.assertBisect(paths, split_paths, b'bb', exists=False)
142
 
        self.assertBisect(paths, split_paths, b'cc', exists=False)
143
 
        self.assertBisect(paths, split_paths, b'dd', exists=False)
144
 
        self.assertBisect(paths, split_paths, b'a/a', exists=False)
145
 
        self.assertBisect(paths, split_paths, b'b/b', exists=False)
146
 
        self.assertBisect(paths, split_paths, b'c/c', exists=False)
147
 
        self.assertBisect(paths, split_paths, b'd/d', exists=False)
 
155
        self.assertBisect(paths, split_paths, '_', exists=False)
 
156
        self.assertBisect(paths, split_paths, 'aa', exists=False)
 
157
        self.assertBisect(paths, split_paths, 'bb', exists=False)
 
158
        self.assertBisect(paths, split_paths, 'cc', exists=False)
 
159
        self.assertBisect(paths, split_paths, 'dd', exists=False)
 
160
        self.assertBisect(paths, split_paths, 'a/a', exists=False)
 
161
        self.assertBisect(paths, split_paths, 'b/b', exists=False)
 
162
        self.assertBisect(paths, split_paths, 'c/c', exists=False)
 
163
        self.assertBisect(paths, split_paths, 'd/d', exists=False)
148
164
 
149
165
    def test_involved(self):
150
166
        """This is where bisect_path_* diverges slightly."""
181
197
        # children are mentioned.
182
198
        # So all the root-directory paths, then all the
183
199
        # first sub directory, etc.
184
 
        paths = [  # content of '/'
185
 
            b'', b'a', b'a-a', b'a-z', b'a=a', b'a=z',
 
200
        paths = [# content of '/'
 
201
                 '', 'a', 'a-a', 'a-z', 'a=a', 'a=z',
186
202
                 # content of 'a/'
187
 
                 b'a/a', b'a/a-a', b'a/a-z',
188
 
                 b'a/a=a', b'a/a=z',
189
 
                 b'a/z', b'a/z-a', b'a/z-z',
190
 
                 b'a/z=a', b'a/z=z',
 
203
                 'a/a', 'a/a-a', 'a/a-z',
 
204
                 'a/a=a', 'a/a=z',
 
205
                 'a/z', 'a/z-a', 'a/z-z',
 
206
                 'a/z=a', 'a/z=z',
191
207
                 # content of 'a/a/'
192
 
                 b'a/a/a', b'a/a/z',
 
208
                 'a/a/a', 'a/a/z',
193
209
                 # content of 'a/a-a'
194
 
                 b'a/a-a/a',
 
210
                 'a/a-a/a',
195
211
                 # content of 'a/a-z'
196
 
                 b'a/a-z/z',
 
212
                 'a/a-z/z',
197
213
                 # content of 'a/a=a'
198
 
                 b'a/a=a/a',
 
214
                 'a/a=a/a',
199
215
                 # content of 'a/a=z'
200
 
                 b'a/a=z/z',
 
216
                 'a/a=z/z',
201
217
                 # content of 'a/z/'
202
 
                 b'a/z/a', b'a/z/z',
 
218
                 'a/z/a', 'a/z/z',
203
219
                 # content of 'a-a'
204
 
                 b'a-a/a',
 
220
                 'a-a/a',
205
221
                 # content of 'a-z'
206
 
                 b'a-z/z',
 
222
                 'a-z/z',
207
223
                 # content of 'a=a'
208
 
                 b'a=a/a',
 
224
                 'a=a/a',
209
225
                 # content of 'a=z'
210
 
                 b'a=z/z',
211
 
            ]
 
226
                 'a=z/z',
 
227
                ]
212
228
        split_paths = self.split_for_dirblocks(paths)
213
229
        sorted_paths = []
214
230
        for dir_parts, basename in split_paths:
215
 
            if dir_parts == [b'']:
 
231
            if dir_parts == ['']:
216
232
                sorted_paths.append(basename)
217
233
            else:
218
 
                sorted_paths.append(b'/'.join(dir_parts + [basename]))
 
234
                sorted_paths.append('/'.join(dir_parts + [basename]))
219
235
 
220
236
        self.assertEqual(sorted_paths, paths)
221
237
 
227
243
    """Run all Bisect Path tests against _bisect_path_left."""
228
244
 
229
245
    def get_bisect_path(self):
230
 
        from breezy.bzr._dirstate_helpers_py import _bisect_path_left
 
246
        from bzrlib._dirstate_helpers_py import _bisect_path_left
231
247
        return _bisect_path_left
232
248
 
233
249
    def get_bisect(self):
240
256
    _test_needs_features = [compiled_dirstate_helpers_feature]
241
257
 
242
258
    def get_bisect_path(self):
243
 
        from breezy.bzr._dirstate_helpers_pyx import _bisect_path_left
 
259
        from bzrlib._dirstate_helpers_pyx import _bisect_path_left
244
260
        return _bisect_path_left
245
261
 
246
262
 
248
264
    """Run all Bisect Path tests against _bisect_path_right"""
249
265
 
250
266
    def get_bisect_path(self):
251
 
        from breezy.bzr._dirstate_helpers_py import _bisect_path_right
 
267
        from bzrlib._dirstate_helpers_py import _bisect_path_right
252
268
        return _bisect_path_right
253
269
 
254
270
    def get_bisect(self):
261
277
    _test_needs_features = [compiled_dirstate_helpers_feature]
262
278
 
263
279
    def get_bisect_path(self):
264
 
        from breezy.bzr._dirstate_helpers_pyx import _bisect_path_right
 
280
        from bzrlib._dirstate_helpers_pyx import _bisect_path_right
265
281
        return _bisect_path_right
266
282
 
267
283
 
279
295
 
280
296
    def get_bisect_dirblock(self):
281
297
        """Return an implementation of bisect_dirblock"""
282
 
        from breezy.bzr._dirstate_helpers_py import bisect_dirblock
 
298
        from bzrlib._dirstate_helpers_py import bisect_dirblock
283
299
        return bisect_dirblock
284
300
 
285
301
    def assertBisect(self, dirblocks, split_dirblocks, path, *args, **kwargs):
294
310
        bisect_dirblock = self.get_bisect_dirblock()
295
311
        self.assertIsInstance(dirblocks, list)
296
312
        bisect_split_idx = bisect_dirblock(dirblocks, path, *args, **kwargs)
297
 
        split_dirblock = (path.split(b'/'), [])
 
313
        split_dirblock = (path.split('/'), [])
298
314
        bisect_left_idx = bisect.bisect_left(split_dirblocks, split_dirblock,
299
315
                                             *args)
300
316
        self.assertEqual(bisect_left_idx, bisect_split_idx,
309
325
        Also, ensure that the paths are in proper sorted order.
310
326
        """
311
327
        dirblocks = [(path, []) for path in paths]
312
 
        split_dirblocks = [(path.split(b'/'), []) for path in paths]
 
328
        split_dirblocks = [(path.split('/'), []) for path in paths]
313
329
        self.assertEqual(sorted(split_dirblocks), split_dirblocks)
314
330
        return dirblocks, split_dirblocks
315
331
 
316
332
    def test_simple(self):
317
333
        """In the simple case it works just like bisect_left"""
318
 
        paths = [b'', b'a', b'b', b'c', b'd']
 
334
        paths = ['', 'a', 'b', 'c', 'd']
319
335
        dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
320
336
        for path in paths:
321
337
            self.assertBisect(dirblocks, split_dirblocks, path)
322
 
        self.assertBisect(dirblocks, split_dirblocks, b'_')
323
 
        self.assertBisect(dirblocks, split_dirblocks, b'aa')
324
 
        self.assertBisect(dirblocks, split_dirblocks, b'bb')
325
 
        self.assertBisect(dirblocks, split_dirblocks, b'cc')
326
 
        self.assertBisect(dirblocks, split_dirblocks, b'dd')
327
 
        self.assertBisect(dirblocks, split_dirblocks, b'a/a')
328
 
        self.assertBisect(dirblocks, split_dirblocks, b'b/b')
329
 
        self.assertBisect(dirblocks, split_dirblocks, b'c/c')
330
 
        self.assertBisect(dirblocks, split_dirblocks, b'd/d')
 
338
        self.assertBisect(dirblocks, split_dirblocks, '_')
 
339
        self.assertBisect(dirblocks, split_dirblocks, 'aa')
 
340
        self.assertBisect(dirblocks, split_dirblocks, 'bb')
 
341
        self.assertBisect(dirblocks, split_dirblocks, 'cc')
 
342
        self.assertBisect(dirblocks, split_dirblocks, 'dd')
 
343
        self.assertBisect(dirblocks, split_dirblocks, 'a/a')
 
344
        self.assertBisect(dirblocks, split_dirblocks, 'b/b')
 
345
        self.assertBisect(dirblocks, split_dirblocks, 'c/c')
 
346
        self.assertBisect(dirblocks, split_dirblocks, 'd/d')
331
347
 
332
348
    def test_involved(self):
333
349
        """This is where bisect_left diverges slightly."""
334
 
        paths = [b'', b'a',
335
 
                 b'a/a', b'a/a/a', b'a/a/z', b'a/a-a', b'a/a-z',
336
 
                 b'a/z', b'a/z/a', b'a/z/z', b'a/z-a', b'a/z-z',
337
 
                 b'a-a', b'a-z',
338
 
                 b'z', b'z/a/a', b'z/a/z', b'z/a-a', b'z/a-z',
339
 
                 b'z/z', b'z/z/a', b'z/z/z', b'z/z-a', b'z/z-z',
340
 
                 b'z-a', b'z-z',
341
 
                 ]
 
350
        paths = ['', 'a',
 
351
                 'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
 
352
                 'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
 
353
                 'a-a', 'a-z',
 
354
                 'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
 
355
                 'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
 
356
                 'z-a', 'z-z',
 
357
                ]
342
358
        dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
343
359
        for path in paths:
344
360
            self.assertBisect(dirblocks, split_dirblocks, path)
345
361
 
346
362
    def test_involved_cached(self):
347
363
        """This is where bisect_left diverges slightly."""
348
 
        paths = [b'', b'a',
349
 
                 b'a/a', b'a/a/a', b'a/a/z', b'a/a-a', b'a/a-z',
350
 
                 b'a/z', b'a/z/a', b'a/z/z', b'a/z-a', b'a/z-z',
351
 
                 b'a-a', b'a-z',
352
 
                 b'z', b'z/a/a', b'z/a/z', b'z/a-a', b'z/a-z',
353
 
                 b'z/z', b'z/z/a', b'z/z/z', b'z/z-a', b'z/z-z',
354
 
                 b'z-a', b'z-z',
355
 
                 ]
 
364
        paths = ['', 'a',
 
365
                 'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
 
366
                 'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
 
367
                 'a-a', 'a-z',
 
368
                 'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
 
369
                 'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
 
370
                 'z-a', 'z-z',
 
371
                ]
356
372
        cache = {}
357
373
        dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
358
374
        for path in paths:
373
389
    _test_needs_features = [compiled_dirstate_helpers_feature]
374
390
 
375
391
    def get_bisect_dirblock(self):
376
 
        from breezy.bzr._dirstate_helpers_pyx import bisect_dirblock
 
392
        from bzrlib._dirstate_helpers_pyx import bisect_dirblock
377
393
        return bisect_dirblock
378
394
 
379
395
 
380
 
class TestLtByDirs(tests.TestCase):
381
 
    """Test an implementation of lt_by_dirs()
 
396
class TestCmpByDirs(tests.TestCase):
 
397
    """Test an implementation of cmp_by_dirs()
382
398
 
383
 
    lt_by_dirs() compares 2 paths by their directory sections, rather than as
 
399
    cmp_by_dirs() compares 2 paths by their directory sections, rather than as
384
400
    plain strings.
385
401
 
386
 
    Child test cases can override ``get_lt_by_dirs`` to test a specific
 
402
    Child test cases can override ``get_cmp_by_dirs`` to test a specific
387
403
    implementation.
388
404
    """
389
405
 
390
 
    def get_lt_by_dirs(self):
391
 
        """Get a specific implementation of lt_by_dirs."""
392
 
        from ..bzr._dirstate_helpers_py import lt_by_dirs
393
 
        return lt_by_dirs
 
406
    def get_cmp_by_dirs(self):
 
407
        """Get a specific implementation of cmp_by_dirs."""
 
408
        from bzrlib._dirstate_helpers_py import cmp_by_dirs
 
409
        return cmp_by_dirs
394
410
 
395
411
    def assertCmpByDirs(self, expected, str1, str2):
396
412
        """Compare the two strings, in both directions.
400
416
        :param str1: string to compare
401
417
        :param str2: string to compare
402
418
        """
403
 
        lt_by_dirs = self.get_lt_by_dirs()
 
419
        cmp_by_dirs = self.get_cmp_by_dirs()
404
420
        if expected == 0:
405
421
            self.assertEqual(str1, str2)
406
 
            self.assertFalse(lt_by_dirs(str1, str2))
407
 
            self.assertFalse(lt_by_dirs(str2, str1))
 
422
            self.assertEqual(0, cmp_by_dirs(str1, str2))
 
423
            self.assertEqual(0, cmp_by_dirs(str2, str1))
408
424
        elif expected > 0:
409
 
            self.assertFalse(lt_by_dirs(str1, str2))
410
 
            self.assertTrue(lt_by_dirs(str2, str1))
 
425
            self.assertPositive(cmp_by_dirs(str1, str2))
 
426
            self.assertNegative(cmp_by_dirs(str2, str1))
411
427
        else:
412
 
            self.assertTrue(lt_by_dirs(str1, str2))
413
 
            self.assertFalse(lt_by_dirs(str2, str1))
 
428
            self.assertNegative(cmp_by_dirs(str1, str2))
 
429
            self.assertPositive(cmp_by_dirs(str2, str1))
414
430
 
415
431
    def test_cmp_empty(self):
416
432
        """Compare against the empty string."""
417
 
        self.assertCmpByDirs(0, b'', b'')
418
 
        self.assertCmpByDirs(1, b'a', b'')
419
 
        self.assertCmpByDirs(1, b'ab', b'')
420
 
        self.assertCmpByDirs(1, b'abc', b'')
421
 
        self.assertCmpByDirs(1, b'abcd', b'')
422
 
        self.assertCmpByDirs(1, b'abcde', b'')
423
 
        self.assertCmpByDirs(1, b'abcdef', b'')
424
 
        self.assertCmpByDirs(1, b'abcdefg', b'')
425
 
        self.assertCmpByDirs(1, b'abcdefgh', b'')
426
 
        self.assertCmpByDirs(1, b'abcdefghi', b'')
427
 
        self.assertCmpByDirs(1, b'test/ing/a/path/', b'')
 
433
        self.assertCmpByDirs(0, '', '')
 
434
        self.assertCmpByDirs(1, 'a', '')
 
435
        self.assertCmpByDirs(1, 'ab', '')
 
436
        self.assertCmpByDirs(1, 'abc', '')
 
437
        self.assertCmpByDirs(1, 'abcd', '')
 
438
        self.assertCmpByDirs(1, 'abcde', '')
 
439
        self.assertCmpByDirs(1, 'abcdef', '')
 
440
        self.assertCmpByDirs(1, 'abcdefg', '')
 
441
        self.assertCmpByDirs(1, 'abcdefgh', '')
 
442
        self.assertCmpByDirs(1, 'abcdefghi', '')
 
443
        self.assertCmpByDirs(1, 'test/ing/a/path/', '')
428
444
 
429
445
    def test_cmp_same_str(self):
430
446
        """Compare the same string"""
431
 
        self.assertCmpByDirs(0, b'a', b'a')
432
 
        self.assertCmpByDirs(0, b'ab', b'ab')
433
 
        self.assertCmpByDirs(0, b'abc', b'abc')
434
 
        self.assertCmpByDirs(0, b'abcd', b'abcd')
435
 
        self.assertCmpByDirs(0, b'abcde', b'abcde')
436
 
        self.assertCmpByDirs(0, b'abcdef', b'abcdef')
437
 
        self.assertCmpByDirs(0, b'abcdefg', b'abcdefg')
438
 
        self.assertCmpByDirs(0, b'abcdefgh', b'abcdefgh')
439
 
        self.assertCmpByDirs(0, b'abcdefghi', b'abcdefghi')
440
 
        self.assertCmpByDirs(0, b'testing a long string',
441
 
                             b'testing a long string')
442
 
        self.assertCmpByDirs(0, b'x' * 10000, b'x' * 10000)
443
 
        self.assertCmpByDirs(0, b'a/b', b'a/b')
444
 
        self.assertCmpByDirs(0, b'a/b/c', b'a/b/c')
445
 
        self.assertCmpByDirs(0, b'a/b/c/d', b'a/b/c/d')
446
 
        self.assertCmpByDirs(0, b'a/b/c/d/e', b'a/b/c/d/e')
 
447
        self.assertCmpByDirs(0, 'a', 'a')
 
448
        self.assertCmpByDirs(0, 'ab', 'ab')
 
449
        self.assertCmpByDirs(0, 'abc', 'abc')
 
450
        self.assertCmpByDirs(0, 'abcd', 'abcd')
 
451
        self.assertCmpByDirs(0, 'abcde', 'abcde')
 
452
        self.assertCmpByDirs(0, 'abcdef', 'abcdef')
 
453
        self.assertCmpByDirs(0, 'abcdefg', 'abcdefg')
 
454
        self.assertCmpByDirs(0, 'abcdefgh', 'abcdefgh')
 
455
        self.assertCmpByDirs(0, 'abcdefghi', 'abcdefghi')
 
456
        self.assertCmpByDirs(0, 'testing a long string', 'testing a long string')
 
457
        self.assertCmpByDirs(0, 'x'*10000, 'x'*10000)
 
458
        self.assertCmpByDirs(0, 'a/b', 'a/b')
 
459
        self.assertCmpByDirs(0, 'a/b/c', 'a/b/c')
 
460
        self.assertCmpByDirs(0, 'a/b/c/d', 'a/b/c/d')
 
461
        self.assertCmpByDirs(0, 'a/b/c/d/e', 'a/b/c/d/e')
447
462
 
448
463
    def test_simple_paths(self):
449
464
        """Compare strings that act like normal string comparison"""
450
 
        self.assertCmpByDirs(-1, b'a', b'b')
451
 
        self.assertCmpByDirs(-1, b'aa', b'ab')
452
 
        self.assertCmpByDirs(-1, b'ab', b'bb')
453
 
        self.assertCmpByDirs(-1, b'aaa', b'aab')
454
 
        self.assertCmpByDirs(-1, b'aab', b'abb')
455
 
        self.assertCmpByDirs(-1, b'abb', b'bbb')
456
 
        self.assertCmpByDirs(-1, b'aaaa', b'aaab')
457
 
        self.assertCmpByDirs(-1, b'aaab', b'aabb')
458
 
        self.assertCmpByDirs(-1, b'aabb', b'abbb')
459
 
        self.assertCmpByDirs(-1, b'abbb', b'bbbb')
460
 
        self.assertCmpByDirs(-1, b'aaaaa', b'aaaab')
461
 
        self.assertCmpByDirs(-1, b'a/a', b'a/b')
462
 
        self.assertCmpByDirs(-1, b'a/b', b'b/b')
463
 
        self.assertCmpByDirs(-1, b'a/a/a', b'a/a/b')
464
 
        self.assertCmpByDirs(-1, b'a/a/b', b'a/b/b')
465
 
        self.assertCmpByDirs(-1, b'a/b/b', b'b/b/b')
466
 
        self.assertCmpByDirs(-1, b'a/a/a/a', b'a/a/a/b')
467
 
        self.assertCmpByDirs(-1, b'a/a/a/b', b'a/a/b/b')
468
 
        self.assertCmpByDirs(-1, b'a/a/b/b', b'a/b/b/b')
469
 
        self.assertCmpByDirs(-1, b'a/b/b/b', b'b/b/b/b')
470
 
        self.assertCmpByDirs(-1, b'a/a/a/a/a', b'a/a/a/a/b')
 
465
        self.assertCmpByDirs(-1, 'a', 'b')
 
466
        self.assertCmpByDirs(-1, 'aa', 'ab')
 
467
        self.assertCmpByDirs(-1, 'ab', 'bb')
 
468
        self.assertCmpByDirs(-1, 'aaa', 'aab')
 
469
        self.assertCmpByDirs(-1, 'aab', 'abb')
 
470
        self.assertCmpByDirs(-1, 'abb', 'bbb')
 
471
        self.assertCmpByDirs(-1, 'aaaa', 'aaab')
 
472
        self.assertCmpByDirs(-1, 'aaab', 'aabb')
 
473
        self.assertCmpByDirs(-1, 'aabb', 'abbb')
 
474
        self.assertCmpByDirs(-1, 'abbb', 'bbbb')
 
475
        self.assertCmpByDirs(-1, 'aaaaa', 'aaaab')
 
476
        self.assertCmpByDirs(-1, 'a/a', 'a/b')
 
477
        self.assertCmpByDirs(-1, 'a/b', 'b/b')
 
478
        self.assertCmpByDirs(-1, 'a/a/a', 'a/a/b')
 
479
        self.assertCmpByDirs(-1, 'a/a/b', 'a/b/b')
 
480
        self.assertCmpByDirs(-1, 'a/b/b', 'b/b/b')
 
481
        self.assertCmpByDirs(-1, 'a/a/a/a', 'a/a/a/b')
 
482
        self.assertCmpByDirs(-1, 'a/a/a/b', 'a/a/b/b')
 
483
        self.assertCmpByDirs(-1, 'a/a/b/b', 'a/b/b/b')
 
484
        self.assertCmpByDirs(-1, 'a/b/b/b', 'b/b/b/b')
 
485
        self.assertCmpByDirs(-1, 'a/a/a/a/a', 'a/a/a/a/b')
471
486
 
472
487
    def test_tricky_paths(self):
473
 
        self.assertCmpByDirs(1, b'ab/cd/ef', b'ab/cc/ef')
474
 
        self.assertCmpByDirs(1, b'ab/cd/ef', b'ab/c/ef')
475
 
        self.assertCmpByDirs(-1, b'ab/cd/ef', b'ab/cd-ef')
476
 
        self.assertCmpByDirs(-1, b'ab/cd', b'ab/cd-')
477
 
        self.assertCmpByDirs(-1, b'ab/cd', b'ab-cd')
 
488
        self.assertCmpByDirs(1, 'ab/cd/ef', 'ab/cc/ef')
 
489
        self.assertCmpByDirs(1, 'ab/cd/ef', 'ab/c/ef')
 
490
        self.assertCmpByDirs(-1, 'ab/cd/ef', 'ab/cd-ef')
 
491
        self.assertCmpByDirs(-1, 'ab/cd', 'ab/cd-')
 
492
        self.assertCmpByDirs(-1, 'ab/cd', 'ab-cd')
478
493
 
479
494
    def test_cmp_unicode_not_allowed(self):
480
 
        lt_by_dirs = self.get_lt_by_dirs()
481
 
        self.assertRaises(TypeError, lt_by_dirs, u'Unicode', b'str')
482
 
        self.assertRaises(TypeError, lt_by_dirs, b'str', u'Unicode')
483
 
        self.assertRaises(TypeError, lt_by_dirs, u'Unicode', u'Unicode')
 
495
        cmp_by_dirs = self.get_cmp_by_dirs()
 
496
        self.assertRaises(TypeError, cmp_by_dirs, u'Unicode', 'str')
 
497
        self.assertRaises(TypeError, cmp_by_dirs, 'str', u'Unicode')
 
498
        self.assertRaises(TypeError, cmp_by_dirs, u'Unicode', u'Unicode')
484
499
 
485
500
    def test_cmp_non_ascii(self):
486
 
        self.assertCmpByDirs(-1, b'\xc2\xb5', b'\xc3\xa5')  # u'\xb5', u'\xe5'
487
 
        self.assertCmpByDirs(-1, b'a', b'\xc3\xa5')  # u'a', u'\xe5'
488
 
        self.assertCmpByDirs(-1, b'b', b'\xc2\xb5')  # u'b', u'\xb5'
489
 
        self.assertCmpByDirs(-1, b'a/b', b'a/\xc3\xa5')  # u'a/b', u'a/\xe5'
490
 
        self.assertCmpByDirs(-1, b'b/a', b'b/\xc2\xb5')  # u'b/a', u'b/\xb5'
491
 
 
492
 
 
493
 
class TestCompiledLtByDirs(TestLtByDirs):
494
 
    """Test the pyrex implementation of lt_by_dirs"""
 
501
        self.assertCmpByDirs(-1, '\xc2\xb5', '\xc3\xa5') # u'\xb5', u'\xe5'
 
502
        self.assertCmpByDirs(-1, 'a', '\xc3\xa5') # u'a', u'\xe5'
 
503
        self.assertCmpByDirs(-1, 'b', '\xc2\xb5') # u'b', u'\xb5'
 
504
        self.assertCmpByDirs(-1, 'a/b', 'a/\xc3\xa5') # u'a/b', u'a/\xe5'
 
505
        self.assertCmpByDirs(-1, 'b/a', 'b/\xc2\xb5') # u'b/a', u'b/\xb5'
 
506
 
 
507
 
 
508
class TestCompiledCmpByDirs(TestCmpByDirs):
 
509
    """Test the pyrex implementation of cmp_by_dirs"""
495
510
 
496
511
    _test_needs_features = [compiled_dirstate_helpers_feature]
497
512
 
498
 
    def get_lt_by_dirs(self):
499
 
        from ..bzr._dirstate_helpers_pyx import lt_by_dirs
500
 
        return lt_by_dirs
501
 
 
502
 
 
503
 
class TestLtPathByDirblock(tests.TestCase):
504
 
    """Test an implementation of _lt_path_by_dirblock()
505
 
 
506
 
    _lt_path_by_dirblock() compares two paths using the sort order used by
 
513
    def get_cmp_by_dirs(self):
 
514
        from bzrlib._dirstate_helpers_pyx import cmp_by_dirs
 
515
        return cmp_by_dirs
 
516
 
 
517
 
 
518
class TestCmpPathByDirblock(tests.TestCase):
 
519
    """Test an implementation of _cmp_path_by_dirblock()
 
520
 
 
521
    _cmp_path_by_dirblock() compares two paths using the sort order used by
507
522
    DirState. All paths in the same directory are sorted together.
508
523
 
509
 
    Child test cases can override ``get_lt_path_by_dirblock`` to test a specific
 
524
    Child test cases can override ``get_cmp_path_by_dirblock`` to test a specific
510
525
    implementation.
511
526
    """
512
527
 
513
 
    def get_lt_path_by_dirblock(self):
514
 
        """Get a specific implementation of _lt_path_by_dirblock."""
515
 
        from ..bzr._dirstate_helpers_py import _lt_path_by_dirblock
516
 
        return _lt_path_by_dirblock
 
528
    def get_cmp_path_by_dirblock(self):
 
529
        """Get a specific implementation of _cmp_path_by_dirblock."""
 
530
        from bzrlib._dirstate_helpers_py import _cmp_path_by_dirblock
 
531
        return _cmp_path_by_dirblock
517
532
 
518
 
    def assertLtPathByDirblock(self, paths):
 
533
    def assertCmpPathByDirblock(self, paths):
519
534
        """Compare all paths and make sure they evaluate to the correct order.
520
535
 
521
536
        This does N^2 comparisons. It is assumed that ``paths`` is properly
526
541
        # First, make sure the paths being passed in are correct
527
542
        def _key(p):
528
543
            dirname, basename = os.path.split(p)
529
 
            return dirname.split(b'/'), basename
 
544
            return dirname.split('/'), basename
530
545
        self.assertEqual(sorted(paths, key=_key), paths)
531
546
 
532
 
        lt_path_by_dirblock = self.get_lt_path_by_dirblock()
 
547
        cmp_path_by_dirblock = self.get_cmp_path_by_dirblock()
533
548
        for idx1, path1 in enumerate(paths):
534
549
            for idx2, path2 in enumerate(paths):
535
 
                lt_result = lt_path_by_dirblock(path1, path2)
536
 
                self.assertEqual(idx1 < idx2, lt_result,
537
 
                                 '%s did not state that %r < %r, lt=%s'
538
 
                                 % (lt_path_by_dirblock.__name__,
539
 
                                    path1, path2, lt_result))
 
550
                cmp_val = cmp_path_by_dirblock(path1, path2)
 
551
                if idx1 < idx2:
 
552
                    self.assertTrue(cmp_val < 0,
 
553
                        '%s did not state that %r came before %r, cmp=%s'
 
554
                        % (cmp_path_by_dirblock.__name__,
 
555
                           path1, path2, cmp_val))
 
556
                elif idx1 > idx2:
 
557
                    self.assertTrue(cmp_val > 0,
 
558
                        '%s did not state that %r came after %r, cmp=%s'
 
559
                        % (cmp_path_by_dirblock.__name__,
 
560
                           path1, path2, cmp_val))
 
561
                else: # idx1 == idx2
 
562
                    self.assertTrue(cmp_val == 0,
 
563
                        '%s did not state that %r == %r, cmp=%s'
 
564
                        % (cmp_path_by_dirblock.__name__,
 
565
                           path1, path2, cmp_val))
540
566
 
541
567
    def test_cmp_simple_paths(self):
542
568
        """Compare against the empty string."""
543
 
        self.assertLtPathByDirblock(
544
 
            [b'', b'a', b'ab', b'abc', b'a/b/c', b'b/d/e'])
545
 
        self.assertLtPathByDirblock([b'kl', b'ab/cd', b'ab/ef', b'gh/ij'])
 
569
        self.assertCmpPathByDirblock(['', 'a', 'ab', 'abc', 'a/b/c', 'b/d/e'])
 
570
        self.assertCmpPathByDirblock(['kl', 'ab/cd', 'ab/ef', 'gh/ij'])
546
571
 
547
572
    def test_tricky_paths(self):
548
 
        self.assertLtPathByDirblock([
 
573
        self.assertCmpPathByDirblock([
549
574
            # Contents of ''
550
 
            b'', b'a', b'a-a', b'a=a', b'b',
 
575
            '', 'a', 'a-a', 'a=a', 'b',
551
576
            # Contents of 'a'
552
 
            b'a/a', b'a/a-a', b'a/a=a', b'a/b',
 
577
            'a/a', 'a/a-a', 'a/a=a', 'a/b',
553
578
            # Contents of 'a/a'
554
 
            b'a/a/a', b'a/a/a-a', b'a/a/a=a',
 
579
            'a/a/a', 'a/a/a-a', 'a/a/a=a',
555
580
            # Contents of 'a/a/a'
556
 
            b'a/a/a/a', b'a/a/a/b',
 
581
            'a/a/a/a', 'a/a/a/b',
557
582
            # Contents of 'a/a/a-a',
558
 
            b'a/a/a-a/a', b'a/a/a-a/b',
 
583
            'a/a/a-a/a', 'a/a/a-a/b',
559
584
            # Contents of 'a/a/a=a',
560
 
            b'a/a/a=a/a', b'a/a/a=a/b',
 
585
            'a/a/a=a/a', 'a/a/a=a/b',
561
586
            # Contents of 'a/a-a'
562
 
            b'a/a-a/a',
 
587
            'a/a-a/a',
563
588
            # Contents of 'a/a-a/a'
564
 
            b'a/a-a/a/a', b'a/a-a/a/b',
 
589
            'a/a-a/a/a', 'a/a-a/a/b',
565
590
            # Contents of 'a/a=a'
566
 
            b'a/a=a/a',
 
591
            'a/a=a/a',
567
592
            # Contents of 'a/b'
568
 
            b'a/b/a', b'a/b/b',
 
593
            'a/b/a', 'a/b/b',
569
594
            # Contents of 'a-a',
570
 
            b'a-a/a', b'a-a/b',
 
595
            'a-a/a', 'a-a/b',
571
596
            # Contents of 'a=a',
572
 
            b'a=a/a', b'a=a/b',
 
597
            'a=a/a', 'a=a/b',
573
598
            # Contents of 'b',
574
 
            b'b/a', b'b/b',
 
599
            'b/a', 'b/b',
575
600
            ])
576
 
        self.assertLtPathByDirblock([
577
 
            # content of '/'
578
 
            b'', b'a', b'a-a', b'a-z', b'a=a', b'a=z',
 
601
        self.assertCmpPathByDirblock([
 
602
                 # content of '/'
 
603
                 '', 'a', 'a-a', 'a-z', 'a=a', 'a=z',
579
604
                 # content of 'a/'
580
 
                 b'a/a', b'a/a-a', b'a/a-z',
581
 
                 b'a/a=a', b'a/a=z',
582
 
                 b'a/z', b'a/z-a', b'a/z-z',
583
 
                 b'a/z=a', b'a/z=z',
 
605
                 'a/a', 'a/a-a', 'a/a-z',
 
606
                 'a/a=a', 'a/a=z',
 
607
                 'a/z', 'a/z-a', 'a/z-z',
 
608
                 'a/z=a', 'a/z=z',
584
609
                 # content of 'a/a/'
585
 
                 b'a/a/a', b'a/a/z',
 
610
                 'a/a/a', 'a/a/z',
586
611
                 # content of 'a/a-a'
587
 
                 b'a/a-a/a',
 
612
                 'a/a-a/a',
588
613
                 # content of 'a/a-z'
589
 
                 b'a/a-z/z',
 
614
                 'a/a-z/z',
590
615
                 # content of 'a/a=a'
591
 
                 b'a/a=a/a',
 
616
                 'a/a=a/a',
592
617
                 # content of 'a/a=z'
593
 
                 b'a/a=z/z',
 
618
                 'a/a=z/z',
594
619
                 # content of 'a/z/'
595
 
                 b'a/z/a', b'a/z/z',
 
620
                 'a/z/a', 'a/z/z',
596
621
                 # content of 'a-a'
597
 
                 b'a-a/a',
 
622
                 'a-a/a',
598
623
                 # content of 'a-z'
599
 
                 b'a-z/z',
 
624
                 'a-z/z',
600
625
                 # content of 'a=a'
601
 
                 b'a=a/a',
 
626
                 'a=a/a',
602
627
                 # content of 'a=z'
603
 
                 b'a=z/z',
604
 
            ])
 
628
                 'a=z/z',
 
629
                ])
605
630
 
606
631
    def test_unicode_not_allowed(self):
607
 
        lt_path_by_dirblock = self.get_lt_path_by_dirblock()
608
 
        self.assertRaises(TypeError, lt_path_by_dirblock, u'Uni', 'str')
609
 
        self.assertRaises(TypeError, lt_path_by_dirblock, 'str', u'Uni')
610
 
        self.assertRaises(TypeError, lt_path_by_dirblock, u'Uni', u'Uni')
611
 
        self.assertRaises(TypeError, lt_path_by_dirblock, u'x/Uni', 'x/str')
612
 
        self.assertRaises(TypeError, lt_path_by_dirblock, 'x/str', u'x/Uni')
613
 
        self.assertRaises(TypeError, lt_path_by_dirblock, u'x/Uni', u'x/Uni')
 
632
        cmp_path_by_dirblock = self.get_cmp_path_by_dirblock()
 
633
        self.assertRaises(TypeError, cmp_path_by_dirblock, u'Uni', 'str')
 
634
        self.assertRaises(TypeError, cmp_path_by_dirblock, 'str', u'Uni')
 
635
        self.assertRaises(TypeError, cmp_path_by_dirblock, u'Uni', u'Uni')
 
636
        self.assertRaises(TypeError, cmp_path_by_dirblock, u'x/Uni', 'x/str')
 
637
        self.assertRaises(TypeError, cmp_path_by_dirblock, 'x/str', u'x/Uni')
 
638
        self.assertRaises(TypeError, cmp_path_by_dirblock, u'x/Uni', u'x/Uni')
614
639
 
615
640
    def test_nonascii(self):
616
 
        self.assertLtPathByDirblock([
 
641
        self.assertCmpPathByDirblock([
617
642
            # content of '/'
618
 
            b'', b'a', b'\xc2\xb5', b'\xc3\xa5',
 
643
            '', 'a', '\xc2\xb5', '\xc3\xa5',
619
644
            # content of 'a'
620
 
            b'a/a', b'a/\xc2\xb5', b'a/\xc3\xa5',
 
645
            'a/a', 'a/\xc2\xb5', 'a/\xc3\xa5',
621
646
            # content of 'a/a'
622
 
            b'a/a/a', b'a/a/\xc2\xb5', b'a/a/\xc3\xa5',
 
647
            'a/a/a', 'a/a/\xc2\xb5', 'a/a/\xc3\xa5',
623
648
            # content of 'a/\xc2\xb5'
624
 
            b'a/\xc2\xb5/a', b'a/\xc2\xb5/\xc2\xb5', b'a/\xc2\xb5/\xc3\xa5',
 
649
            'a/\xc2\xb5/a', 'a/\xc2\xb5/\xc2\xb5', 'a/\xc2\xb5/\xc3\xa5',
625
650
            # content of 'a/\xc3\xa5'
626
 
            b'a/\xc3\xa5/a', b'a/\xc3\xa5/\xc2\xb5', b'a/\xc3\xa5/\xc3\xa5',
 
651
            'a/\xc3\xa5/a', 'a/\xc3\xa5/\xc2\xb5', 'a/\xc3\xa5/\xc3\xa5',
627
652
            # content of '\xc2\xb5'
628
 
            b'\xc2\xb5/a', b'\xc2\xb5/\xc2\xb5', b'\xc2\xb5/\xc3\xa5',
 
653
            '\xc2\xb5/a', '\xc2\xb5/\xc2\xb5', '\xc2\xb5/\xc3\xa5',
629
654
            # content of '\xc2\xe5'
630
 
            b'\xc3\xa5/a', b'\xc3\xa5/\xc2\xb5', b'\xc3\xa5/\xc3\xa5',
 
655
            '\xc3\xa5/a', '\xc3\xa5/\xc2\xb5', '\xc3\xa5/\xc3\xa5',
631
656
            ])
632
657
 
633
658
 
634
 
class TestCompiledLtPathByDirblock(TestLtPathByDirblock):
635
 
    """Test the pyrex implementation of _lt_path_by_dirblock"""
 
659
class TestCompiledCmpPathByDirblock(TestCmpPathByDirblock):
 
660
    """Test the pyrex implementation of _cmp_path_by_dirblock"""
636
661
 
637
662
    _test_needs_features = [compiled_dirstate_helpers_feature]
638
663
 
639
 
    def get_lt_path_by_dirblock(self):
640
 
        from ..bzr._dirstate_helpers_pyx import _lt_path_by_dirblock
641
 
        return _lt_path_by_dirblock
 
664
    def get_cmp_by_dirs(self):
 
665
        from bzrlib._dirstate_helpers_pyx import _cmp_path_by_dirblock
 
666
        return _cmp_path_by_dirblock
642
667
 
643
668
 
644
669
class TestMemRChr(tests.TestCase):
647
672
    _test_needs_features = [compiled_dirstate_helpers_feature]
648
673
 
649
674
    def assertMemRChr(self, expected, s, c):
650
 
        from breezy.bzr._dirstate_helpers_pyx import _py_memrchr
 
675
        from bzrlib._dirstate_helpers_pyx import _py_memrchr
651
676
        self.assertEqual(expected, _py_memrchr(s, c))
652
677
 
653
678
    def test_missing(self):
654
 
        self.assertMemRChr(None, b'', b'a')
655
 
        self.assertMemRChr(None, b'', b'c')
656
 
        self.assertMemRChr(None, b'abcdefghijklm', b'q')
657
 
        self.assertMemRChr(None, b'aaaaaaaaaaaaaaaaaaaaaaa', b'b')
 
679
        self.assertMemRChr(None, '', 'a')
 
680
        self.assertMemRChr(None, '', 'c')
 
681
        self.assertMemRChr(None, 'abcdefghijklm', 'q')
 
682
        self.assertMemRChr(None, 'aaaaaaaaaaaaaaaaaaaaaaa', 'b')
658
683
 
659
684
    def test_single_entry(self):
660
 
        self.assertMemRChr(0, b'abcdefghijklm', b'a')
661
 
        self.assertMemRChr(1, b'abcdefghijklm', b'b')
662
 
        self.assertMemRChr(2, b'abcdefghijklm', b'c')
663
 
        self.assertMemRChr(10, b'abcdefghijklm', b'k')
664
 
        self.assertMemRChr(11, b'abcdefghijklm', b'l')
665
 
        self.assertMemRChr(12, b'abcdefghijklm', b'm')
 
685
        self.assertMemRChr(0, 'abcdefghijklm', 'a')
 
686
        self.assertMemRChr(1, 'abcdefghijklm', 'b')
 
687
        self.assertMemRChr(2, 'abcdefghijklm', 'c')
 
688
        self.assertMemRChr(10, 'abcdefghijklm', 'k')
 
689
        self.assertMemRChr(11, 'abcdefghijklm', 'l')
 
690
        self.assertMemRChr(12, 'abcdefghijklm', 'm')
666
691
 
667
692
    def test_multiple(self):
668
 
        self.assertMemRChr(10, b'abcdefjklmabcdefghijklm', b'a')
669
 
        self.assertMemRChr(11, b'abcdefjklmabcdefghijklm', b'b')
670
 
        self.assertMemRChr(12, b'abcdefjklmabcdefghijklm', b'c')
671
 
        self.assertMemRChr(20, b'abcdefjklmabcdefghijklm', b'k')
672
 
        self.assertMemRChr(21, b'abcdefjklmabcdefghijklm', b'l')
673
 
        self.assertMemRChr(22, b'abcdefjklmabcdefghijklm', b'm')
674
 
        self.assertMemRChr(22, b'aaaaaaaaaaaaaaaaaaaaaaa', b'a')
 
693
        self.assertMemRChr(10, 'abcdefjklmabcdefghijklm', 'a')
 
694
        self.assertMemRChr(11, 'abcdefjklmabcdefghijklm', 'b')
 
695
        self.assertMemRChr(12, 'abcdefjklmabcdefghijklm', 'c')
 
696
        self.assertMemRChr(20, 'abcdefjklmabcdefghijklm', 'k')
 
697
        self.assertMemRChr(21, 'abcdefjklmabcdefghijklm', 'l')
 
698
        self.assertMemRChr(22, 'abcdefjklmabcdefghijklm', 'm')
 
699
        self.assertMemRChr(22, 'aaaaaaaaaaaaaaaaaaaaaaa', 'a')
675
700
 
676
701
    def test_with_nulls(self):
677
 
        self.assertMemRChr(10, b'abc\0\0\0jklmabc\0\0\0ghijklm', b'a')
678
 
        self.assertMemRChr(11, b'abc\0\0\0jklmabc\0\0\0ghijklm', b'b')
679
 
        self.assertMemRChr(12, b'abc\0\0\0jklmabc\0\0\0ghijklm', b'c')
680
 
        self.assertMemRChr(20, b'abc\0\0\0jklmabc\0\0\0ghijklm', b'k')
681
 
        self.assertMemRChr(21, b'abc\0\0\0jklmabc\0\0\0ghijklm', b'l')
682
 
        self.assertMemRChr(22, b'abc\0\0\0jklmabc\0\0\0ghijklm', b'm')
683
 
        self.assertMemRChr(22, b'aaa\0\0\0aaaaaaa\0\0\0aaaaaaa', b'a')
684
 
        self.assertMemRChr(9, b'\0\0\0\0\0\0\0\0\0\0', b'\0')
 
702
        self.assertMemRChr(10, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'a')
 
703
        self.assertMemRChr(11, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'b')
 
704
        self.assertMemRChr(12, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'c')
 
705
        self.assertMemRChr(20, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'k')
 
706
        self.assertMemRChr(21, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'l')
 
707
        self.assertMemRChr(22, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'm')
 
708
        self.assertMemRChr(22, 'aaa\0\0\0aaaaaaa\0\0\0aaaaaaa', 'a')
 
709
        self.assertMemRChr(9, '\0\0\0\0\0\0\0\0\0\0', '\0')
685
710
 
686
711
 
687
712
class TestReadDirblocks(test_dirstate.TestCaseWithDirState):
694
719
    implementation.
695
720
    """
696
721
 
697
 
    # inherits scenarios from test_dirstate
698
 
 
699
722
    def get_read_dirblocks(self):
700
 
        from breezy.bzr._dirstate_helpers_py import _read_dirblocks
 
723
        from bzrlib._dirstate_helpers_py import _read_dirblocks
701
724
        return _read_dirblocks
702
725
 
703
726
    def test_smoketest(self):
714
737
 
715
738
    def test_trailing_garbage(self):
716
739
        tree, state, expected = self.create_basic_dirstate()
717
 
        # On Unix, we can write extra data as long as we haven't read yet, but
 
740
        # On Linux, we can write extra data as long as we haven't read yet, but
718
741
        # on Win32, if you've opened the file with FILE_SHARE_READ, trying to
719
742
        # open it in append mode will fail.
720
743
        state.unlock()
721
744
        f = open('dirstate', 'ab')
722
745
        try:
723
746
            # Add bogus trailing garbage
724
 
            f.write(b'bogus\n')
 
747
            f.write('bogus\n')
725
748
        finally:
726
749
            f.close()
727
750
            state.lock_read()
728
 
        e = self.assertRaises(dirstate.DirstateCorrupt,
 
751
        e = self.assertRaises(errors.DirstateCorrupt,
729
752
                              state._read_dirblocks_if_needed)
730
753
        # Make sure we mention the bogus characters in the error
731
754
        self.assertContainsRe(str(e), 'bogus')
737
760
    _test_needs_features = [compiled_dirstate_helpers_feature]
738
761
 
739
762
    def get_read_dirblocks(self):
740
 
        from breezy.bzr._dirstate_helpers_pyx import _read_dirblocks
 
763
        from bzrlib._dirstate_helpers_pyx import _read_dirblocks
741
764
        return _read_dirblocks
742
765
 
743
766
 
751
774
 
752
775
    def test_bisect_dirblock(self):
753
776
        if compiled_dirstate_helpers_feature.available():
754
 
            from breezy.bzr._dirstate_helpers_pyx import bisect_dirblock
 
777
            from bzrlib._dirstate_helpers_pyx import bisect_dirblock
755
778
        else:
756
 
            from breezy.bzr._dirstate_helpers_py import bisect_dirblock
 
779
            from bzrlib._dirstate_helpers_py import bisect_dirblock
757
780
        self.assertIs(bisect_dirblock, dirstate.bisect_dirblock)
758
781
 
759
782
    def test__bisect_path_left(self):
760
783
        if compiled_dirstate_helpers_feature.available():
761
 
            from breezy.bzr._dirstate_helpers_pyx import _bisect_path_left
 
784
            from bzrlib._dirstate_helpers_pyx import _bisect_path_left
762
785
        else:
763
 
            from breezy.bzr._dirstate_helpers_py import _bisect_path_left
 
786
            from bzrlib._dirstate_helpers_py import _bisect_path_left
764
787
        self.assertIs(_bisect_path_left, dirstate._bisect_path_left)
765
788
 
766
789
    def test__bisect_path_right(self):
767
790
        if compiled_dirstate_helpers_feature.available():
768
 
            from breezy.bzr._dirstate_helpers_pyx import _bisect_path_right
 
791
            from bzrlib._dirstate_helpers_pyx import _bisect_path_right
769
792
        else:
770
 
            from breezy.bzr._dirstate_helpers_py import _bisect_path_right
 
793
            from bzrlib._dirstate_helpers_py import _bisect_path_right
771
794
        self.assertIs(_bisect_path_right, dirstate._bisect_path_right)
772
795
 
773
 
    def test_lt_by_dirs(self):
 
796
    def test_cmp_by_dirs(self):
774
797
        if compiled_dirstate_helpers_feature.available():
775
 
            from ..bzr._dirstate_helpers_pyx import lt_by_dirs
 
798
            from bzrlib._dirstate_helpers_pyx import cmp_by_dirs
776
799
        else:
777
 
            from ..bzr._dirstate_helpers_py import lt_by_dirs
778
 
        self.assertIs(lt_by_dirs, dirstate.lt_by_dirs)
 
800
            from bzrlib._dirstate_helpers_py import cmp_by_dirs
 
801
        self.assertIs(cmp_by_dirs, dirstate.cmp_by_dirs)
779
802
 
780
803
    def test__read_dirblocks(self):
781
804
        if compiled_dirstate_helpers_feature.available():
782
 
            from breezy.bzr._dirstate_helpers_pyx import _read_dirblocks
 
805
            from bzrlib._dirstate_helpers_pyx import _read_dirblocks
783
806
        else:
784
 
            from breezy.bzr._dirstate_helpers_py import _read_dirblocks
 
807
            from bzrlib._dirstate_helpers_py import _read_dirblocks
785
808
        self.assertIs(_read_dirblocks, dirstate._read_dirblocks)
786
809
 
787
810
    def test_update_entry(self):
788
811
        if compiled_dirstate_helpers_feature.available():
789
 
            from breezy.bzr._dirstate_helpers_pyx import update_entry
 
812
            from bzrlib._dirstate_helpers_pyx import update_entry
790
813
        else:
791
 
            from breezy.bzr.dirstate import update_entry
 
814
            from bzrlib.dirstate import update_entry
792
815
        self.assertIs(update_entry, dirstate.update_entry)
793
816
 
794
817
    def test_process_entry(self):
795
818
        if compiled_dirstate_helpers_feature.available():
796
 
            from breezy.bzr._dirstate_helpers_pyx import ProcessEntryC
 
819
            from bzrlib._dirstate_helpers_pyx import ProcessEntryC
797
820
            self.assertIs(ProcessEntryC, dirstate._process_entry)
798
821
        else:
799
 
            from breezy.bzr.dirstate import ProcessEntryPython
 
822
            from bzrlib.dirstate import ProcessEntryPython
800
823
            self.assertIs(ProcessEntryPython, dirstate._process_entry)
801
824
 
802
825
 
803
826
class TestUpdateEntry(test_dirstate.TestCaseWithDirState):
804
827
    """Test the DirState.update_entry functions"""
805
828
 
806
 
    scenarios = multiply_scenarios(
807
 
        dir_reader_scenarios(), ue_scenarios)
808
 
 
809
829
    # Set by load_tests
810
830
    update_entry = None
811
831
 
817
837
        """Create a DirState tracking a single object named 'a'"""
818
838
        state = test_dirstate.InstrumentedDirState.initialize('dirstate')
819
839
        self.addCleanup(state.unlock)
820
 
        state.add('a', b'a-id', 'file', None, b'')
821
 
        entry = state._get_entry(0, path_utf8=b'a')
 
840
        state.add('a', 'a-id', 'file', None, '')
 
841
        entry = state._get_entry(0, path_utf8='a')
822
842
        return state, entry
823
843
 
824
844
    def test_observed_sha1_cachable(self):
825
845
        state, entry = self.get_state_with_a()
826
 
        state.save()
827
846
        atime = time.time() - 10
828
847
        self.build_tree(['a'])
829
 
        statvalue = test_dirstate._FakeStat.from_stat(os.lstat('a'))
830
 
        statvalue.st_mtime = statvalue.st_ctime = atime
831
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
832
 
                         state._dirblock_state)
833
 
        state._observed_sha1(entry, b"foo", statvalue)
834
 
        self.assertEqual(b'foo', entry[1][0][1])
 
848
        statvalue = os.lstat('a')
 
849
        statvalue = test_dirstate._FakeStat(statvalue.st_size, atime, atime,
 
850
            statvalue.st_dev, statvalue.st_ino, statvalue.st_mode)
 
851
        state._observed_sha1(entry, "foo", statvalue)
 
852
        self.assertEqual('foo', entry[1][0][1])
835
853
        packed_stat = dirstate.pack_stat(statvalue)
836
854
        self.assertEqual(packed_stat, entry[1][0][4])
837
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
838
 
                         state._dirblock_state)
839
855
 
840
856
    def test_observed_sha1_not_cachable(self):
841
857
        state, entry = self.get_state_with_a()
842
 
        state.save()
843
858
        oldval = entry[1][0][1]
844
859
        oldstat = entry[1][0][4]
845
860
        self.build_tree(['a'])
846
861
        statvalue = os.lstat('a')
847
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
848
 
                         state._dirblock_state)
849
862
        state._observed_sha1(entry, "foo", statvalue)
850
863
        self.assertEqual(oldval, entry[1][0][1])
851
864
        self.assertEqual(oldstat, entry[1][0][4])
852
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
853
 
                         state._dirblock_state)
854
865
 
855
866
    def test_update_entry(self):
856
867
        state, _ = self.get_state_with_a()
858
869
        tree.lock_write()
859
870
        empty_revid = tree.commit('empty')
860
871
        self.build_tree(['tree/a'])
861
 
        tree.add(['a'], [b'a-id'])
 
872
        tree.add(['a'], ['a-id'])
862
873
        with_a_id = tree.commit('with_a')
863
874
        self.addCleanup(tree.unlock)
864
875
        state.set_parent_trees(
865
876
            [(empty_revid, tree.branch.repository.revision_tree(empty_revid))],
866
877
            [])
867
 
        entry = state._get_entry(0, path_utf8=b'a')
 
878
        entry = state._get_entry(0, path_utf8='a')
868
879
        self.build_tree(['a'])
869
880
        # Add one where we don't provide the stat or sha already
870
 
        self.assertEqual((b'', b'a', b'a-id'), entry[0])
871
 
        self.assertEqual((b'f', b'', 0, False, dirstate.DirState.NULLSTAT),
 
881
        self.assertEqual(('', 'a', 'a-id'), entry[0])
 
882
        self.assertEqual(('f', '', 0, False, dirstate.DirState.NULLSTAT),
872
883
                         entry[1][0])
873
884
        # Flush the buffers to disk
874
885
        state.save()
877
888
 
878
889
        stat_value = os.lstat('a')
879
890
        packed_stat = dirstate.pack_stat(stat_value)
880
 
        link_or_sha1 = self.update_entry(state, entry, abspath=b'a',
881
 
                                         stat_value=stat_value)
 
891
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
 
892
                                          stat_value=stat_value)
882
893
        self.assertEqual(None, link_or_sha1)
883
894
 
884
 
        # The dirblock entry should not have computed or cached the file's
885
 
        # sha1, but it did update the files' st_size. However, this is not
886
 
        # worth writing a dirstate file for, so we leave the state UNMODIFIED
887
 
        self.assertEqual((b'f', b'', 14, False, dirstate.DirState.NULLSTAT),
 
895
        # The dirblock entry should not have cached the file's sha1 (too new)
 
896
        self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
888
897
                         entry[1][0])
889
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
 
898
        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
890
899
                         state._dirblock_state)
891
900
        mode = stat_value.st_mode
892
901
        self.assertEqual([('is_exec', mode, False)], state._log)
895
904
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
896
905
                         state._dirblock_state)
897
906
 
898
 
        # Roll the clock back so the file is guaranteed to look too new. We
899
 
        # should still not compute the sha1.
 
907
        # If we do it again right away, we don't know if the file has changed
 
908
        # so we will re-read the file. Roll the clock back so the file is
 
909
        # guaranteed to look too new.
900
910
        state.adjust_time(-10)
901
911
        del state._log[:]
902
912
 
903
 
        link_or_sha1 = self.update_entry(state, entry, abspath=b'a',
904
 
                                         stat_value=stat_value)
 
913
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
 
914
                                          stat_value=stat_value)
905
915
        self.assertEqual([('is_exec', mode, False)], state._log)
906
916
        self.assertEqual(None, link_or_sha1)
907
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
 
917
        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
908
918
                         state._dirblock_state)
909
 
        self.assertEqual((b'f', b'', 14, False, dirstate.DirState.NULLSTAT),
 
919
        self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
910
920
                         entry[1][0])
911
921
        state.save()
912
922
 
914
924
        # won't calculate the sha or cache it.
915
925
        state.adjust_time(+20)
916
926
        del state._log[:]
917
 
        link_or_sha1 = dirstate.update_entry(state, entry, abspath=b'a',
918
 
                                             stat_value=stat_value)
 
927
        link_or_sha1 = dirstate.update_entry(state, entry, abspath='a',
 
928
                                          stat_value=stat_value)
919
929
        self.assertEqual(None, link_or_sha1)
920
930
        self.assertEqual([('is_exec', mode, False)], state._log)
921
 
        self.assertEqual((b'f', b'', 14, False, dirstate.DirState.NULLSTAT),
 
931
        self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
922
932
                         entry[1][0])
923
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
924
 
                         state._dirblock_state)
925
933
 
926
934
        # If the file is no longer new, and the clock has been moved forward
927
935
        # sufficiently, it will cache the sha.
929
937
        state.set_parent_trees(
930
938
            [(with_a_id, tree.branch.repository.revision_tree(with_a_id))],
931
939
            [])
932
 
        entry = state._get_entry(0, path_utf8=b'a')
 
940
        entry = state._get_entry(0, path_utf8='a')
933
941
 
934
 
        link_or_sha1 = self.update_entry(state, entry, abspath=b'a',
935
 
                                         stat_value=stat_value)
936
 
        self.assertEqual(b'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
 
942
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
 
943
                                          stat_value=stat_value)
 
944
        self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
937
945
                         link_or_sha1)
938
 
        self.assertEqual([('is_exec', mode, False), ('sha1', b'a')],
939
 
                         state._log)
940
 
        self.assertEqual((b'f', link_or_sha1, 14, False, packed_stat),
 
946
        self.assertEqual([('is_exec', mode, False), ('sha1', 'a')],
 
947
                          state._log)
 
948
        self.assertEqual(('f', link_or_sha1, 14, False, packed_stat),
941
949
                         entry[1][0])
942
950
 
943
951
        # Subsequent calls will just return the cached value
944
952
        del state._log[:]
945
 
        link_or_sha1 = self.update_entry(state, entry, abspath=b'a',
946
 
                                         stat_value=stat_value)
947
 
        self.assertEqual(b'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
 
953
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
 
954
                                          stat_value=stat_value)
 
955
        self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
948
956
                         link_or_sha1)
949
957
        self.assertEqual([], state._log)
950
 
        self.assertEqual((b'f', link_or_sha1, 14, False, packed_stat),
 
958
        self.assertEqual(('f', link_or_sha1, 14, False, packed_stat),
951
959
                         entry[1][0])
952
960
 
953
961
    def test_update_entry_symlink(self):
954
962
        """Update entry should read symlinks."""
955
 
        self.requireFeature(features.SymlinkFeature)
 
963
        self.requireFeature(tests.SymlinkFeature)
956
964
        state, entry = self.get_state_with_a()
957
965
        state.save()
958
966
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
959
967
                         state._dirblock_state)
960
968
        os.symlink('target', 'a')
961
969
 
962
 
        state.adjust_time(-10)  # Make the symlink look new
 
970
        state.adjust_time(-10) # Make the symlink look new
963
971
        stat_value = os.lstat('a')
964
972
        packed_stat = dirstate.pack_stat(stat_value)
965
 
        link_or_sha1 = self.update_entry(state, entry, abspath=b'a',
966
 
                                         stat_value=stat_value)
967
 
        self.assertEqual(b'target', link_or_sha1)
968
 
        self.assertEqual([('read_link', b'a', b'')], state._log)
 
973
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
 
974
                                          stat_value=stat_value)
 
975
        self.assertEqual('target', link_or_sha1)
 
976
        self.assertEqual([('read_link', 'a', '')], state._log)
969
977
        # Dirblock is not updated (the link is too new)
970
 
        self.assertEqual([(b'l', b'', 6, False, dirstate.DirState.NULLSTAT)],
 
978
        self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
971
979
                         entry[1])
972
 
        # The file entry turned into a symlink, that is considered
973
 
        # HASH modified worthy.
974
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
 
980
        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
975
981
                         state._dirblock_state)
976
982
 
977
983
        # Because the stat_value looks new, we should re-read the target
978
 
        del state._log[:]
979
 
        link_or_sha1 = self.update_entry(state, entry, abspath=b'a',
980
 
                                         stat_value=stat_value)
981
 
        self.assertEqual(b'target', link_or_sha1)
982
 
        self.assertEqual([('read_link', b'a', b'')], state._log)
983
 
        self.assertEqual([(b'l', b'', 6, False, dirstate.DirState.NULLSTAT)],
 
984
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
 
985
                                          stat_value=stat_value)
 
986
        self.assertEqual('target', link_or_sha1)
 
987
        self.assertEqual([('read_link', 'a', ''),
 
988
                          ('read_link', 'a', ''),
 
989
                         ], state._log)
 
990
        self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
984
991
                         entry[1])
985
 
        state.save()
986
 
        state.adjust_time(+20)  # Skip into the future, all files look old
987
 
        del state._log[:]
988
 
        link_or_sha1 = self.update_entry(state, entry, abspath=b'a',
989
 
                                         stat_value=stat_value)
990
 
        # The symlink stayed a symlink. So while it is new enough to cache, we
991
 
        # don't bother setting the flag, because it is not really worth saving
992
 
        # (when we stat the symlink, we'll have paged in the target.)
993
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
994
 
                         state._dirblock_state)
995
 
        self.assertEqual(b'target', link_or_sha1)
 
992
        state.adjust_time(+20) # Skip into the future, all files look old
 
993
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
 
994
                                          stat_value=stat_value)
 
995
        self.assertEqual('target', link_or_sha1)
996
996
        # We need to re-read the link because only now can we cache it
997
 
        self.assertEqual([('read_link', b'a', b'')], state._log)
998
 
        self.assertEqual([(b'l', b'target', 6, False, packed_stat)],
 
997
        self.assertEqual([('read_link', 'a', ''),
 
998
                          ('read_link', 'a', ''),
 
999
                          ('read_link', 'a', ''),
 
1000
                         ], state._log)
 
1001
        self.assertEqual([('l', 'target', 6, False, packed_stat)],
999
1002
                         entry[1])
1000
1003
 
1001
 
        del state._log[:]
1002
1004
        # Another call won't re-read the link
1003
 
        self.assertEqual([], state._log)
1004
 
        link_or_sha1 = self.update_entry(state, entry, abspath=b'a',
1005
 
                                         stat_value=stat_value)
1006
 
        self.assertEqual(b'target', link_or_sha1)
1007
 
        self.assertEqual([(b'l', b'target', 6, False, packed_stat)],
 
1005
        self.assertEqual([('read_link', 'a', ''),
 
1006
                          ('read_link', 'a', ''),
 
1007
                          ('read_link', 'a', ''),
 
1008
                         ], state._log)
 
1009
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
 
1010
                                          stat_value=stat_value)
 
1011
        self.assertEqual('target', link_or_sha1)
 
1012
        self.assertEqual([('l', 'target', 6, False, packed_stat)],
1008
1013
                         entry[1])
1009
1014
 
1010
1015
    def do_update_entry(self, state, entry, abspath):
1014
1019
    def test_update_entry_dir(self):
1015
1020
        state, entry = self.get_state_with_a()
1016
1021
        self.build_tree(['a/'])
1017
 
        self.assertIs(None, self.do_update_entry(state, entry, b'a'))
 
1022
        self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1018
1023
 
1019
1024
    def test_update_entry_dir_unchanged(self):
1020
1025
        state, entry = self.get_state_with_a()
1021
1026
        self.build_tree(['a/'])
1022
1027
        state.adjust_time(+20)
1023
 
        self.assertIs(None, self.do_update_entry(state, entry, b'a'))
1024
 
        # a/ used to be a file, but is now a directory, worth saving
 
1028
        self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1025
1029
        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1026
1030
                         state._dirblock_state)
1027
1031
        state.save()
1028
1032
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1029
1033
                         state._dirblock_state)
1030
 
        # No changes to a/ means not worth saving.
1031
 
        self.assertIs(None, self.do_update_entry(state, entry, b'a'))
1032
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1033
 
                         state._dirblock_state)
1034
 
        # Change the last-modified time for the directory
1035
 
        t = time.time() - 100.0
1036
 
        try:
1037
 
            os.utime('a', (t, t))
1038
 
        except OSError:
1039
 
            # It looks like Win32 + FAT doesn't allow to change times on a dir.
1040
 
            raise tests.TestSkipped("can't update mtime of a dir on FAT")
1041
 
        saved_packed_stat = entry[1][0][-1]
1042
 
        self.assertIs(None, self.do_update_entry(state, entry, b'a'))
1043
 
        # We *do* go ahead and update the information in the dirblocks, but we
1044
 
        # don't bother setting IN_MEMORY_MODIFIED because it is trivial to
1045
 
        # recompute.
1046
 
        self.assertNotEqual(saved_packed_stat, entry[1][0][-1])
 
1034
        self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1047
1035
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1048
1036
                         state._dirblock_state)
1049
1037
 
1052
1040
        tree = self.make_branch_and_tree('tree')
1053
1041
        tree.lock_write()
1054
1042
        self.build_tree(['tree/a'])
1055
 
        tree.add(['a'], [b'a-id'])
 
1043
        tree.add(['a'], ['a-id'])
1056
1044
        with_a_id = tree.commit('witha')
1057
1045
        self.addCleanup(tree.unlock)
1058
1046
        state.set_parent_trees(
1059
1047
            [(with_a_id, tree.branch.repository.revision_tree(with_a_id))],
1060
1048
            [])
1061
 
        entry = state._get_entry(0, path_utf8=b'a')
 
1049
        entry = state._get_entry(0, path_utf8='a')
1062
1050
        self.build_tree(['a'])
1063
 
        sha1sum = b'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
 
1051
        sha1sum = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1064
1052
        state.adjust_time(+20)
1065
 
        self.assertEqual(sha1sum, self.do_update_entry(state, entry, b'a'))
 
1053
        self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1066
1054
        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1067
1055
                         state._dirblock_state)
1068
1056
        state.save()
1069
1057
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1070
1058
                         state._dirblock_state)
1071
 
        self.assertEqual(sha1sum, self.do_update_entry(state, entry, b'a'))
 
1059
        self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1072
1060
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1073
1061
                         state._dirblock_state)
1074
1062
 
1075
1063
    def test_update_entry_tree_reference(self):
1076
1064
        state = test_dirstate.InstrumentedDirState.initialize('dirstate')
1077
1065
        self.addCleanup(state.unlock)
1078
 
        state.add('r', b'r-id', 'tree-reference', None, b'')
 
1066
        state.add('r', 'r-id', 'tree-reference', None, '')
1079
1067
        self.build_tree(['r/'])
1080
 
        entry = state._get_entry(0, path_utf8=b'r')
 
1068
        entry = state._get_entry(0, path_utf8='r')
1081
1069
        self.do_update_entry(state, entry, 'r')
1082
 
        entry = state._get_entry(0, path_utf8=b'r')
1083
 
        self.assertEqual(b't', entry[1][0][0])
 
1070
        entry = state._get_entry(0, path_utf8='r')
 
1071
        self.assertEqual('t', entry[1][0][0])
1084
1072
 
1085
1073
    def create_and_test_file(self, state, entry):
1086
1074
        """Create a file at 'a' and verify the state finds it during update.
1094
1082
 
1095
1083
        link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1096
1084
        self.assertEqual(None, link_or_sha1)
1097
 
        self.assertEqual([(b'f', b'', 14, False, dirstate.DirState.NULLSTAT)],
 
1085
        self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
1098
1086
                         entry[1])
1099
1087
        return packed_stat
1100
1088
 
1108
1096
        stat_value = os.lstat('a')
1109
1097
        packed_stat = dirstate.pack_stat(stat_value)
1110
1098
 
1111
 
        link_or_sha1 = self.do_update_entry(state, entry, abspath=b'a')
 
1099
        link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1112
1100
        self.assertIs(None, link_or_sha1)
1113
 
        self.assertEqual([(b'd', b'', 0, False, packed_stat)], entry[1])
 
1101
        self.assertEqual([('d', '', 0, False, packed_stat)], entry[1])
1114
1102
 
1115
1103
        return packed_stat
1116
1104
 
1130
1118
        stat_value = os.lstat('a')
1131
1119
        packed_stat = dirstate.pack_stat(stat_value)
1132
1120
 
1133
 
        link_or_sha1 = self.do_update_entry(state, entry, abspath=b'a')
1134
 
        self.assertEqual(b'path/to/foo', link_or_sha1)
1135
 
        self.assertEqual([(b'l', b'path/to/foo', 11, False, packed_stat)],
 
1121
        link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
 
1122
        self.assertEqual('path/to/foo', link_or_sha1)
 
1123
        self.assertEqual([('l', 'path/to/foo', 11, False, packed_stat)],
1136
1124
                         entry[1])
1137
1125
        return packed_stat
1138
1126
 
1149
1137
 
1150
1138
    def test_update_file_to_symlink(self):
1151
1139
        """File becomes a symlink"""
1152
 
        self.requireFeature(features.SymlinkFeature)
 
1140
        self.requireFeature(tests.SymlinkFeature)
1153
1141
        state, entry = self.get_state_with_a()
1154
1142
        # The file sha1 won't be cached unless the file is old
1155
1143
        state.adjust_time(+10)
1168
1156
 
1169
1157
    def test_update_dir_to_symlink(self):
1170
1158
        """Directory becomes a symlink"""
1171
 
        self.requireFeature(features.SymlinkFeature)
 
1159
        self.requireFeature(tests.SymlinkFeature)
1172
1160
        state, entry = self.get_state_with_a()
1173
1161
        # The symlink target won't be cached if it isn't old
1174
1162
        state.adjust_time(+10)
1178
1166
 
1179
1167
    def test_update_symlink_to_file(self):
1180
1168
        """Symlink becomes a file"""
1181
 
        self.requireFeature(features.SymlinkFeature)
 
1169
        self.requireFeature(tests.SymlinkFeature)
1182
1170
        state, entry = self.get_state_with_a()
1183
1171
        # The symlink and file info won't be cached unless old
1184
1172
        state.adjust_time(+10)
1188
1176
 
1189
1177
    def test_update_symlink_to_dir(self):
1190
1178
        """Symlink becomes a directory"""
1191
 
        self.requireFeature(features.SymlinkFeature)
 
1179
        self.requireFeature(tests.SymlinkFeature)
1192
1180
        state, entry = self.get_state_with_a()
1193
1181
        # The symlink target won't be cached if it isn't old
1194
1182
        state.adjust_time(+10)
1200
1188
        state, entry = self.get_state_with_a()
1201
1189
        self.build_tree(['a'])
1202
1190
 
1203
 
        # Make sure we are using the version of _is_executable that doesn't
1204
 
        # check the filesystem mode.
1205
 
        state._use_filesystem_for_exec = False
 
1191
        # Make sure we are using the win32 implementation of _is_executable
 
1192
        state._is_executable = state._is_executable_win32
1206
1193
 
1207
1194
        # The file on disk is not executable, but we are marking it as though
1208
 
        # it is. With _use_filesystem_for_exec disabled we ignore what is on
1209
 
        # disk.
1210
 
        entry[1][0] = (b'f', b'', 0, True, dirstate.DirState.NULLSTAT)
 
1195
        # it is. With _is_executable_win32 we ignore what is on disk.
 
1196
        entry[1][0] = ('f', '', 0, True, dirstate.DirState.NULLSTAT)
1211
1197
 
1212
1198
        stat_value = os.lstat('a')
1213
1199
        packed_stat = dirstate.pack_stat(stat_value)
1214
1200
 
1215
 
        state.adjust_time(-10)  # Make sure everything is new
1216
 
        self.update_entry(state, entry, abspath=b'a', stat_value=stat_value)
 
1201
        state.adjust_time(-10) # Make sure everything is new
 
1202
        self.update_entry(state, entry, abspath='a', stat_value=stat_value)
1217
1203
 
1218
1204
        # The row is updated, but the executable bit stays set.
1219
 
        self.assertEqual([(b'f', b'', 14, True, dirstate.DirState.NULLSTAT)],
 
1205
        self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1220
1206
                         entry[1])
1221
1207
 
1222
1208
        # Make the disk object look old enough to cache (but it won't cache the
1223
1209
        # sha as it is a new file).
1224
1210
        state.adjust_time(+20)
1225
 
        digest = b'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1226
 
        self.update_entry(state, entry, abspath=b'a', stat_value=stat_value)
1227
 
        self.assertEqual([(b'f', b'', 14, True, dirstate.DirState.NULLSTAT)],
1228
 
                         entry[1])
 
1211
        digest = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
 
1212
        self.update_entry(state, entry, abspath='a', stat_value=stat_value)
 
1213
        self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
 
1214
            entry[1])
1229
1215
 
1230
1216
    def _prepare_tree(self):
1231
1217
        # Create a tree
1232
 
        text = b'Hello World\n'
 
1218
        text = 'Hello World\n'
1233
1219
        tree = self.make_branch_and_tree('tree')
1234
1220
        self.build_tree_contents([('tree/a file', text)])
1235
 
        tree.add('a file', b'a-file-id')
 
1221
        tree.add('a file', 'a-file-id')
1236
1222
        # Note: dirstate does not sha prior to the first commit
1237
1223
        # so commit now in order for the test to work
1238
1224
        tree.commit('first')
1241
1227
    def test_sha1provider_sha1_used(self):
1242
1228
        tree, text = self._prepare_tree()
1243
1229
        state = dirstate.DirState.from_tree(tree, 'dirstate',
1244
 
                                            UppercaseSHA1Provider())
 
1230
            UppercaseSHA1Provider())
1245
1231
        self.addCleanup(state.unlock)
1246
 
        expected_sha = osutils.sha_string(text.upper() + b"foo")
1247
 
        entry = state._get_entry(0, path_utf8=b'a file')
1248
 
        self.assertNotEqual((None, None), entry)
 
1232
        expected_sha = osutils.sha_string(text.upper() + "foo")
 
1233
        entry = state._get_entry(0, path_utf8='a file')
1249
1234
        state._sha_cutoff_time()
1250
1235
        state._cutoff_time += 10
1251
1236
        sha1 = self.update_entry(state, entry, 'tree/a file',
1260
1245
        state._sha1_provider = UppercaseSHA1Provider()
1261
1246
        # If we used the standard provider, it would look like nothing has
1262
1247
        # changed
1263
 
        file_ids_changed = [change.file_id for change
 
1248
        file_ids_changed = [change[0] for change
1264
1249
                            in tree.iter_changes(tree.basis_tree())]
1265
 
        self.assertEqual([b'a-file-id'], file_ids_changed)
 
1250
        self.assertEqual(['a-file-id'], file_ids_changed)
1266
1251
 
1267
1252
 
1268
1253
class UppercaseSHA1Provider(dirstate.SHA1Provider):
1272
1257
        return self.stat_and_sha1(abspath)[1]
1273
1258
 
1274
1259
    def stat_and_sha1(self, abspath):
1275
 
        with open(abspath, 'rb') as file_obj:
 
1260
        file_obj = file(abspath, 'rb')
 
1261
        try:
1276
1262
            statvalue = os.fstat(file_obj.fileno())
1277
 
            text = b''.join(file_obj.readlines())
1278
 
            sha1 = osutils.sha_string(text.upper() + b"foo")
 
1263
            text = ''.join(file_obj.readlines())
 
1264
            sha1 = osutils.sha_string(text.upper() + "foo")
 
1265
        finally:
 
1266
            file_obj.close()
1279
1267
        return statvalue, sha1
1280
1268
 
1281
1269
 
1282
1270
class TestProcessEntry(test_dirstate.TestCaseWithDirState):
1283
1271
 
1284
 
    scenarios = multiply_scenarios(dir_reader_scenarios(), pe_scenarios)
1285
 
 
1286
1272
    # Set by load_tests
1287
1273
    _process_entry = None
1288
1274
 
1291
1277
        self.overrideAttr(dirstate, '_process_entry', self._process_entry)
1292
1278
 
1293
1279
    def assertChangedFileIds(self, expected, tree):
1294
 
        with tree.lock_read():
1295
 
            file_ids = [info.file_id for info
 
1280
        tree.lock_read()
 
1281
        try:
 
1282
            file_ids = [info[0] for info
1296
1283
                        in tree.iter_changes(tree.basis_tree())]
 
1284
        finally:
 
1285
            tree.unlock()
1297
1286
        self.assertEqual(sorted(expected), sorted(file_ids))
1298
1287
 
1299
1288
    def test_exceptions_raised(self):
1308
1297
        tree.lock_read()
1309
1298
        self.addCleanup(tree.unlock)
1310
1299
        basis_tree = tree.basis_tree()
1311
 
 
1312
1300
        def is_inside_raises(*args, **kwargs):
1313
1301
            raise RuntimeError('stop this')
1314
 
        self.overrideAttr(dirstate, 'is_inside', is_inside_raises)
1315
 
        try:
1316
 
            from breezy.bzr import _dirstate_helpers_pyx
1317
 
        except ImportError:
1318
 
            pass
1319
 
        else:
1320
 
            self.overrideAttr(_dirstate_helpers_pyx,
1321
 
                              'is_inside', is_inside_raises)
1322
1302
        self.overrideAttr(osutils, 'is_inside', is_inside_raises)
1323
1303
        self.assertListRaises(RuntimeError, tree.iter_changes, basis_tree)
1324
1304
 
1325
1305
    def test_simple_changes(self):
1326
1306
        tree = self.make_branch_and_tree('tree')
1327
1307
        self.build_tree(['tree/file'])
1328
 
        tree.add(['file'], [b'file-id'])
1329
 
        self.assertChangedFileIds([tree.path2id(''), b'file-id'], tree)
 
1308
        tree.add(['file'], ['file-id'])
 
1309
        self.assertChangedFileIds([tree.get_root_id(), 'file-id'], tree)
1330
1310
        tree.commit('one')
1331
1311
        self.assertChangedFileIds([], tree)
1332
1312
 
1333
1313
    def test_sha1provider_stat_and_sha1_used(self):
1334
1314
        tree = self.make_branch_and_tree('tree')
1335
1315
        self.build_tree(['tree/file'])
1336
 
        tree.add(['file'], [b'file-id'])
 
1316
        tree.add(['file'], ['file-id'])
1337
1317
        tree.commit('one')
1338
1318
        tree.lock_write()
1339
1319
        self.addCleanup(tree.unlock)
1340
1320
        state = tree._current_dirstate()
1341
1321
        state._sha1_provider = UppercaseSHA1Provider()
1342
 
        self.assertChangedFileIds([b'file-id'], tree)
1343
 
 
1344
 
 
1345
 
class TestPackStat(tests.TestCase):
1346
 
    """Check packed representaton of stat values is robust on all inputs"""
1347
 
 
1348
 
    scenarios = helper_scenarios
1349
 
 
1350
 
    def pack(self, statlike_tuple):
1351
 
        return self.helpers.pack_stat(os.stat_result(statlike_tuple))
1352
 
 
1353
 
    @staticmethod
1354
 
    def unpack_field(packed_string, stat_field):
1355
 
        return _dirstate_helpers_py._unpack_stat(packed_string)[stat_field]
1356
 
 
1357
 
    def test_result(self):
1358
 
        self.assertEqual(b"AAAQAAAAABAAAAARAAAAAgAAAAEAAIHk",
1359
 
                         self.pack((33252, 1, 2, 0, 0, 0, 4096, 15.5, 16.5, 17.5)))
1360
 
 
1361
 
    def test_giant_inode(self):
1362
 
        packed = self.pack((33252, 0xF80000ABC, 0, 0, 0, 0, 0, 0, 0, 0))
1363
 
        self.assertEqual(0x80000ABC, self.unpack_field(packed, "st_ino"))
1364
 
 
1365
 
    def test_giant_size(self):
1366
 
        packed = self.pack((33252, 0, 0, 0, 0, 0, (1 << 33) + 4096, 0, 0, 0))
1367
 
        self.assertEqual(4096, self.unpack_field(packed, "st_size"))
1368
 
 
1369
 
    def test_fractional_mtime(self):
1370
 
        packed = self.pack((33252, 0, 0, 0, 0, 0, 0, 0, 16.9375, 0))
1371
 
        self.assertEqual(16, self.unpack_field(packed, "st_mtime"))
1372
 
 
1373
 
    def test_ancient_mtime(self):
1374
 
        packed = self.pack((33252, 0, 0, 0, 0, 0, 0, 0, -11644473600.0, 0))
1375
 
        self.assertEqual(1240428288, self.unpack_field(packed, "st_mtime"))
1376
 
 
1377
 
    def test_distant_mtime(self):
1378
 
        packed = self.pack((33252, 0, 0, 0, 0, 0, 0, 0, 64060588800.0, 0))
1379
 
        self.assertEqual(3931046656, self.unpack_field(packed, "st_mtime"))
1380
 
 
1381
 
    def test_fractional_ctime(self):
1382
 
        packed = self.pack((33252, 0, 0, 0, 0, 0, 0, 0, 0, 17.5625))
1383
 
        self.assertEqual(17, self.unpack_field(packed, "st_ctime"))
1384
 
 
1385
 
    def test_ancient_ctime(self):
1386
 
        packed = self.pack((33252, 0, 0, 0, 0, 0, 0, 0, 0, -11644473600.0))
1387
 
        self.assertEqual(1240428288, self.unpack_field(packed, "st_ctime"))
1388
 
 
1389
 
    def test_distant_ctime(self):
1390
 
        packed = self.pack((33252, 0, 0, 0, 0, 0, 0, 0, 0, 64060588800.0))
1391
 
        self.assertEqual(3931046656, self.unpack_field(packed, "st_ctime"))
1392
 
 
1393
 
    def test_negative_dev(self):
1394
 
        packed = self.pack((33252, 0, -0xFFFFFCDE, 0, 0, 0, 0, 0, 0, 0))
1395
 
        self.assertEqual(0x322, self.unpack_field(packed, "st_dev"))
 
1322
        self.assertChangedFileIds(['file-id'], tree)
 
1323