~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test__dirstate_helpers.py

  • Committer: Martin Pool
  • Date: 2005-07-23 13:59:30 UTC
  • Revision ID: mbp@sourcefrog.net-20050723135930-d81530c82c925cb0
- less dodgy is_inside function

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007 Canonical Ltd
2
 
#
3
 
# This program is free software; you can redistribute it and/or modify
4
 
# it under the terms of the GNU General Public License as published by
5
 
# the Free Software Foundation; either version 2 of the License, or
6
 
# (at your option) any later version.
7
 
#
8
 
# This program is distributed in the hope that it will be useful,
9
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 
# GNU General Public License for more details.
12
 
#
13
 
# You should have received a copy of the GNU General Public License
14
 
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
 
 
17
 
"""Tests for the compiled dirstate helpers."""
18
 
 
19
 
import bisect
20
 
import os
21
 
 
22
 
from bzrlib import (
23
 
    dirstate,
24
 
    tests,
25
 
    )
26
 
from bzrlib.tests import test_dirstate
27
 
 
28
 
 
29
 
class _CompiledDirstateHelpersFeature(tests.Feature):
30
 
    def _probe(self):
31
 
        try:
32
 
            import bzrlib._dirstate_helpers_c
33
 
        except ImportError:
34
 
            return False
35
 
        return True
36
 
 
37
 
    def feature_name(self):
38
 
        return 'bzrlib._dirstate_helpers_c'
39
 
 
40
 
CompiledDirstateHelpersFeature = _CompiledDirstateHelpersFeature()
41
 
 
42
 
 
43
 
class TestBisectPathMixin(object):
44
 
    """Test that _bisect_path_*() returns the expected values.
45
 
 
46
 
    _bisect_path_* is intended to work like bisect.bisect_*() except it
47
 
    knows it is working on paths that are sorted by ('path', 'to', 'foo')
48
 
    chunks rather than by raw 'path/to/foo'.
49
 
 
50
 
    Test Cases should inherit from this and override ``get_bisect_path`` return
51
 
    their implementation, and ``get_bisect`` to return the matching
52
 
    bisect.bisect_* function.
53
 
    """
54
 
 
55
 
    def get_bisect_path(self):
56
 
        """Return an implementation of _bisect_path_*"""
57
 
        raise NotImplementedError
58
 
 
59
 
    def get_bisect(self):
60
 
        """Return a version of bisect.bisect_*.
61
 
 
62
 
        Also, for the 'exists' check, return the offset to the real values.
63
 
        For example bisect_left returns the index of an entry, while
64
 
        bisect_right returns the index *after* an entry
65
 
 
66
 
        :return: (bisect_func, offset)
67
 
        """
68
 
        raise NotImplementedError
69
 
 
70
 
    def assertBisect(self, paths, split_paths, path, exists=True):
71
 
        """Assert that bisect_split works like bisect_left on the split paths.
72
 
 
73
 
        :param paths: A list of path names
74
 
        :param split_paths: A list of path names that are already split up by directory
75
 
            ('path/to/foo' => ('path', 'to', 'foo'))
76
 
        :param path: The path we are indexing.
77
 
        :param exists: The path should be present, so make sure the
78
 
            final location actually points to the right value.
79
 
 
80
 
        All other arguments will be passed along.
81
 
        """
82
 
        bisect_path = self.get_bisect_path()
83
 
        self.assertIsInstance(paths, list)
84
 
        bisect_path_idx = bisect_path(paths, path)
85
 
        split_path = self.split_for_dirblocks([path])[0]
86
 
        bisect_func, offset = self.get_bisect()
87
 
        bisect_split_idx = bisect_func(split_paths, split_path)
88
 
        self.assertEqual(bisect_split_idx, bisect_path_idx,
89
 
                         '%s disagreed. %s != %s'
90
 
                         ' for key %r'
91
 
                         % (bisect_path.__name__,
92
 
                            bisect_split_idx, bisect_path_idx, path)
93
 
                         )
94
 
        if exists:
95
 
            self.assertEqual(path, paths[bisect_path_idx+offset])
96
 
 
97
 
    def split_for_dirblocks(self, paths):
98
 
        dir_split_paths = []
99
 
        for path in paths:
100
 
            dirname, basename = os.path.split(path)
101
 
            dir_split_paths.append((dirname.split('/'), basename))
102
 
        dir_split_paths.sort()
103
 
        return dir_split_paths
104
 
 
105
 
    def test_simple(self):
106
 
        """In the simple case it works just like bisect_left"""
107
 
        paths = ['', 'a', 'b', 'c', 'd']
108
 
        split_paths = self.split_for_dirblocks(paths)
109
 
        for path in paths:
110
 
            self.assertBisect(paths, split_paths, path, exists=True)
111
 
        self.assertBisect(paths, split_paths, '_', exists=False)
112
 
        self.assertBisect(paths, split_paths, 'aa', exists=False)
113
 
        self.assertBisect(paths, split_paths, 'bb', exists=False)
