/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 breezy/tests/test__dirstate_helpers.py

  • Committer: Jelmer Vernooij
  • Date: 2020-04-05 19:11:34 UTC
  • mto: (7490.7.16 work)
  • mto: This revision was merged to the branch mainline in revision 7501.
  • Revision ID: jelmer@jelmer.uk-20200405191134-0aebh8ikiwygxma5
Populate the .gitignore file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2010 Canonical Ltd
 
1
# Copyright (C) 2007-2011 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 bzrlib import (
24
 
    dirstate,
25
 
    errors,
 
23
from .. import (
26
24
    osutils,
27
25
    tests,
28
26
    )
29
 
from bzrlib.tests import (
 
27
from ..bzr import (
 
28
    dirstate,
 
29
    _dirstate_helpers_py,
 
30
    )
 
31
from . import (
30
32
    test_dirstate,
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
 
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}))
85
69
 
86
70
 
87
71
class TestBisectPathMixin(object):
136
120
                            bisect_split_idx, bisect_path_idx, path)
137
121
                         )
138
122
        if exists:
139
 
            self.assertEqual(path, paths[bisect_path_idx+offset])
 
123
            self.assertEqual(path, paths[bisect_path_idx + offset])
140
124
 
141
125
    def split_for_dirblocks(self, paths):
142
126
        dir_split_paths = []
143
127
        for path in paths:
144
128
            dirname, basename = os.path.split(path)
145
 
            dir_split_paths.append((dirname.split('/'), basename))
 
129
            dir_split_paths.append((dirname.split(b'/'), basename))
146
130
        dir_split_paths.sort()
147
131
        return dir_split_paths
148
132
 
149
133
    def test_simple(self):
150
134
        """In the simple case it works just like bisect_left"""
151
 
        paths = ['', 'a', 'b', 'c', 'd']
 
135
        paths = [b'', b'a', b'b', b'c', b'd']
152
136
        split_paths = self.split_for_dirblocks(paths)
153
137
        for path in paths:
154
138
            self.assertBisect(paths, split_paths, path, exists=True)
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)
 
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)
164
148
 
165
149
    def test_involved(self):
166
150
        """This is where bisect_path_* diverges slightly."""
197
181
        # children are mentioned.
198
182
        # So all the root-directory paths, then all the
199
183
        # first sub directory, etc.
200
 
        paths = [# content of '/'
201
 
                 '', 'a', 'a-a', 'a-z', 'a=a', 'a=z',
 
184
        paths = [  # content of '/'
 
185
            b'', b'a', b'a-a', b'a-z', b'a=a', b'a=z',
202
186
                 # content of 'a/'
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',
 
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',
207
191
                 # content of 'a/a/'
208
 
                 'a/a/a', 'a/a/z',
 
192
                 b'a/a/a', b'a/a/z',
209
193
                 # content of 'a/a-a'
210
 
                 'a/a-a/a',
 
194
                 b'a/a-a/a',
211
195
                 # content of 'a/a-z'
212
 
                 'a/a-z/z',
 
196
                 b'a/a-z/z',
213
197
                 # content of 'a/a=a'
214
 
                 'a/a=a/a',
 
198
                 b'a/a=a/a',
215
199
                 # content of 'a/a=z'
216
 
                 'a/a=z/z',
 
200
                 b'a/a=z/z',
217
201
                 # content of 'a/z/'
218
 
                 'a/z/a', 'a/z/z',
 
202
                 b'a/z/a', b'a/z/z',
219
203
                 # content of 'a-a'
220
 
                 'a-a/a',
 
204
                 b'a-a/a',
221
205
                 # content of 'a-z'
222
 
                 'a-z/z',
 
206
                 b'a-z/z',
223
207
                 # content of 'a=a'
224
 
                 'a=a/a',
 
208
                 b'a=a/a',
225
209
                 # content of 'a=z'
226
 
                 'a=z/z',
227
 
                ]
 
210
                 b'a=z/z',
 
211
            ]
228
212
        split_paths = self.split_for_dirblocks(paths)
229
213
        sorted_paths = []
230
214
        for dir_parts, basename in split_paths:
231
 
            if dir_parts == ['']:
 
215
            if dir_parts == [b'']:
232
216
                sorted_paths.append(basename)
233
217
            else:
234
 
                sorted_paths.append('/'.join(dir_parts + [basename]))
 
218
                sorted_paths.append(b'/'.join(dir_parts + [basename]))
235
219
 
236
220
        self.assertEqual(sorted_paths, paths)
237
221
 
243
227
    """Run all Bisect Path tests against _bisect_path_left."""
244
228
 
245
229
    def get_bisect_path(self):
246
 
        from bzrlib._dirstate_helpers_py import _bisect_path_left
 
230
        from breezy.bzr._dirstate_helpers_py import _bisect_path_left
247
231
        return _bisect_path_left
248
232
 
249
233
    def get_bisect(self):
256
240
    _test_needs_features = [compiled_dirstate_helpers_feature]
257
241
 
258
242
    def get_bisect_path(self):
259
 
        from bzrlib._dirstate_helpers_pyx import _bisect_path_left
 
243
        from breezy.bzr._dirstate_helpers_pyx import _bisect_path_left
260
244
        return _bisect_path_left
261
245
 
262
246
 
264
248
    """Run all Bisect Path tests against _bisect_path_right"""
265
249
 
266
250
    def get_bisect_path(self):
267
 
        from bzrlib._dirstate_helpers_py import _bisect_path_right
 
251
        from breezy.bzr._dirstate_helpers_py import _bisect_path_right
268
252
        return _bisect_path_right
269
253
 
270
254
    def get_bisect(self):
277
261
    _test_needs_features = [compiled_dirstate_helpers_feature]
278
262
 
279
263
    def get_bisect_path(self):
280
 
        from bzrlib._dirstate_helpers_pyx import _bisect_path_right
 
264
        from breezy.bzr._dirstate_helpers_pyx import _bisect_path_right
281
265
        return _bisect_path_right
282
266
 
283
267
 
295
279
 
296
280
    def get_bisect_dirblock(self):
297
281
        """Return an implementation of bisect_dirblock"""
298
 
        from bzrlib._dirstate_helpers_py import bisect_dirblock
 
