17
17
"""Tests for the compiled dirstate helpers."""
19
21
from bzrlib import (
23
from bzrlib.compiled import dirstate_helpers
25
have_dirstate_helpers = False
27
have_dirstate_helpers = True
28
from bzrlib.tests import test_dirstate
31
26
class _CompiledDirstateHelpersFeature(tests.Feature):
33
return have_dirstate_helpers
29
import bzrlib._dirstate_helpers_c
35
34
def feature_name(self):
36
return 'bzrlib.compiled.dirstate_helpers'
35
return 'bzrlib._dirstate_helpers_c'
38
37
CompiledDirstateHelpersFeature = _CompiledDirstateHelpersFeature()
41
class TestCCmpByDirs(test_dirstate.TestCmpByDirs):
40
class TestCmpByDirs(tests.TestCase):
42
def get_cmp_by_dirs(self):
43
"""Get a specific implementation of cmp_by_dirs."""
44
from bzrlib._dirstate_helpers_py import cmp_by_dirs_py
47
def assertPositive(self, val):
48
"""Assert that val is greater than 0."""
49
self.assertTrue(val > 0, 'expected a positive value, but got %s' % val)
51
def assertNegative(self, val):
52
"""Assert that val is less than 0."""
53
self.assertTrue(val < 0, 'expected a negative value, but got %s' % val)
55
def assertCmpByDirs(self, expected, str1, str2):
56
"""Compare the two strings, in both directions.
58
:param expected: The expected comparison value. -1 means str1 comes
59
first, 0 means they are equal, 1 means str2 comes first
60
:param str1: string to compare
61
:param str2: string to compare
63
cmp_by_dirs = self.get_cmp_by_dirs()
65
self.assertEqual(str1, str2)
66
self.assertEqual(0, cmp_by_dirs(str1, str2))
67
self.assertEqual(0, cmp_by_dirs(str2, str1))
69
self.assertPositive(cmp_by_dirs(str1, str2))
70
self.assertNegative(cmp_by_dirs(str2, str1))
72
self.assertNegative(cmp_by_dirs(str1, str2))
73
self.assertPositive(cmp_by_dirs(str2, str1))
75
def test_cmp_empty(self):
76
"""Compare against the empty string."""
77
self.assertCmpByDirs(0, '', '')
78
self.assertCmpByDirs(1, 'a', '')
79
self.assertCmpByDirs(1, 'ab', '')
80
self.assertCmpByDirs(1, 'abc', '')
81
self.assertCmpByDirs(1, 'abcd', '')
82
self.assertCmpByDirs(1, 'abcde', '')
83
self.assertCmpByDirs(1, 'abcdef', '')
84
self.assertCmpByDirs(1, 'abcdefg', '')
85
self.assertCmpByDirs(1, 'abcdefgh', '')
86
self.assertCmpByDirs(1, 'abcdefghi', '')
87
self.assertCmpByDirs(1, 'test/ing/a/path/', '')
89
def test_cmp_same_str(self):
90
"""Compare the same string"""
91
self.assertCmpByDirs(0, 'a', 'a')
92
self.assertCmpByDirs(0, 'ab', 'ab')
93
self.assertCmpByDirs(0, 'abc', 'abc')
94
self.assertCmpByDirs(0, 'abcd', 'abcd')
95
self.assertCmpByDirs(0, 'abcde', 'abcde')
96
self.assertCmpByDirs(0, 'abcdef', 'abcdef')
97
self.assertCmpByDirs(0, 'abcdefg', 'abcdefg')
98
self.assertCmpByDirs(0, 'abcdefgh', 'abcdefgh')
99
self.assertCmpByDirs(0, 'abcdefghi', 'abcdefghi')
100
self.assertCmpByDirs(0, 'testing a long string', 'testing a long string')
101
self.assertCmpByDirs(0, 'x'*10000, 'x'*10000)
102
self.assertCmpByDirs(0, 'a/b', 'a/b')
103
self.assertCmpByDirs(0, 'a/b/c', 'a/b/c')
104
self.assertCmpByDirs(0, 'a/b/c/d', 'a/b/c/d')
105
self.assertCmpByDirs(0, 'a/b/c/d/e', 'a/b/c/d/e')
107
def test_simple_paths(self):
108
"""Compare strings that act like normal string comparison"""
109
self.assertCmpByDirs(-1, 'a', 'b')
110
self.assertCmpByDirs(-1, 'aa', 'ab')
111
self.assertCmpByDirs(-1, 'ab', 'bb')
112
self.assertCmpByDirs(-1, 'aaa', 'aab')
113
self.assertCmpByDirs(-1, 'aab', 'abb')
114
self.assertCmpByDirs(-1, 'abb', 'bbb')
115
self.assertCmpByDirs(-1, 'aaaa', 'aaab')
116
self.assertCmpByDirs(-1, 'aaab', 'aabb')
117
self.assertCmpByDirs(-1, 'aabb', 'abbb')
118
self.assertCmpByDirs(-1, 'abbb', 'bbbb')
119
self.assertCmpByDirs(-1, 'aaaaa', 'aaaab')
120
self.assertCmpByDirs(-1, 'a/a', 'a/b')
121
self.assertCmpByDirs(-1, 'a/b', 'b/b')
122
self.assertCmpByDirs(-1, 'a/a/a', 'a/a/b')
123
self.assertCmpByDirs(-1, 'a/a/b', 'a/b/b')
124
self.assertCmpByDirs(-1, 'a/b/b', 'b/b/b')
125
self.assertCmpByDirs(-1, 'a/a/a/a', 'a/a/a/b')
126
self.assertCmpByDirs(-1, 'a/a/a/b', 'a/a/b/b')
127
self.assertCmpByDirs(-1, 'a/a/b/b', 'a/b/b/b')
128
self.assertCmpByDirs(-1, 'a/b/b/b', 'b/b/b/b')
129
self.assertCmpByDirs(-1, 'a/a/a/a/a', 'a/a/a/a/b')
131
def test_tricky_paths(self):
132
self.assertCmpByDirs(1, 'ab/cd/ef', 'ab/cc/ef')
133
self.assertCmpByDirs(1, 'ab/cd/ef', 'ab/c/ef')
134
self.assertCmpByDirs(-1, 'ab/cd/ef', 'ab/cd-ef')
135
self.assertCmpByDirs(-1, 'ab/cd', 'ab/cd-')
136
self.assertCmpByDirs(-1, 'ab/cd', 'ab-cd')
139
class TestCCmpByDirs(TestCmpByDirs):
42
140
"""Test the C implementation of cmp_by_dirs"""
44
142
_test_needs_features = [CompiledDirstateHelpersFeature]
46
144
def get_cmp_by_dirs(self):
47
return dirstate_helpers.cmp_by_dirs_c
50
class TestCompiledBisectDirblock(test_dirstate.TestBisectDirblock):
145
from bzrlib._dirstate_helpers_c import cmp_by_dirs_c
149
class TestBisectDirblock(tests.TestCase):
150
"""Test that bisect_dirblock() returns the expected values.
152
bisect_dirblock is intended to work like bisect.bisect_left() except it
153
knows it is working on dirblocks and that dirblocks are sorted by ('path',
154
'to', 'foo') chunks rather than by raw 'path/to/foo'.
157
def get_bisect_dirblock(self):
158
"""Return an implementation of bisect_dirblock"""
159
from bzrlib._dirstate_helpers_py import bisect_dirblock_py
160
return bisect_dirblock_py
162
def assertBisect(self, dirblocks, split_dirblocks, path, *args, **kwargs):
163
"""Assert that bisect_split works like bisect_left on the split paths.
165
:param dirblocks: A list of (path, [info]) pairs.
166
:param split_dirblocks: A list of ((split, path), [info]) pairs.
167
:param path: The path we are indexing.
169
All other arguments will be passed along.
171
bisect_dirblock = self.get_bisect_dirblock()
172
self.assertIsInstance(dirblocks, list)
173
bisect_split_idx = bisect_dirblock(dirblocks, path, *args, **kwargs)
174
split_dirblock = (path.split('/'), [])
175
bisect_left_idx = bisect.bisect_left(split_dirblocks, split_dirblock,
177
self.assertEqual(bisect_left_idx, bisect_split_idx,
178
'bisect_split disagreed. %s != %s'
180
% (bisect_left_idx, bisect_split_idx, path)
183
def paths_to_dirblocks(self, paths):
184
"""Convert a list of paths into dirblock form.
186
Also, ensure that the paths are in proper sorted order.
188
dirblocks = [(path, []) for path in paths]
189
split_dirblocks = [(path.split('/'), []) for path in paths]
190
self.assertEqual(sorted(split_dirblocks), split_dirblocks)
191
return dirblocks, split_dirblocks
193
def test_simple(self):
194
"""In the simple case it works just like bisect_left"""
195
paths = ['', 'a', 'b', 'c', 'd']
196
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
198
self.assertBisect(dirblocks, split_dirblocks, path)
199
self.assertBisect(dirblocks, split_dirblocks, '_')
200
self.assertBisect(dirblocks, split_dirblocks, 'aa')
201
self.assertBisect(dirblocks, split_dirblocks, 'bb')
202
self.assertBisect(dirblocks, split_dirblocks, 'cc')
203
self.assertBisect(dirblocks, split_dirblocks, 'dd')
204
self.assertBisect(dirblocks, split_dirblocks, 'a/a')
205
self.assertBisect(dirblocks, split_dirblocks, 'b/b')
206
self.assertBisect(dirblocks, split_dirblocks, 'c/c')
207
self.assertBisect(dirblocks, split_dirblocks, 'd/d')
209
def test_involved(self):
210
"""This is where bisect_left diverges slightly."""
212
'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
213
'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
215
'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
216
'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
219
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
221
self.assertBisect(dirblocks, split_dirblocks, path)
223
def test_involved_cached(self):
224
"""This is where bisect_left diverges slightly."""
226
'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
227
'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
229
'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
230
'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
234
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
236
self.assertBisect(dirblocks, split_dirblocks, path, cache=cache)
239
class TestCompiledBisectDirblock(TestBisectDirblock):
51
240
"""Test that bisect_dirblock() returns the expected values.
53
242
bisect_dirblock is intended to work like bisect.bisect_left() except it