114
 
        self.assertBisect(paths, split_paths, 'cc', exists=False)
115
 
        self.assertBisect(paths, split_paths, 'dd', exists=False)
116
 
        self.assertBisect(paths, split_paths, 'a/a', exists=False)
117
 
        self.assertBisect(paths, split_paths, 'b/b', exists=False)
118
 
        self.assertBisect(paths, split_paths, 'c/c', exists=False)
119
 
        self.assertBisect(paths, split_paths, 'd/d', exists=False)
120
 
 
121
 
    def test_involved(self):
122
 
        """This is where bisect_path_* diverges slightly."""
123
 
        # This is the list of paths and their contents
124
 
        # a/
125
 
        #   a/
126
 
        #     a
127
 
        #     z
128
 
        #   a-a/
129
 
        #     a
130
 
        #   a-z/
131
 
        #     z
132
 
        #   a=a/
133
 
        #     a
134
 
        #   a=z/
135
 
        #     z
136
 
        #   z/
137
 
        #     a
138
 
        #     z
139
 
        #   z-a
140
 
        #   z-z
141
 
        #   z=a
142
 
        #   z=z
143
 
        # a-a/
144
 
        #   a
145
 
        # a-z/
146
 
        #   z
147
 
        # a=a/
148
 
        #   a
149
 
        # a=z/
150
 
        #   z
151
 
        # This is the exact order that is stored by dirstate
152
 
        # All children in a directory are mentioned before an children of
153
 
        # children are mentioned.
154
 
        # So all the root-directory paths, then all the
155
 
        # first sub directory, etc.
156
 
        paths = [# content of '/'
157
 
                 '', 'a', 'a-a', 'a-z', 'a=a', 'a=z',
158
 
                 # content of 'a/'
159
 
                 'a/a', 'a/a-a', 'a/a-z',
160
 
                 'a/a=a', 'a/a=z',
161
 
                 'a/z', 'a/z-a', 'a/z-z',
162
 
                 'a/z=a', 'a/z=z',
163
 
                 # content of 'a/a/'
164
 
                 'a/a/a', 'a/a/z',
165
 
                 # content of 'a/a-a'
166
 
                 'a/a-a/a',
167
 
                 # content of 'a/a-z'
168
 
                 'a/a-z/z',
169
 
                 # content of 'a/a=a'
170
 
                 'a/a=a/a',
171
 
                 # content of 'a/a=z'
172
 
                 'a/a=z/z',
173
 
                 # content of 'a/z/'
174
 
                 'a/z/a', 'a/z/z',
175
 
                 # content of 'a-a'
176
 
                 'a-a/a',
177
 
                 # content of 'a-z'
178
 
                 'a-z/z',
179
 
                 # content of 'a=a'
180
 
                 'a=a/a',
181
 
                 # content of 'a=z'
182
 
                 'a=z/z',
183
 
                ]
184
 
        split_paths = self.split_for_dirblocks(paths)
185
 
        sorted_paths = []
186
 
        for dir_parts, basename in split_paths:
187
 
            if dir_parts == ['']:
188
 
                sorted_paths.append(basename)
189
 
            else:
190
 
                sorted_paths.append('/'.join(dir_parts + [basename]))
191
 
 
192
 
        self.assertEqual(sorted_paths, paths)
193
 
 
194
 
        for path in paths:
195
 
            self.assertBisect(paths, split_paths, path, exists=True)
196
 
 
197
 
 
198
 
class TestBisectPathLeft(tests.TestCase, TestBisectPathMixin):
199
 
    """Run all Bisect Path tests against _bisect_path_left_py."""
200
 
 
201
 
    def get_bisect_path(self):
202
 
        from bzrlib._dirstate_helpers_py import _bisect_path_left_py
203
 
        return _bisect_path_left_py
204
 
 
205
 
    def get_bisect(self):
206
 
        return bisect.bisect_left, 0
207
 
 
208
 
 
209
 
class TestCompiledBisectPathLeft(TestBisectPathLeft):
210
 
    """Run all Bisect Path tests against _bisect_path_right_c"""
211
 
 
212
 
    _test_needs_features = [CompiledDirstateHelpersFeature]
213
 
 
214
 
    def get_bisect_path(self):
215
 
        from bzrlib._dirstate_helpers_c import _bisect_path_left_c
216
 
        return _bisect_path_left_c
217
 
 
218
 
 
219
 
class TestBisectPathRight(tests.TestCase, TestBisectPathMixin):
220
 
    """Run all Bisect Path tests against _bisect_path_right_py"""
221
 
 
222
 
    def get_bisect_path(self):
223
 
        from bzrlib._dirstate_helpers_py import _bisect_path_right_py
224
 
        return _bisect_path_right_py
225
 
 
226
 
    def get_bisect(self):
227
 
        return bisect.bisect_right, -1
228
 
 
229
 
 
230
 
class TestCompiledBisectPathRight(TestBisectPathRight):
231
 
    """Run all Bisect Path tests against _bisect_path_right_c"""