282
        from breezy.bzr._dirstate_helpers_py import bisect_dirblock
299
283
        return bisect_dirblock
300
284
 
301
285
    def assertBisect(self, dirblocks, split_dirblocks, path, *args, **kwargs):
310
294
        bisect_dirblock = self.get_bisect_dirblock()
311
295
        self.assertIsInstance(dirblocks, list)
312
296
        bisect_split_idx = bisect_dirblock(dirblocks, path, *args, **kwargs)
313
 
        split_dirblock = (path.split('/'), [])
 
297
        split_dirblock = (path.split(b'/'), [])
314
298
        bisect_left_idx = bisect.bisect_left(split_dirblocks, split_dirblock,
315
299
                                             *args)
316
300
        self.assertEqual(bisect_left_idx, bisect_split_idx,
325
309
        Also, ensure that the paths are in proper sorted order.
326
310
        """
327
311
        dirblocks = [(path, []) for path in paths]
328
 
        split_dirblocks = [(path.split('/'), []) for path in paths]
 
312
        split_dirblocks = [(path.split(b'/'), []) for path in paths]
329
313
        self.assertEqual(sorted(split_dirblocks), split_dirblocks)
330
314
        return dirblocks, split_dirblocks
331
315
 
332
316
    def test_simple(self):
333
317
        """In the simple case it works just like bisect_left"""
334
 
        paths = ['', 'a', 'b', 'c', 'd']
 
318
        paths = [b'', b'a', b'b', b'c', b'd']
335
319
        dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
336
320
        for path in paths:
337
321
            self.assertBisect(dirblocks, split_dirblocks, path)
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')
 
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')
347
331
 
348
332
    def test_involved(self):
349
333
        """This is where bisect_left diverges slightly."""
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
 
                ]
 
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
                 ]
358
342
        dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
359
343
        for path in paths:
360
344
            self.assertBisect(dirblocks, split_dirblocks, path)
361
345
 
362
346
    def test_involved_cached(self):
363
347
        """This is where bisect_left diverges slightly."""
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
 
                ]
 
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
                 ]
372
356
        cache = {}
373
357
        dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
374
358
        for path in paths:
389
373
    _test_needs_features = [compiled_dirstate_helpers_feature]
390
374
 
391
375
    def get_bisect_dirblock(self):
392
 
        from bzrlib._dirstate_helpers_pyx import bisect_dirblock
 
376
        from breezy.bzr._dirstate_helpers_pyx import bisect_dirblock
393
377
        return bisect_dirblock
394
378
 
395
379
 
396
 
class TestCmpByDirs(tests.TestCase):
397
 
    """Test an implementation of cmp_by_dirs()
 
380
class TestLtByDirs(tests.TestCase):
 
381
    """Test an implementation of lt_by_dirs()
398
382
 
399
 
    cmp_by_dirs() compares 2 paths by their directory sections, rather than as
 
383
    lt_by_dirs() compares 2 paths by their directory sections, rather than as
400
384
    plain strings.
401
385
 
402
 
    Child test cases can override ``get_cmp_by_dirs`` to test a specific
 
386
    Child test cases can override ``get_lt_by_dirs`` to test a specific
403
387
    implementation.
404
388
    """
405
389
 
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
 
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
410
394
 
411
395
    def assertCmpByDirs(self, expected, str1, str2):
412
396
        """Compare the two strings, in both directions.
416
400
        :param str1: string to compare
417
401
        :param str2: string to compare
418
402
        """
419
 
        cmp_by_dirs = self.get_cmp_by_dirs()
 
403
        lt_by_dirs = self.get_lt_by_dirs()
420
404
        if expected == 0:
421
405
            self.assertEqual(str1, str2)
422
 
            self.assertEqual(0, cmp_by_dirs(str1, str2))
423
 
            self.assertEqual(0, cmp_by_dirs(str2, str1))
 
406
            self.assertFalse(lt_by_dirs(str1, str2))
 
407
            self.assertFalse(lt_by_dirs(str2, str1))
424
408
        elif expected > 0:
425
 
            self.assertPositive(cmp_by_dirs(str1, str2))
426
 
            self.assertNegative(cmp_by_dirs(str2, str1))
 
409
            self.assertFalse(lt_by_dirs(str1, str2))
 
410
            self.assertTrue(lt_by_dirs(str2, str1))
427
411
        else:
428
 
            self.assertNegative(cmp_by_dirs(str1, str2))
429
 
            self.assertPositive(cmp_by_dirs(str2, str1))
 
412
            self.assertTrue(lt_by_dirs(str1, str2))
 
413
            self.assertFalse(lt_by_dirs(str2, str1))
430
414
 
431
415
    def test_cmp_empty(self):
432
416
        """Compare against the empty string."""
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/', '')
 
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'')
444
428
 
445
429
    def test_cmp_same_str(self):
446
430
        """Compare the same string"""
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')
 
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')
462
447
 
463
448
    def test_simple_paths(self):
464
449
        """Compare strings that act like normal string comparison"""
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')
 
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')
486
471
 
487
472
    def test_tricky_paths(self):
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')
 
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')
493
478
 
494
479
    def test_cmp_unicode_not_allowed(self):
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')
 
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')
499
484
 
500
485
    def test_cmp_non_ascii(self):
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"""
 
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"""
510
495
 
511
496
    _test_needs_features = [compiled_dirstate_helpers_feature]
512
497
 
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
 
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
522
507
    DirState. All paths in the same directory are sorted together.
523
508
 
524
 
    Child test cases can override ``get_cmp_path_by_dirblock`` to test a specific
 
509
    Child test cases can override ``get_lt_path_by_dirblock`` to test a specific
525
510
    implementation.
526
511
    """
527
512
 
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
 
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
532
517
 
533
 
    def assertCmpPathByDirblock(self, paths):
 
518
    def assertLtPathByDirblock(self, paths):
534
519
        """Compare all paths and make sure they evaluate to the correct order.
535
520
 
536
521
        This does N^2 comparisons. It is assumed that ``paths`` is properly
541
526
        # First, make sure the paths being passed in are correct
542
527
        def _key(p):
543
528
            dirname, basename = os.path.split(p)
544
 
            return dirname.split('/'), basename
 
529
            return dirname.split(b'/'), basename
545
530
        self.assertEqual(sorted(paths, key=_key), paths)
546
531
 
547
 
        cmp_path_by_dirblock = self.get_cmp_path_by_dirblock()
 
532
        lt_path_by_dirblock = self.get_lt_path_by_dirblock()
548
533
        for idx1, path1 in enumerate(paths):
549
534
            for idx2, path2 in enumerate(paths):
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))
 
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))
566
540
 
567
541
    def test_cmp_simple_paths(self):
568
542
        """Compare against the empty string."""
569
 
        self.assertCmpPathByDirblock(['', 'a', 'ab', 'abc', 'a/b/c', 'b/d/e'])
570
 
        self.assertCmpPathByDirblock(['kl', 'ab/cd', 'ab/ef', 'gh/ij'])
 
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'])
571
546
 
572
547
    def test_tricky_paths(self):
573
 
        self.assertCmpPathByDirblock([
 
548
        self.assertLtPathByDirblock([
574
549
            # Contents of ''
575
 
            '', 'a', 'a-a', 'a=a', 'b',
 
550
            b'', b'a', b'a-a', b'a=a', b'b',
576
551
            # Contents of 'a'
577
 
            'a/a', 'a/a-a', 'a/a=a', 'a/b',
 
552
            b'a/a', b'a/a-a', b'a/a=a', b'a/b',
578
553
            # Contents of 'a/a'
579
 
            'a/a/a', 'a/a/a-a', 'a/a/a=a',
 
554
            b'a/a/a', b'a/a/a-a', b'a/a/a=a',
580
555
            # Contents of 'a/a/a'
581
 
            'a/a/a/a', 'a/a/a/b',
 
556
            b'a/a/a/a', b'a/a/a/b',
582
557
            # Contents of 'a/a/a-a',
583
 
            'a/a/a-a/a', 'a/a/a-a/b',
 
558
            b'a/a/a-a/a', b'a/a/a-a/b',
584
559
            # Contents of 'a/a/a=a',
585
 
            'a/a/a=a/a', 'a/a/a=a/b',
 
560
            b'a/a/a=a/a', b'a/a/a=a/b',
586
561
            # Contents of 'a/a-a'
587
 
            'a/a-a/a',
 
562
            b'a/a-a/a',
588
563
            # Contents of 'a/a-a/a'
589
 
            'a/a-a/a/a', 'a/a-a/a/b',
 
564
            b'a/a-a/a/a', b'a/a-a/a/b',
590
565
            # Contents of 'a/a=a'
591
 
            'a/a=a/a',
 
566
            b'a/a=a/a',
592
567
            # Contents of 'a/b'
593
 
            'a/b/a', 'a/b/b',
 
568
            b'a/b/a', b'a/b/b',
594
569
            # Contents of 'a-a',
595
 
            'a-a/a', 'a-a/b',
 
570
            b'a-a/a', b'a-a/b',
596
571
            # Contents of 'a=a',
597
 
            'a=a/a', 'a=a/b',
 
572
            b'a=a/a', b'a=a/b',
598
573
            # Contents of 'b',
599
 
            'b/a', 'b/b',
 
574
            b'b/a', b'b/b',
600
575
            ])
601
 
        self.assertCmpPathByDirblock([
602
 
                 # content of '/'
603
 
                 '', 'a', 'a-a', 'a-z', 'a=a', 'a=z',
 
576
        self.assertLtPathByDirblock([
 
577
            # content of '/'
 
578
            b'', b'a', b'a-a', b'a-z', b'a=a', b'a=z',
604
579
                 # content of 'a/'
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',
 
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',
609
584
                 # content of 'a/a/'
610
 
                 'a/a/a', 'a/a/z',
 
585
                 b'a/a/a', b'a/a/z',
611
586
                 # content of 'a/a-a'
612
 
                 'a/a-a/a',
 
587
                 b'a/a-a/a',
613
588
                 # content of 'a/a-z'
614
 
                 'a/a-z/z',
 
589
                 b'a/a-z/z',
615
590
                 # content of 'a/a=a'
616
 
                 'a/a=a/a',
 
591
                 b'a/a=a/a',
617
592
                 # content of 'a/a=z'
618
 
                 'a/a=z/z',
 
593
                 b'a/a=z/z',
619
594
                 # content of 'a/z/'
620
 
                 'a/z/a', 'a/z/z',
 
595
                 b'a/z/a', b'a/z/z',
621
596
                 # content of 'a-a'
622
 
                 'a-a/a',
 
597
                 b'a-a/a',
623
598
                 # content of 'a-z'
624
 
                 'a-z/z',
 
599
                 b'a-z/z',
625
600
                 # content of 'a=a'
626
 
                 'a=a/a',
 
601
                 b'a=a/a',
627
602
                 # content of 'a=z'
628
 
                 'a=z/z',
629
 
                ])
 
603
                 b'a=z/z',
 
604
            ])
630
605
 
631
606
    def test_unicode_not_allowed(self):
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')
 
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')
639
614
 
640
615
    def test_nonascii(self):
641
 
        self.assertCmpPathByDirblock([
 
616
        self.assertLtPathByDirblock([
642
617
            # content of '/'
643
 
            '', 'a', '\xc2\xb5', '\xc3\xa5',
 
618
            b'', b'a', b'\xc2\xb5', b'\xc3\xa5',
644
619
            # content of 'a'
645
 
            'a/a', 'a/\xc2\xb5', 'a/\xc3\xa5',
 
620
            b'a/a', b'a/\xc2\xb5', b'a/\xc3\xa5',
646
621
            # content of 'a/a'
647
 
            'a/a/a', 'a/a/\xc2\xb5', 'a/a/\xc3\xa5',
 
622
            b'a/a/a', b'a/a/\xc2\xb5', b'a/a/\xc3\xa5',
648
623
            # content of 'a/\xc2\xb5'
649
 
            'a/\xc2\xb5/a', 'a/\xc2\xb5/\xc2\xb5', 'a/\xc2\xb5/\xc3\xa5',
 
624
            b'a/\xc2\xb5/a', b'a/\xc2\xb5/\xc2\xb5', b'a/\xc2\xb5/\xc3\xa5',
650
625
            # content of 'a/\xc3\xa5'
651
 
            'a/\xc3\xa5/a', 'a/\xc3\xa5/\xc2\xb5', 'a/\xc3\xa5/\xc3\xa5',
 
626
            b'a/\xc3\xa5/a', b'a/\xc3\xa5/\xc2\xb5', b'a/\xc3\xa5/\xc3\xa5',
652
627
            # content of '\xc2\xb5'
653
 
            '\xc2\xb5/a', '\xc2\xb5/\xc2\xb5', '\xc2\xb5/\xc3\xa5',
 
628
            b'\xc2\xb5/a', b'\xc2\xb5/\xc2\xb5', b'\xc2\xb5/\xc3\xa5',
654
629
            # content of '\xc2\xe5'
655
 
            '\xc3\xa5/a', '\xc3\xa5/\xc2\xb5', '\xc3\xa5/\xc3\xa5',
 
630
            b'\xc3\xa5/a', b'\xc3\xa5/\xc2\xb5', b'\xc3\xa5/\xc3\xa5',
656
631
            ])
657
632
 
658
633
 
659
 
class TestCompiledCmpPathByDirblock(TestCmpPathByDirblock):
660
 
    """Test the pyrex implementation of _cmp_path_by_dirblock"""
 
634
class TestCompiledLtPathByDirblock(TestLtPathByDirblock):
 
635
    """Test the pyrex implementation of _lt_path_by_dirblock"""
661
636
 
662
637
    _test_needs_features = [compiled_dirstate_helpers_feature]
663
638
 
664
 
    def get_cmp_by_dirs(self):
665
 
        from bzrlib._dirstate_helpers_pyx import _cmp_path_by_dirblock
666
 
        return _cmp_path_by_dirblock
 
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
667
642
 
668
643
 
669
644
class TestMemRChr(tests.TestCase):
672
647
    _test_needs_features = [compiled_dirstate_helpers_feature]
673
648
 
674
649
    def assertMemRChr(self, expected, s, c):
675
 
        from bzrlib._dirstate_helpers_pyx import _py_memrchr
 
650
        from breezy.bzr._dirstate_helpers_pyx import _py_memrchr
676
651
        self.assertEqual(expected, _py_memrchr(s, c))
677
652
 
678
653
    def test_missing(self):
679
 
        self.assertMemRChr(None, '', 'a')
680
 
        self.assertMemRChr(None, '', 'c')
681
 
        self.assertMemRChr(None, 'abcdefghijklm', 'q')
682
 
        self.assertMemRChr(None, 'aaaaaaaaaaaaaaaaaaaaaaa', 'b')
 
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')
683
658
 
684
659
    def test_single_entry(self):
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')
 
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')
691
666
 
692
667
    def test_multiple(self):
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')
 
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')
700
675
 
701
676
    def test_with_nulls(self):
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')
 
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')
710
685
 
711
686
 
712
687
class TestReadDirblocks(test_dirstate.TestCaseWithDirState):
719
694
    implementation.
720
695
    """
721
696
 
 
697
    # inherits scenarios from test_dirstate
 
698
 
722
699
    def get_read_dirblocks(self):
723
 
        from bzrlib._dirstate_helpers_py import _read_dirblocks
 
700
        from breezy.bzr._dirstate_helpers_py import _read_dirblocks
724
701
        return _read_dirblocks
725
702
 
726
703
    def test_smoketest(self):
737
714
 
738
715
    def test_trailing_garbage(self):
739
716
        tree, state, expected = self.create_basic_dirstate()
740
 
        # On Linux, we can write extra data as long as we haven't read yet, but
 
717
        # On Unix, we can write extra data as long as we haven't read yet, but
741
718
        # on Win32, if you've opened the file with FILE_SHARE_READ, trying to
742
719
        # open it in append mode will fail.
743
720
        state.unlock()
744
721
        f = open('dirstate', 'ab')
745
722
        try:
746
723
            # Add bogus trailing garbage
747
 
            f.write('bogus\n')
 
724
            f.write(b'bogus\n')
748
725
        finally:
749
726
            f.close()
750
727
            state.lock_read()
751
 
        e = self.assertRaises(errors.DirstateCorrupt,
 
728
        e = self.assertRaises(dirstate.DirstateCorrupt,
752
729
                              state._read_dirblocks_if_needed)
753
730
        # Make sure we mention the bogus characters in the error
754
731
        self.assertContainsRe(str(e), 'bogus')
760
737
    _test_needs_features = [compiled_dirstate_helpers_feature]
761
738
 
762
739
    def get_read_dirblocks(self):
763
 
        from bzrlib._dirstate_helpers_pyx import _read_dirblocks
 
740
        from breezy.bzr._dirstate_helpers_pyx import _read_dirblocks
764
741
        return _read_dirblocks
765
742
 
766
743
 
774
751
 
775
752
    def test_bisect_dirblock(self):
776
753
        if compiled_dirstate_helpers_feature.available():
777
 
            from bzrlib._dirstate_helpers_pyx import bisect_dirblock
 
754
            from breezy.bzr._dirstate_helpers_pyx import bisect_dirblock
778
755
        else:
779
 
            from bzrlib._dirstate_helpers_py import bisect_dirblock
 
756
            from breezy.bzr._dirstate_helpers_py import bisect_dirblock
780
757
        self.assertIs(bisect_dirblock, dirstate.bisect_dirblock)
781
758
 
782
759
    def test__bisect_path_left(self):
783
760
        if compiled_dirstate_helpers_feature.available():
784
 
            from bzrlib._dirstate_helpers_pyx import _bisect_path_left
 
761
            from breezy.bzr._dirstate_helpers_pyx import _bisect_path_left
785
762
        else:
786
 
            from bzrlib._dirstate_helpers_py import _bisect_path_left
 
763
            from breezy.bzr._dirstate_helpers_py import _bisect_path_left
787
764
        self.assertIs(_bisect_path_left, dirstate._bisect_path_left)
788
765
 
789
766
    def test__bisect_path_right(self):
790
767
        if compiled_dirstate_helpers_feature.available():
791
 
            from bzrlib._dirstate_helpers_pyx import _bisect_path_right
 
768
            from breezy.bzr._dirstate_helpers_pyx import _bisect_path_right
792
769
        else:
793
 
            from bzrlib._dirstate_helpers_py import _bisect_path_right
 
770
            from breezy.bzr._dirstate_helpers_py import _bisect_path_right
794
771
        self.assertIs(_bisect_path_right, dirstate._bisect_path_right)
795
772
 
796
 
    def test_cmp_by_dirs(self):
 
773
    def test_lt_by_dirs(self):
797
774
        if compiled_dirstate_helpers_feature.available():
798
 
            from bzrlib._dirstate_helpers_pyx import cmp_by_dirs
 
775
            from ..bzr._dirstate_helpers_pyx import lt_by_dirs
799
776
        else:
800
 
            from bzrlib._dirstate_helpers_py import cmp_by_dirs
801
 
        self.assertIs(cmp_by_dirs, dirstate.cmp_by_dirs)
 
777
            from ..bzr._dirstate_helpers_py import lt_by_dirs
 
778
        self.assertIs(lt_by_dirs, dirstate.lt_by_dirs)
802
779
 
803
780
    def test__read_dirblocks(self):
804
781
        if compiled_dirstate_helpers_feature.available():
805
 
            from bzrlib._dirstate_helpers_pyx import _read_dirblocks
 
782
            from breezy.bzr._dirstate_helpers_pyx import _read_dirblocks
806
783
        else:
807
 
            from bzrlib._dirstate_helpers_py import _read_dirblocks
 
784
            from breezy.bzr._dirstate_helpers_py import _read_dirblocks
808
785
        self.assertIs(_read_dirblocks, dirstate._read_dirblocks)
809
786
 
810
787
    def test_update_entry(self):
811
788
        if compiled_dirstate_helpers_feature.available():
812
 
            from bzrlib._dirstate_helpers_pyx import update_entry
 
789
            from breezy.bzr._dirstate_helpers_pyx import update_entry
813
790
        else:
814
 
            from bzrlib.dirstate import update_entry
 
791
            from breezy.bzr.dirstate import update_entry
815
792
        self.assertIs(update_entry, dirstate.update_entry)
816
793
 
817
794
    def test_process_entry(self):
818
795
        if compiled_dirstate_helpers_feature.available():
819
 
            from bzrlib._dirstate_helpers_pyx import ProcessEntryC
 
796
            from breezy.bzr._dirstate_helpers_pyx import ProcessEntryC
820
797
            self.assertIs(ProcessEntryC, dirstate._process_entry)
821
798
        else:
822
 
            from bzrlib.dirstate import ProcessEntryPython
 
799
            from breezy.bzr.dirstate import ProcessEntryPython
823
800
            self.assertIs(ProcessEntryPython, dirstate._process_entry)
824
801
 
825
802
 
826
803
class TestUpdateEntry(test_dirstate.TestCaseWithDirState):
827
804
    """Test the DirState.update_entry functions"""
828
805
 
 
806
    scenarios = multiply_scenarios(
 
807
        dir_reader_scenarios(), ue_scenarios)
 
808
 
829
809
    # Set by load_tests
830
810
    update_entry = None
831
811
 
837
817
        """Create a DirState tracking a single object named 'a'"""
838
818
        state = test_dirstate.InstrumentedDirState.initialize('dirstate')
839
819
        self.addCleanup(state.unlock)
840
 
        state.add('a', 'a-id', 'file', None, '')
841
 
        entry = state._get_entry(0, path_utf8='a')
 
820
        state.add('a', b'a-id', 'file', None, b'')
 
821
        entry = state._get_entry(0, path_utf8=b'a')
842
822
        return state, entry
843
823
 
844
824
    def test_observed_sha1_cachable(self):
845
825
        state, entry = self.get_state_with_a()
 
826
        state.save()
846
827
        atime = time.time() - 10
847
828
        self.build_tree(['a'])
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])
 
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])
853
835
        packed_stat = dirstate.pack_stat(statvalue)
854
836
        self.assertEqual(packed_stat, entry[1][0][4])
 
837
        self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
 
838
                         state._dirblock_state)
855
839
 
856
840
    def test_observed_sha1_not_cachable(self):
857
841
        state, entry = self.get_state_with_a()
 
842
        state.save()
858
843
        oldval = entry[1][0][1]
859
844
        oldstat = entry[1][0][4]
860
845
        self.build_tree(['a'])
861
846
        statvalue = os.lstat('a')
 
847
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
 
848
                         state._dirblock_state)
862
849
        state._observed_sha1(entry, "foo", statvalue)
863
850
        self.assertEqual(oldval, entry[1][0][1])
864
851
        self.assertEqual(oldstat, entry[1][0][4])
 
852
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
 
853
                         state._dirblock_state)
865
854
 
866
855
    def test_update_entry(self):
867
856
        state, _ = self.get_state_with_a()
869
858
        tree.lock_write()
870
859
        empty_revid = tree.commit('empty')
871
860
        self.build_tree(['tree/a'])
872
 
        tree.add(['a'], ['a-id'])
 
861
        tree.add(['a'], [b'a-id'])
873
862
        with_a_id = tree.commit('with_a')
874
863
        self.addCleanup(tree.unlock)
875
864
        state.set_parent_trees(
876
865
            [(empty_revid, tree.branch.repository.revision_tree(empty_revid))],
877
866
            [])
878
 
        entry = state._get_entry(0, path_utf8='a')
 
867
        entry = state._get_entry(0, path_utf8=b'a')
879
868
        self.build_tree(['a'])
880
869
        # Add one where we don't provide the stat or sha already
881
 
        self.assertEqual(('', 'a', 'a-id'), entry[0])
882
 
        self.assertEqual(('f', '', 0, False, dirstate.DirState.NULLSTAT),
 
870
        self.assertEqual((b'', b'a', b'a-id'), entry[0])
 
871
        self.assertEqual((b'f', b'', 0, False, dirstate.DirState.NULLSTAT),
883
872
                         entry[1][0])
884
873
        # Flush the buffers to disk
885
874
        state.save()
888
877
 
889
878
        stat_value = os.lstat('a')
890
879
        packed_stat = dirstate.pack_stat(stat_value)
891
 
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
892
 
                                          stat_value=stat_value)
 
880
        link_or_sha1 = self.update_entry(state, entry, abspath=b'a',
 
881
                                         stat_value=stat_value)
893
882
        self.assertEqual(None, link_or_sha1)
894
883
 
895
 
        # The dirblock entry should not have cached the file's sha1 (too new)
896
 
        self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
 
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),
897
888
                         entry[1][0])
898
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
889
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
899
890
                         state._dirblock_state)
900
891
        mode = stat_value.st_mode
901
892
        self.assertEqual([('is_exec', mode, False)], state._log)
904
895
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
905
896
                         state._dirblock_state)
906
897
 
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.
 
898
        # Roll the clock back so the file is guaranteed to look too new. We
 
899
        # should still not compute the sha1.
910
900
        state.adjust_time(-10)
911
901
        del state._log[:]
912
902
 
913
 
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
914
 
                                          stat_value=stat_value)
 
903
        link_or_sha1 = self.update_entry(state, entry, abspath=b'a',
 
904
                                         stat_value=stat_value)
915
905
        self.assertEqual([('is_exec', mode, False)], state._log)
916
906
        self.assertEqual(None, link_or_sha1)
917
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
907
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
918
908
                         state._dirblock_state)
919
 
        self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
 
909
        self.assertEqual((b'f', b'', 14, False, dirstate.DirState.NULLSTAT),
920
910
                         entry[1][0])
921
911
        state.save()
922
912
 
924
914
        # won't calculate the sha or cache it.
925
915
        state.adjust_time(+20)
926
916
        del state._log[:]
927
 
        link_or_sha1 = dirstate.update_entry(state, entry, abspath='a',
928
 
                                          stat_value=stat_value)
 
917
        link_or_sha1 = dirstate.update_entry(state, entry, abspath=b'a',
 
918
                                             stat_value=stat_value)
929
919
        self.assertEqual(None, link_or_sha1)
930
920
        self.assertEqual([('is_exec', mode, False)], state._log)
931
 
        self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
 
921
        self.assertEqual((b'f', b'', 14, False, dirstate.DirState.NULLSTAT),
932
922
                         entry[1][0])
 
923
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
 
924
                         state._dirblock_state)
933
925
 
934
926
        # If the file is no longer new, and the clock has been moved forward
935
927
        # sufficiently, it will cache the sha.
937
929
        state.set_parent_trees(
938
930
            [(with_a_id, tree.branch.repository.revision_tree(with_a_id))],
939
931
            [])
940
 
        entry = state._get_entry(0, path_utf8='a')
 
932
        entry = state._get_entry(0, path_utf8=b'a')
941
933
 
942
 
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
943
 
                                          stat_value=stat_value)
944
 
        self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
 
934
        link_or_sha1 = self.update_entry(state, entry, abspath=b'a',
 
935
                                         stat_value=stat_value)
 
936
        self.assertEqual(b'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
945
937
                         link_or_sha1)
946
 
        self.assertEqual([('is_exec', mode, False), ('sha1', 'a')],
947
 
                          state._log)
948
 
        self.assertEqual(('f', link_or_sha1, 14, False, packed_stat),
 
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),
949
941
                         entry[1][0])
950
942
 
951
943
        # Subsequent calls will just return the cached value
952
944
        del state._log[:]
953
 
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
954
 
                                          stat_value=stat_value)
955
 
        self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
 
945
        link_or_sha1 = self.update_entry(state, entry, abspath=b'a',
 
946
                                         stat_value=stat_value)
 
947
        self.assertEqual(b'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
956
948
                         link_or_sha1)
957
949
        self.assertEqual([], state._log)
958
 
        self.assertEqual(('f', link_or_sha1, 14, False, packed_stat),
 
950
        self.assertEqual((b'f', link_or_sha1, 14, False, packed_stat),
959
951
                         entry[1][0])
960
952
 
961
953
    def test_update_entry_symlink(self):
962
954
        """Update entry should read symlinks."""
963
 
        self.requireFeature(tests.SymlinkFeature)
 
955
        self.requireFeature(features.SymlinkFeature)
964
956
        state, entry = self.get_state_with_a()
965
957
        state.save()
966
958
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
967
959
                         state._dirblock_state)
968
960
        os.symlink('target', 'a')
969
961
 
970
 
        state.adjust_time(-10) # Make the symlink look new
 
962
        state.adjust_time(-10)  # Make the symlink look new
971
963
        stat_value = os.lstat('a')
972
964
        packed_stat = dirstate.pack_stat(stat_value)
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)
 
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)
977
969
        # Dirblock is not updated (the link is too new)
978
 
        self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
 
970
        self.assertEqual([(b'l', b'', 6, False, dirstate.DirState.NULLSTAT)],
979
971
                         entry[1])
980
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
972
        # The file entry turned into a symlink, that is considered
 
973
        # HASH modified worthy.
 
974
        self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
981
975
                         state._dirblock_state)
982
976
 
983
977
        # Because the stat_value looks new, we should re-read the target
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)],
 
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)],
991
984
                         entry[1])
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)
 
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)
996
996
        # We need to re-read the link because only now can we cache it
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)],
 
997
        self.assertEqual([('read_link', b'a', b'')], state._log)
 
998
        self.assertEqual([(b'l', b'target', 6, False, packed_stat)],
1002
999
                         entry[1])
1003
1000
 
 
1001
        del state._log[:]
1004
1002
        # Another call won't re-read the link
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)],
 
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)],
1013
1008
                         entry[1])
1014
1009
 
1015
1010
    def do_update_entry(self, state, entry, abspath):
1019
1014
    def test_update_entry_dir(self):
1020
1015
        state, entry = self.get_state_with_a()
1021
1016
        self.build_tree(['a/'])
1022
 
        self.assertIs(None, self.do_update_entry(state, entry, 'a'))
 
1017
        self.assertIs(None, self.do_update_entry(state, entry, b'a'))
1023
1018
 
1024
1019
    def test_update_entry_dir_unchanged(self):
1025
1020
        state, entry = self.get_state_with_a()
1026
1021
        self.build_tree(['a/'])
1027
1022
        state.adjust_time(+20)
1028
 
        self.assertIs(None, self.do_update_entry(state, entry, 'a'))
 
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
1029
1025
        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1030
1026
                         state._dirblock_state)
1031
1027
        state.save()
1032
1028
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1033
1029
                         state._dirblock_state)
1034
 
        self.assertIs(None, self.do_update_entry(state, entry, 'a'))
 
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])
1035
1047
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1036
1048
                         state._dirblock_state)
1037
1049
 
1040
1052
        tree = self.make_branch_and_tree('tree')
1041
1053
        tree.lock_write()
1042
1054
        self.build_tree(['tree/a'])
1043
 
        tree.add(['a'], ['a-id'])
 
1055
        tree.add(['a'], [b'a-id'])
1044
1056
        with_a_id = tree.commit('witha')
1045
1057
        self.addCleanup(tree.unlock)
1046
1058
        state.set_parent_trees(
1047
1059
            [(with_a_id, tree.branch.repository.revision_tree(with_a_id))],
1048
1060
            [])
1049
 
        entry = state._get_entry(0, path_utf8='a')
 
1061
        entry = state._get_entry(0, path_utf8=b'a')
1050
1062
        self.build_tree(['a'])
1051
 
        sha1sum = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
 
1063
        sha1sum = b'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1052
1064
        state.adjust_time(+20)
1053
 
        self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
 
1065
        self.assertEqual(sha1sum, self.do_update_entry(state, entry, b'a'))
1054
1066
        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1055
1067
                         state._dirblock_state)
1056
1068
        state.save()
1057
1069
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1058
1070
                         state._dirblock_state)
1059
 
        self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
 
1071
        self.assertEqual(sha1sum, self.do_update_entry(state, entry, b'a'))
1060
1072
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1061
1073
                         state._dirblock_state)
1062
1074
 
1063
1075
    def test_update_entry_tree_reference(self):
1064
1076
        state = test_dirstate.InstrumentedDirState.initialize('dirstate')
1065
1077
        self.addCleanup(state.unlock)
1066
 
        state.add('r', 'r-id', 'tree-reference', None, '')
 
1078
        state.add('r', b'r-id', 'tree-reference', None, b'')
1067
1079
        self.build_tree(['r/'])
1068
 
        entry = state._get_entry(0, path_utf8='r')
 
1080
        entry = state._get_entry(0, path_utf8=b'r')
1069
1081
        self.do_update_entry(state, entry, 'r')
1070
 
        entry = state._get_entry(0, path_utf8='r')
1071
 
        self.assertEqual('t', entry[1][0][0])
 
1082
        entry = state._get_entry(0, path_utf8=b'r')
 
1083
        self.assertEqual(b't', entry[1][0][0])
1072
1084
 
1073
1085
    def create_and_test_file(self, state, entry):
1074
1086
        """Create a file at 'a' and verify the state finds it during update.
1082
1094
 
1083
1095
        link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1084
1096
        self.assertEqual(None, link_or_sha1)
1085
 
        self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
 
1097
        self.assertEqual([(b'f', b'', 14, False, dirstate.DirState.NULLSTAT)],
1086
1098
                         entry[1])
1087
1099
        return packed_stat
1088
1100
 
1096
1108
        stat_value = os.lstat('a')
1097
1109
        packed_stat = dirstate.pack_stat(stat_value)
1098
1110
 
1099
 
        link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
 
1111
        link_or_sha1 = self.do_update_entry(state, entry, abspath=b'a')
1100
1112
        self.assertIs(None, link_or_sha1)
1101
 
        self.assertEqual([('d', '', 0, False, packed_stat)], entry[1])
 
1113
        self.assertEqual([(b'd', b'', 0, False, packed_stat)], entry[1])
1102
1114
 
1103
1115
        return packed_stat
1104
1116
 
1118
1130
        stat_value = os.lstat('a')
1119
1131
        packed_stat = dirstate.pack_stat(stat_value)
1120
1132
 
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)],
 
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)],
1124
1136
                         entry[1])