232
 
 
233
 
    _test_needs_features = [CompiledDirstateHelpersFeature]
234
 
 
235
 
    def get_bisect_path(self):
236
 
        from bzrlib._dirstate_helpers_c import _bisect_path_right_c
237
 
        return _bisect_path_right_c
238
 
 
239
 
 
240
 
class TestBisectDirblock(tests.TestCase):
241
 
    """Test that bisect_dirblock() returns the expected values.
242
 
 
243
 
    bisect_dirblock is intended to work like bisect.bisect_left() except it
244
 
    knows it is working on dirblocks and that dirblocks are sorted by ('path',
245
 
    'to', 'foo') chunks rather than by raw 'path/to/foo'.
246
 
 
247
 
    This test is parameterized by calling get_bisect_dirblock(). Child test
248
 
    cases can override this function to test against a different
249
 
    implementation.
250
 
    """
251
 
 
252
 
    def get_bisect_dirblock(self):
253
 
        """Return an implementation of bisect_dirblock"""
254
 
        from bzrlib._dirstate_helpers_py import bisect_dirblock_py
255
 
        return bisect_dirblock_py
256
 
 
257
 
    def assertBisect(self, dirblocks, split_dirblocks, path, *args, **kwargs):
258
 
        """Assert that bisect_split works like bisect_left on the split paths.
259
 
 
260
 
        :param dirblocks: A list of (path, [info]) pairs.
261
 
        :param split_dirblocks: A list of ((split, path), [info]) pairs.
262
 
        :param path: The path we are indexing.
263
 
 
264
 
        All other arguments will be passed along.
265
 
        """
266
 
        bisect_dirblock = self.get_bisect_dirblock()
267
 
        self.assertIsInstance(dirblocks, list)
268
 
        bisect_split_idx = bisect_dirblock(dirblocks, path, *args, **kwargs)
269
 
        split_dirblock = (path.split('/'), [])
270
 
        bisect_left_idx = bisect.bisect_left(split_dirblocks, split_dirblock,
271
 
                                             *args)
272
 
        self.assertEqual(bisect_left_idx, bisect_split_idx,
273
 
                         'bisect_split disagreed. %s != %s'
274
 
                         ' for key %r'
275
 
                         % (bisect_left_idx, bisect_split_idx, path)
276
 
                         )
277
 
 
278
 
    def paths_to_dirblocks(self, paths):
279
 
        """Convert a list of paths into dirblock form.
280
 
 
281
 
        Also, ensure that the paths are in proper sorted order.
282
 
        """
283
 
        dirblocks = [(path, []) for path in paths]
284
 
        split_dirblocks = [(path.split('/'), []) for path in paths]
285
 
        self.assertEqual(sorted(split_dirblocks), split_dirblocks)
286
 
        return dirblocks, split_dirblocks
287
 
 
288
 
    def test_simple(self):
289
 
        """In the simple case it works just like bisect_left"""
290
 
        paths = ['', 'a', 'b', 'c', 'd']
291
 
        dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
292
 
        for path in paths:
293
 
            self.assertBisect(dirblocks, split_dirblocks, path)
294
 
        self.assertBisect(dirblocks, split_dirblocks, '_')
295
 
        self.assertBisect(dirblocks, split_dirblocks, 'aa')
296
 
        self.assertBisect(dirblocks, split_dirblocks, 'bb')
297
 
        self.assertBisect(dirblocks, split_dirblocks, 'cc')
298
 
        self.assertBisect(dirblocks, split_dirblocks, 'dd')
299
 
        self.assertBisect(dirblocks, split_dirblocks, 'a/a')
300
 
        self.assertBisect(dirblocks, split_dirblocks, 'b/b')
301
 
        self.assertBisect(dirblocks, split_dirblocks, 'c/c')
302
 
        self.assertBisect(dirblocks, split_dirblocks, 'd/d')
303
 
 
304
 
    def test_involved(self):
305
 
        """This is where bisect_left diverges slightly."""
306
 
        paths = ['', 'a',
307
 
                 'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
308
 
                 'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
309
 
                 'a-a', 'a-z',
310
 
                 'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
311
 
                 'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
312
 
                 'z-a', 'z-z',
313
 
                ]
314
 
        dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
315
 
        for path in paths:
316
 
            self.assertBisect(dirblocks, split_dirblocks, path)
317
 
 
318
 
    def test_involved_cached(self):
319
 
        """This is where bisect_left diverges slightly."""
320
 
        paths = ['', 'a',
321
 
                 'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
322
 
                 'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
323
 
                 'a-a', 'a-z',
324
 
                 'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
325
 
                 'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
326
 
                 'z-a', 'z-z',
327
 
                ]
328
 
        cache = {}
329
 
        dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
330
 
        for path in paths:
331
 
            self.assertBisect(dirblocks, split_dirblocks, path, cache=cache)
332
 
 
333
 
 
334
 