1125
1137
        return packed_stat
1126
1138
 
1137
1149
 
1138
1150
    def test_update_file_to_symlink(self):
1139
1151
        """File becomes a symlink"""
1140
 
        self.requireFeature(tests.SymlinkFeature)
 
1152
        self.requireFeature(features.SymlinkFeature)
1141
1153
        state, entry = self.get_state_with_a()
1142
1154
        # The file sha1 won't be cached unless the file is old
1143
1155
        state.adjust_time(+10)
1156
1168
 
1157
1169
    def test_update_dir_to_symlink(self):
1158
1170
        """Directory becomes a symlink"""
1159
 
        self.requireFeature(tests.SymlinkFeature)
 
1171
        self.requireFeature(features.SymlinkFeature)
1160
1172
        state, entry = self.get_state_with_a()
1161
1173
        # The symlink target won't be cached if it isn't old
1162
1174
        state.adjust_time(+10)
1166
1178
 
1167
1179
    def test_update_symlink_to_file(self):
1168
1180
        """Symlink becomes a file"""
1169
 
        self.requireFeature(tests.SymlinkFeature)
 
1181
        self.requireFeature(features.SymlinkFeature)
1170
1182
        state, entry = self.get_state_with_a()
1171
1183
        # The symlink and file info won't be cached unless old
1172
1184
        state.adjust_time(+10)
1176
1188
 
1177
1189
    def test_update_symlink_to_dir(self):
1178
1190
        """Symlink becomes a directory"""
1179
 
        self.requireFeature(tests.SymlinkFeature)
 
1191
        self.requireFeature(features.SymlinkFeature)
1180
1192
        state, entry = self.get_state_with_a()
1181
1193
        # The symlink target won't be cached if it isn't old
1182
1194
        state.adjust_time(+10)
1188
1200
        state, entry = self.get_state_with_a()
1189
1201
        self.build_tree(['a'])
1190
1202
 
1191
 
        # Make sure we are using the win32 implementation of _is_executable
1192
 
        state._is_executable = state._is_executable_win32
 
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
1193
1206
 
1194
1207
        # The file on disk is not executable, but we are marking it as though
1195
 
        # it is. With _is_executable_win32 we ignore what is on disk.
1196
 
        entry[1][0] = ('f', '', 0, True, dirstate.DirState.NULLSTAT)
 
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)
1197
1211
 
1198
1212
        stat_value = os.lstat('a')
1199
1213
        packed_stat = dirstate.pack_stat(stat_value)
1200
1214
 
1201
 
        state.adjust_time(-10) # Make sure everything is new
1202
 
        self.update_entry(state, entry, abspath='a', stat_value=stat_value)
 