class TestCompiledBisectDirblock(TestBisectDirblock):
335
 
    """Test that bisect_dirblock() returns the expected values.
336
 
 
337
 
    bisect_dirblock is intended to work like bisect.bisect_left() except it
338
 
    knows it is working on dirblocks and that dirblocks are sorted by ('path',
339
 
    'to', 'foo') chunks rather than by raw 'path/to/foo'.
340
 
 
341
 
    This runs all the normal tests that TestBisectDirblock did, but uses the
342
 
    compiled version.
343
 
    """
344
 
 
345
 
    _test_needs_features = [CompiledDirstateHelpersFeature]
346
 
 
347
 
    def get_bisect_dirblock(self):
348
 
        from bzrlib._dirstate_helpers_c import bisect_dirblock_c
349
 
        return bisect_dirblock_c
350
 
 
351
 
 
352
 
class TestCmpByDirs(tests.TestCase):
353
 
    """Test an implementation of cmp_by_dirs()
354
 
 
355
 
    cmp_by_dirs() compares 2 paths by their directory sections, rather than as
356
 
    plain strings.
357
 
 
358
 
    Child test cases can override ``get_cmp_by_dirs`` to test a specific
359
 
    implementation.
360
 
    """
361
 
 
362
 
    def get_cmp_by_dirs(self):
363
 
        """Get a specific implementation of cmp_by_dirs."""
364
 
        from bzrlib._dirstate_helpers_py import cmp_by_dirs_py
365
 
        return cmp_by_dirs_py
366
 
 
367
 
    def assertCmpByDirs(self, expected, str1, str2):
368
 
        """Compare the two strings, in both directions.
369
 
 
370
 
        :param expected: The expected comparison value. -1 means str1 comes
371
 
            first, 0 means they are equal, 1 means str2 comes first
372
 
        :param str1: string to compare
373
 
        :param str2: string to compare
374
 
        """
375
 
        cmp_by_dirs = self.get_cmp_by_dirs()
376
 
        if expected == 0:
377
 
            self.assertEqual(str1, str2)
378
 
            self.assertEqual(0, cmp_by_dirs(str1, str2))
379
 
            self.assertEqual(0, cmp_by_dirs(str2, str1))
380
 
        elif expected > 0:
381
 
            self.assertPositive(cmp_by_dirs(str1, str2))
382
 
            self.assertNegative(cmp_by_dirs(str2, str1))
383
 
        else:
384
 
            self.assertNegative(cmp_by_dirs(str1, str2))
385
 
            self.assertPositive(cmp_by_dirs(str2, str1))
386
 
 
387
 
    def test_cmp_empty(self):
388
 
        """Compare against the empty string."""
389
 
        self.assertCmpByDirs(0, '', '')
390
 
        self.assertCmpByDirs(1, 'a', '')
391
 
        self.assertCmpByDirs(1, 'ab', '')
392
 
        self.assertCmpByDirs(1, 'abc', '')
393
 
        self.assertCmpByDirs(1, 'abcd', '')
394
 
        self.assertCmpByDirs(1, 'abcde', '')
395
 
        self.assertCmpByDirs(1, 'abcdef', '')
396
 
        self.assertCmpByDirs(1, 'abcdefg', '')
397
 
        self.assertCmpByDirs(1, 'abcdefgh', '')
398
 
        self.assertCmpByDirs(1, 'abcdefghi', '')
399
 
        self.assertCmpByDirs(1, 'test/ing/a/path/', '')
400
 
 
401
 
    def test_cmp_same_str(self):
402
 
        """Compare the same string"""
403
 
        self.assertCmpByDirs(0, 'a', 'a')
404
 
        self.assertCmpByDirs(0, 'ab', 'ab')
405
 
        self.assertCmpByDirs(0, 'abc', 'abc')
406
 
        self.assertCmpByDirs(0, 'abcd', 'abcd')
407
 
        self.assertCmpByDirs(0, 'abcde', 'abcde')
408
 
        self.assertCmpByDirs(0, 'abcdef', 'abcdef')
409
 
        self.assertCmpByDirs(0, 'abcdefg', 'abcdefg')
410
 
        self.assertCmpByDirs(0, 'abcdefgh', 'abcdefgh')
411
 
        self.assertCmpByDirs(0, 'abcdefghi', 'abcdefghi')
412
 
        self.assertCmpByDirs(0, 'testing a long string', 'testing a long string')
413
 
        self.assertCmpByDirs(0, 'x'*10000, 'x'*10000)
414
 
        self.assertCmpByDirs(0, 'a/b', 'a/b')
415
 
        self.assertCmpByDirs(0, 'a/b/c', 'a/b/c')
416
 
        self.assertCmpByDirs(0, 'a/b/c/d', 'a/b/c/d')
417
 
        self.assertCmpByDirs(0, 'a/b/c/d/e', 'a/b/c/d/e')
418
 
 
419
 
    def test_simple_paths(self):
420
 
        """Compare strings that act like normal string comparison"""
421
 
        self.assertCmpByDirs(-1, 'a', 'b')
422
 
        self.assertCmpByDirs(-1, 'aa', 'ab')
423
 
        self.assertCmpByDirs(-1, 'ab', 'bb')
424
 
        self.assertCmpByDirs(-1, 'aaa', 'aab')
425
 
        self.assertCmpByDirs(-1, 'aab', 'abb')
426
 
        self.assertCmpByDirs(-1, 'abb', 'bbb')
427
 
        self.assertCmpByDirs(-1, 'aaaa', 'aaab')
428
 
        self.assertCmpByDirs(-1, 'aaab', 'aabb')
429
 
        self.assertCmpByDirs(-1, 'aabb', 'abbb')
430
 
        self.assertCmpByDirs(-1, 'abbb', 'bbbb')
431
 
        self.assertCmpByDirs(-1, 'aaaaa', 'aaaab')
432
 
        self.assertCmpByDirs(-1, 'a/a', 'a/b')
433
 
        self.assertCmpByDirs(-1, 'a/b', 'b/b')
434
 
        self.assertCmpByDirs(-1, 'a/a/a', 'a/a/b')
435
 
        self.assertCmpByDirs(-1, 'a/a/b', 'a/b/b')
436
 
        self.assertCmpByDirs(-1, 'a/b/b', 'b/b/b')
437
 
        self.assertCmpByDirs(-1, 'a/a/a/a', 'a/a/a/b')
438
 
        self.assertCmpByDirs(-1, 'a/a/a/b', 'a/a/b/b')
439
 
        self.assertCmpByDirs(-1, 'a/a/b/b', 'a/b/b/b')
440
 
        self.assertCmpByDirs(-1, 'a/b/b/b', 'b/b/b/b')
441
 
        self.assertCmpByDirs(-1, 'a/a/a/a/a', 'a/a/a/a/b')
442
 
 
443
 
    def test_tricky_paths(self):
444
 
        self.assertCmpByDirs(1, 'ab/cd/ef', 'ab/cc/ef')
445
 
        self.assertCmpByDirs(1, 'ab/cd/ef', 'ab/c/ef')
446
 
        self.assertCmpByDirs(-1, 'ab/cd/ef', 'ab/cd-ef')
447
 
        self.assertCmpByDirs(-1, 'ab/cd', 'ab/cd-')
448
 
        self.assertCmpByDirs(-1, 'ab/cd', 'ab-cd')
449
 
 
450
 
    def test_cmp_unicode_not_allowed(self):
451
 
        cmp_by_dirs = self.get_cmp_by_dirs()
452
 
        self.assertRaises(TypeError, cmp_by_dirs, u'Unicode', 'str')
453
 
        self.assertRaises(TypeError, cmp_by_dirs, 'str', u'Unicode')
454
 
        self.assertRaises(TypeError, cmp_by_dirs, u'Unicode', u'Unicode')
455
 
 
456
 
    def test_cmp_non_ascii(self):
457
 
        self.assertCmpByDirs(-1, '\xc2\xb5', '\xc3\xa5') # u'\xb5', u'\xe5'
458
 
        self.assertCmpByDirs(-1, 'a', '\xc3\xa5') # u'a', u'\xe5'
459
 
        self.assertCmpByDirs(-1, 'b', '\xc2\xb5') # u'b', u'\xb5'
460
 
        self.assertCmpByDirs(-1, 'a/b', 'a/\xc3\xa5') # u'a/b', u'a/\xe5'
461
 
        self.assertCmpByDirs(-1, 'b/a', 'b/\xc2\xb5') # u'b/a', u'b/\xb5'
462
 
 
463
 
 
464
 
class TestCompiledCmpByDirs(TestCmpByDirs):
465
 
    """Test the pyrex implementation of cmp_by_dirs"""
466
 
 
467
 
    _test_needs_features = [CompiledDirstateHelpersFeature]
468
 
 
469
 
    def get_cmp_by_dirs(self):
470
 
        from bzrlib._dirstate_helpers_c import cmp_by_dirs_c
471
 
        return cmp_by_dirs_c
472
 
 
473
 
 
474
 
class TestCmpPathByDirblock(tests.TestCase):
475
 
    """Test an implementation of _cmp_path_by_dirblock()
476
 
 
477
 
    _cmp_path_by_dirblock() compares two paths using the sort order used by
478
 
    DirState. All paths in the same directory are sorted together.
479
 
 
480
 
    Child test cases can override ``get_cmp_path_by_dirblock`` to test a specific
481
 
    implementation.
482
 
    """
483
 
 
484
 
    def get_cmp_path_by_dirblock(self):
485
 
        """Get a specific implementation of _cmp_path_by_dirblock."""
486
 
        from bzrlib._dirstate_helpers_py import _cmp_path_by_dirblock_py
487
 
        return _cmp_path_by_dirblock_py
488
 
 
489
 
    def assertCmpPathByDirblock(self, paths):