1215
        state.adjust_time(-10)  # Make sure everything is new
 
1216
        self.update_entry(state, entry, abspath=b'a', stat_value=stat_value)
1203
1217
 
1204
1218
        # The row is updated, but the executable bit stays set.
1205
 
        self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
 
1219
        self.assertEqual([(b'f', b'', 14, True, dirstate.DirState.NULLSTAT)],
1206
1220
                         entry[1])
1207
1221
 
1208
1222
        # Make the disk object look old enough to cache (but it won't cache the
1209
1223
        # sha as it is a new file).
1210
1224
        state.adjust_time(+20)
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])
 
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])
1215
1229
 
1216
1230
    def _prepare_tree(self):
1217
1231
        # Create a tree
1218
 
        text = 'Hello World\n'
 
1232
        text = b'Hello World\n'
1219
1233
        tree = self.make_branch_and_tree('tree')
1220
1234
        self.build_tree_contents([('tree/a file', text)])
1221
 
        tree.add('a file', 'a-file-id')
 
1235
        tree.add('a file', b'a-file-id')
1222
1236
        # Note: dirstate does not sha prior to the first commit
1223
1237
        # so commit now in order for the test to work
1224
1238
        tree.commit('first')
1227
1241
    def test_sha1provider_sha1_used(self):
1228
1242
        tree, text = self._prepare_tree()
1229
1243
        state = dirstate.DirState.from_tree(tree, 'dirstate',
1230
 
            UppercaseSHA1Provider())
 
1244
                                            UppercaseSHA1Provider())
1231
1245
        self.addCleanup(state.unlock)
1232
 
        expected_sha = osutils.sha_string(text.upper() + "foo")
1233
 
        entry = state._get_entry(0, path_utf8='a file')
 
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)
1234
1249
        state._sha_cutoff_time()
1235
1250
        state._cutoff_time += 10
1236
1251
        sha1 = self.update_entry(state, entry, 'tree/a file',
1245
1260
        state._sha1_provider = UppercaseSHA1Provider()
1246
1261
        # If we used the standard provider, it would look like nothing has
1247
1262
        # changed
1248
 
        file_ids_changed = [change[0] for change
 
1263
        file_ids_changed = [change.file_id for change
1249
1264
                            in tree.iter_changes(tree.basis_tree())]
1250
 
        self.assertEqual(['a-file-id'], file_ids_changed)
 
1265
        self.assertEqual([b'a-file-id'], file_ids_changed)
1251
1266
 
1252
1267
 
1253
1268
class UppercaseSHA1Provider(dirstate.SHA1Provider):
1257
1272
        return self.stat_and_sha1(abspath)[1]
1258
1273
 
1259
1274
    def stat_and_sha1(self, abspath):
1260
 
        file_obj = file(abspath, 'rb')
1261
 
        try:
 
1275
        with open(abspath, 'rb') as file_obj:
1262
1276
            statvalue = os.fstat(file_obj.fileno())
1263
 
            text = ''.join(file_obj.readlines())
1264
 
            sha1 = osutils.sha_string(text.upper() + "foo")
1265
 
        finally:
1266
 
            file_obj.close()
 
1277
            text = b''.join(file_obj.readlines())
 
1278
            sha1 = osutils.sha_string(text.upper() + b"foo")
1267
1279
        return statvalue, sha1
1268
1280
 
1269
1281
 
1270
1282
class TestProcessEntry(test_dirstate.TestCaseWithDirState):
1271
1283
 
 
1284
    scenarios = multiply_scenarios(dir_reader_scenarios(), pe_scenarios)
 
1285
 
1272
1286
    # Set by load_tests
1273
1287
    _process_entry = None
1274
1288
 
1277
1291
        self.overrideAttr(dirstate, '_process_entry', self._process_entry)
1278
1292
 
1279
1293
    def assertChangedFileIds(self, expected, tree):
1280
 
        tree.lock_read()
1281
 
        try:
1282
 
            file_ids = [info[0] for info
 
1294
        with tree.lock_read():
 
1295
            file_ids = [info.file_id for info
1283
1296
                        in tree.iter_changes(tree.basis_tree())]
1284
 
        finally:
1285
 
            tree.unlock()
1286
1297
        self.assertEqual(sorted(expected), sorted(file_ids))
1287
1298
 
1288
1299
    def test_exceptions_raised(self):
1297
1308
        tree.lock_read()
1298
1309
        self.addCleanup(tree.unlock)
1299
1310
        basis_tree = tree.basis_tree()
 
1311
 
1300
1312
        def is_inside_raises(*args, **kwargs):
1301
1313
            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)
1302
1322
        self.overrideAttr(osutils, 'is_inside', is_inside_raises)
1303
1323
        self.assertListRaises(RuntimeError, tree.iter_changes, basis_tree)
1304
1324
 
1305
1325
    def test_simple_changes(self):
1306
1326
        tree = self.make_branch_and_tree('tree')
1307
1327
        self.build_tree(['tree/file'])
1308
 
        tree.add(['file'], ['file-id'])
1309
 
        self.assertChangedFileIds([tree.get_root_id(), 'file-id'], tree)
 
1328
        tree.add(['file'], [b'file-id'])
 
1329
        self.assertChangedFileIds([tree.path2id(''), b'file-id'], tree)
1310
1330
        tree.commit('one')
1311
1331
        self.assertChangedFileIds([], tree)
1312
1332
 
1313
1333
    def test_sha1provider_stat_and_sha1_used(self):
1314
1334
        tree = self.make_branch_and_tree('tree')
1315
1335
        self.build_tree(['tree/file'])
1316
 
        tree.add(['file'], ['file-id'])
 
1336
        tree.add(['file'], [b'file-id'])
1317
1337
        tree.commit('one')
1318
1338
        tree.lock_write()
1319
1339
        self.addCleanup(tree.unlock)
1320
1340
        state = tree._current_dirstate()
1321
1341
        state._sha1_provider = UppercaseSHA1Provider()
1322
 
        self.assertChangedFileIds(['file-id'], tree)
1323
 
 
 
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"))