490
 
        """Compare all paths and make sure they evaluate to the correct order.
491
 
 
492
 
        This does N^2 comparisons. It is assumed that ``paths`` is properly
493
 
        sorted list.
494
 
 
495
 
        :param paths: a sorted list of paths to compare
496
 
        """
497
 
        # First, make sure the paths being passed in are correct
498
 
        def _key(p):
499
 
            dirname, basename = os.path.split(p)
500
 
            return dirname.split('/'), basename
501
 
        self.assertEqual(sorted(paths, key=_key), paths)
502
 
 
503
 
        cmp_path_by_dirblock = self.get_cmp_path_by_dirblock()
504
 
        for idx1, path1 in enumerate(paths):
505
 
            for idx2, path2 in enumerate(paths):
506
 
                cmp_val = cmp_path_by_dirblock(path1, path2)
507
 
                if idx1 < idx2:
508
 
                    self.assertTrue(cmp_val < 0,
509
 
                        '%s did not state that %r came before %r, cmp=%s'
510
 
                        % (cmp_path_by_dirblock.__name__,
511
 
                           path1, path2, cmp_val))
512
 
                elif idx1 > idx2:
513
 
                    self.assertTrue(cmp_val > 0,
514
 
                        '%s did not state that %r came after %r, cmp=%s'
515
 
                        % (cmp_path_by_dirblock.__name__,
516
 
                           path1, path2, cmp_val))
517
 
                else: # idx1 == idx2
518
 
                    self.assertTrue(cmp_val == 0,
519
 
                        '%s did not state that %r == %r, cmp=%s'
520
 
                        % (cmp_path_by_dirblock.__name__,
521
 
                           path1, path2, cmp_val))
522
 
 
523
 
    def test_cmp_simple_paths(self):
524
 
        """Compare against the empty string."""
525
 
        self.assertCmpPathByDirblock(['', 'a', 'ab', 'abc', 'a/b/c', 'b/d/e'])
526
 
        self.assertCmpPathByDirblock(['kl', 'ab/cd', 'ab/ef', 'gh/ij'])
527
 
 
528
 
    def test_tricky_paths(self):
529
 
        self.assertCmpPathByDirblock([
530
 
            # Contents of ''
531
 
            '', 'a', 'a-a', 'a=a', 'b',
532
 
            # Contents of 'a'
533
 
            'a/a', 'a/a-a', 'a/a=a', 'a/b',
534
 
            # Contents of 'a/a'
535
 
            'a/a/a', 'a/a/a-a', 'a/a/a=a',
536
 
            # Contents of 'a/a/a'
537
 
            'a/a/a/a', 'a/a/a/b',
538
 
            # Contents of 'a/a/a-a',
539
 
            'a/a/a-a/a', 'a/a/a-a/b',
540
 
            # Contents of 'a/a/a=a',
541
 
            'a/a/a=a/a', 'a/a/a=a/b',
542
 
            # Contents of 'a/a-a'
543
 
            'a/a-a/a',
544
 
            # Contents of 'a/a-a/a'
545
 
            'a/a-a/a/a', 'a/a-a/a/b',
546
 
            # Contents of 'a/a=a'
547
 
            'a/a=a/a',
548
 
            # Contents of 'a/b'
549
 
            'a/b/a', 'a/b/b',
550
 
            # Contents of 'a-a',
551
 
            'a-a/a', 'a-a/b',
552
 
            # Contents of 'a=a',
553
 
            'a=a/a', 'a=a/b',
554
 
            # Contents of 'b',
555
 
            'b/a', 'b/b',
556
 
            ])
557
 
        self.assertCmpPathByDirblock([
558
 
                 # content of '/'
559
 
                 '', 'a', 'a-a', 'a-z', 'a=a', 'a=z',
560
 
                 # content of 'a/'
561
 
                 'a/a', 'a/a-a', 'a/a-z',
562
 
                 'a/a=a', 'a/a=z',
563
 
                 'a/z', 'a/z-a', 'a/z-z',
564
 
                 'a/z=a', 'a/z=z',
565
 
                 # content of 'a/a/'
566
 
                 'a/a/a', 'a/a/z',
567
 
                 # content of 'a/a-a'
568
 
                 'a/a-a/a',
569
 
                 # content of 'a/a-z'
570
 
                 'a/a-z/z',
571
 
                 # content of 'a/a=a'
572
 
                 'a/a=a/a',
573
 
                 # content of 'a/a=z'
574
 
                 'a/a=z/z',
575
 
                 # content of 'a/z/'
576
 
                 'a/z/a', 'a/z/z',
577
 
                 # content of 'a-a'
578
 
                 'a-a/a',
579
 
                 # content of 'a-z'
580
 
                 'a-z/z',
581
 
                 # content of 'a=a'
582
 
                 'a=a/a',
583
 
                 # content of 'a=z'
584
 
                 'a=z/z',
585
 
                ])
586
 
 
587
 
    def test_unicode_not_allowed(self):
588
 
        cmp_path_by_dirblock = self.get_cmp_path_by_dirblock()
589
 
        self.assertRaises(TypeError, cmp_path_by_dirblock, u'Uni', 'str')
590
 
        self.assertRaises(TypeError, cmp_path_by_dirblock, 'str', u'Uni')
591
 
        self.assertRaises(TypeError, cmp_path_by_dirblock, u'Uni', u'Uni')
592
 
        self.assertRaises(TypeError, cmp_path_by_dirblock, u'x/Uni', 'x/str')
593
 
        self.assertRaises(TypeError, cmp_path_by_dirblock, 'x/str', u'x/Uni')
594
 
        self.assertRaises(TypeError, cmp_path_by_dirblock, u'x/Uni', u'x/Uni')
595
 
 
596
 
    def test_nonascii(self):
597
 
        self.assertCmpPathByDirblock([
598
 
            # content of '/'
599
 
            '', 'a', '\xc2\xb5', '\xc3\xa5',
600
 
            # content of 'a'
601
 
            'a/a', 'a/\xc2\xb5', 'a/\xc3\xa5',
602
 
            # content of 'a/a'
603
 
            'a/a/a', 'a/a/\xc2\xb5', 'a/a/\xc3\xa5',
604
 
            # content of 'a/\xc2\xb5'
605
 
            'a/\xc2\xb5/a', 'a/\xc2\xb5/\xc2\xb5', 'a/\xc2\xb5/\xc3\xa5',
606
 
            # content of 'a/\xc3\xa5'
607
 
            'a/\xc3\xa5/a', 'a/\xc3\xa5/\xc2\xb5', 'a/\xc3\xa5/\xc3\xa5',
608
 
            # content of '\xc2\xb5'
609
 
            '\xc2\xb5/a', '\xc2\xb5/\xc2\xb5', '\xc2\xb5/\xc3\xa5',
610
 
            # content of '\xc2\xe5'
611
 
            '\xc3\xa5/a', '\xc3\xa5/\xc2\xb5', '\xc3\xa5/\xc3\xa5',
612
 
            ])
613
 
 
614
 
 
615
 
class TestCompiledCmpPathByDirblock(TestCmpPathByDirblock):
616
 
    """Test the pyrex implementation of _cmp_path_by_dirblock"""
617
 
 
618
 
    _test_needs_features = [CompiledDirstateHelpersFeature]
619
 
 
620
 
    def get_cmp_by_dirs(self):
621
 
        from bzrlib._dirstate_helpers_c import _cmp_path_by_dirblock_c
622
 
        return _cmp_path_by_dirblock_c
623
 
 
624
 
 
625
 
class TestMemRChr(tests.TestCase):
626
 
    """Test memrchr functionality"""
627
 
 
628
 
    _test_needs_features = [CompiledDirstateHelpersFeature]
629
 
 
630
 
    def assertMemRChr(self, expected, s, c):
631
 
        from bzrlib._dirstate_helpers_c import _py_memrchr
632
 
        self.assertEqual(expected, _py_memrchr(s, c))
633
 
 
634
 
    def test_missing(self):
635
 
        self.assertMemRChr(None, '', 'a')
636
 
        self.assertMemRChr(None, '', 'c')
637
 
        self.assertMemRChr(None, 'abcdefghijklm', 'q')
638
 
        self.assertMemRChr(None, 'aaaaaaaaaaaaaaaaaaaaaaa', 'b')
639
 
 
640
 
    def test_single_entry(self):
641
 
        self.assertMemRChr(0, 'abcdefghijklm', 'a')
642
 
        self.assertMemRChr(1, 'abcdefghijklm', 'b')
643
 
        self.assertMemRChr(2, 'abcdefghijklm', 'c')
644
 
        self.assertMemRChr(10, 'abcdefghijklm', 'k')
645
 
        self.assertMemRChr(11, 'abcdefghijklm', 'l')
646
 
        self.assertMemRChr(12, 'abcdefghijklm', 'm')
647
 
 
648
 
    def test_multiple(self):
649
 
        self.assertMemRChr(10, 'abcdefjklmabcdefghijklm', 'a')
650
 
        self.assertMemRChr(11, 'abcdefjklmabcdefghijklm', 'b')
651
 
        self.assertMemRChr(12, 'abcdefjklmabcdefghijklm', 'c')
652
 
        self.assertMemRChr(20, 'abcdefjklmabcdefghijklm', 'k')
653
 
        self.assertMemRChr(21, 'abcdefjklmabcdefghijklm', 'l')
654
 
        self.assertMemRChr(22, 'abcdefjklmabcdefghijklm', 'm')
655
 
        self.assertMemRChr(22, 'aaaaaaaaaaaaaaaaaaaaaaa', 'a')
656
 
 
657
 
    def test_with_nulls(self):
658
 
        self.assertMemRChr(10, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'a')
659
 
        self.assertMemRChr(11, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'b')
660
 
        self.assertMemRChr(12, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'c')
661
 
        self.assertMemRChr(20, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'k')
662
 
        self.assertMemRChr(21, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'l')
663
 
        self.assertMemRChr(22, 'abc\0\0\0jklmabc\0\0\0ghijklm', 'm')
664
 
        self.assertMemRChr(22, 'aaa\0\0\0aaaaaaa\0\0\0aaaaaaa', 'a')
665
 
        self.assertMemRChr(9, '\0\0\0\0\0\0\0\0\0\0', '\0')
666
 
 
667
 
 
668
 
class TestReadDirblocks(test_dirstate.TestCaseWithDirState):
669
 
    """Test an implementation of _read_dirblocks()
670
 
 
671
 
    _read_dirblocks() reads in all of the dirblock information from the disk
672
 
    file.
673
 
 
674
 
    Child test cases can override ``get_read_dirblocks`` to test a specific
675
 
    implementation.
676
 
    """
677
 
 
678
 
    def get_read_dirblocks(self):
679
 
        from bzrlib._dirstate_helpers_py import _read_dirblocks_py
680
 
        return _read_dirblocks_py
681
 
 
682
 
    def test_smoketest(self):
683
 
        """Make sure that we can create and read back a simple file."""
684
 
        tree, state, expected = self.create_basic_dirstate()
685
 
        del tree
686
 
        state._read_header_if_needed()
687
 
        self.assertEqual(dirstate.DirState.NOT_IN_MEMORY,
688
 
                         state._dirblock_state)
689
 
        read_dirblocks = self.get_read_dirblocks()
690
 
        read_dirblocks(state)
691
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
692
 
                         state._dirblock_state)
693
 
 
694
 
 
695
 
class TestCompiledReadDirblocks(TestReadDirblocks):
696
 
    """Test the pyrex implementation of _read_dirblocks"""
697
 
 
698
 
    _test_needs_features = [CompiledDirstateHelpersFeature]
699
 
 
700
 
    def get_read_dirblocks(self):
701
 
        from bzrlib._dirstate_helpers_c import _read_dirblocks_c
702
 
        return _read_dirblocks_c
703
 
 
704
 
 
705
 
class TestUsingCompiledIfAvailable(tests.TestCase):
706
 
    """Check that any compiled functions that are available are the default.
707
 
 
708
 
    It is possible to have typos, etc in the import line, such that
709
 
    _dirstate_helpers_c is actually available, but the compiled functions are
710
 
    not being used.
711
 
    """
712
 
 
713
 
    def test_bisect_dirblock(self):
714
 
        if CompiledDirstateHelpersFeature.available():
715
 
            from bzrlib._dirstate_helpers_c import bisect_dirblock_c
716
 
            self.assertIs(bisect_dirblock_c, dirstate.bisect_dirblock)
717
 
        else:
718
 
            from bzrlib._dirstate_helpers_py import bisect_dirblock_py
719
 
            self.assertIs(bisect_dirblock_py, dirstate.bisect_dirblock)
720
 
 
721
 
    def test__bisect_path_left(self):
722
 
        if CompiledDirstateHelpersFeature.available():
723
 
            from bzrlib._dirstate_helpers_c import _bisect_path_left_c
724
 
            self.assertIs(_bisect_path_left_c, dirstate._bisect_path_left)
725
 
        else:
726
 
            from bzrlib._dirstate_helpers_py import _bisect_path_left_py
727
 
            self.assertIs(_bisect_path_left_py, dirstate._bisect_path_left)
728
 
 
729
 
    def test__bisect_path_right(self):
730
 
        if CompiledDirstateHelpersFeature.available():
731
 
            from bzrlib._dirstate_helpers_c import _bisect_path_right_c
732
 
            self.assertIs(_bisect_path_right_c, dirstate._bisect_path_right)
733
 
        else:
734
 
            from bzrlib._dirstate_helpers_py import _bisect_path_right_py
735
 
            self.assertIs(_bisect_path_right_py, dirstate._bisect_path_right)
736
 
 
737
 
    def test_cmp_by_dirs(self):
738
 
        if CompiledDirstateHelpersFeature.available():
739
 
            from bzrlib._dirstate_helpers_c import cmp_by_dirs_c
740
 
            self.assertIs(cmp_by_dirs_c, dirstate.cmp_by_dirs)
741
 
        else:
742
 
            from bzrlib._dirstate_helpers_py import cmp_by_dirs_py
743
 
            self.assertIs(cmp_by_dirs_py, dirstate.cmp_by_dirs)
744
 
 
745
 
    def test__read_dirblocks(self):
746
 
        if CompiledDirstateHelpersFeature.available():
747
 
            from bzrlib._dirstate_helpers_c import _read_dirblocks_c
748
 
            self.assertIs(_read_dirblocks_c, dirstate._read_dirblocks)
749
 
        else:
750
 
            from bzrlib._dirstate_helpers_py import _read_dirblocks_py
751
 
            self.assertIs(_read_dirblocks_py, dirstate._read_dirblocks)
752