##// END OF EJS Templates
mdiff: add a "blocksinrange" function to filter diff blocks by line range...
Denis Laxalde -
r30717:3eeb8e13 default
parent child Browse files
Show More
@@ -0,0 +1,232 b''
1 from __future__ import absolute_import
2
3 import unittest
4 from mercurial import error, mdiff
5
6 # for readability, line numbers are 0-origin
7 text1 = '''
8 00 at OLD
9 01 at OLD
10 02 at OLD
11 02 at NEW, 03 at OLD
12 03 at NEW, 04 at OLD
13 04 at NEW, 05 at OLD
14 05 at NEW, 06 at OLD
15 07 at OLD
16 08 at OLD
17 09 at OLD
18 10 at OLD
19 11 at OLD
20 '''[1:] # strip initial LF
21
22 text2 = '''
23 00 at NEW
24 01 at NEW
25 02 at NEW, 03 at OLD
26 03 at NEW, 04 at OLD
27 04 at NEW, 05 at OLD
28 05 at NEW, 06 at OLD
29 06 at NEW
30 07 at NEW
31 08 at NEW
32 09 at NEW
33 10 at NEW
34 11 at NEW
35 '''[1:] # strip initial LF
36
37 def filteredblocks(blocks, rangeb):
38 """return `rangea` extracted from `blocks` coming from
39 `mdiff.blocksinrange` along with the mask of blocks within rangeb.
40 """
41 filtered, rangea = mdiff.blocksinrange(blocks, rangeb)
42 skipped = [b not in filtered for b in blocks]
43 return rangea, skipped
44
45 class blocksinrangetests(unittest.TestCase):
46
47 def setUp(self):
48 self.blocks = list(mdiff.allblocks(text1, text2))
49 assert self.blocks == [
50 ([0, 3, 0, 2], '!'),
51 ((3, 7, 2, 6), '='),
52 ([7, 12, 6, 12], '!'),
53 ((12, 12, 12, 12), '='),
54 ], self.blocks
55
56 def testWithinEqual(self):
57 """linerange within an "=" block"""
58 # IDX 0 1
59 # 012345678901
60 # SRC NNOOOONNNNNN (New/Old)
61 # ^^
62 linerange2 = (3, 5)
63 linerange1, skipped = filteredblocks(self.blocks, linerange2)
64 self.assertEqual(linerange1, (4, 6))
65 self.assertEqual(skipped, [True, False, True, True])
66
67 def testWithinEqualStrictly(self):
68 """linerange matching exactly an "=" block"""
69 # IDX 0 1
70 # 012345678901
71 # SRC NNOOOONNNNNN (New/Old)
72 # ^^^^
73 linerange2 = (2, 6)
74 linerange1, skipped = filteredblocks(self.blocks, linerange2)
75 self.assertEqual(linerange1, (3, 7))
76 self.assertEqual(skipped, [True, False, True, True])
77
78 def testWithinEqualLowerbound(self):
79 """linerange at beginning of an "=" block"""
80 # IDX 0 1
81 # 012345678901
82 # SRC NNOOOONNNNNN (New/Old)
83 # ^^
84 linerange2 = (2, 4)
85 linerange1, skipped = filteredblocks(self.blocks, linerange2)
86 self.assertEqual(linerange1, (3, 5))
87 self.assertEqual(skipped, [True, False, True, True])
88
89 def testWithinEqualLowerboundOneline(self):
90 """oneline-linerange at beginning of an "=" block"""
91 # IDX 0 1
92 # 012345678901
93 # SRC NNOOOONNNNNN (New/Old)
94 # ^
95 linerange2 = (2, 3)
96 linerange1, skipped = filteredblocks(self.blocks, linerange2)
97 self.assertEqual(linerange1, (3, 4))
98 self.assertEqual(skipped, [True, False, True, True])
99
100 def testWithinEqualUpperbound(self):
101 """linerange at end of an "=" block"""
102 # IDX 0 1
103 # 012345678901
104 # SRC NNOOOONNNNNN (New/Old)
105 # ^^^
106 linerange2 = (3, 6)
107 linerange1, skipped = filteredblocks(self.blocks, linerange2)
108 self.assertEqual(linerange1, (4, 7))
109 self.assertEqual(skipped, [True, False, True, True])
110
111 def testWithinEqualUpperboundOneLine(self):
112 """oneline-linerange at end of an "=" block"""
113 # IDX 0 1
114 # 012345678901
115 # SRC NNOOOONNNNNN (New/Old)
116 # ^
117 linerange2 = (5, 6)
118 linerange1, skipped = filteredblocks(self.blocks, linerange2)
119 self.assertEqual(linerange1, (6, 7))
120 self.assertEqual(skipped, [True, False, True, True])
121
122 def testWithinFirstBlockNeq(self):
123 """linerange within the first "!" block"""
124 # IDX 0 1
125 # 012345678901
126 # SRC NNOOOONNNNNN (New/Old)
127 # ^
128 # | (empty)
129 # ^
130 # ^^
131 for linerange2 in [
132 (0, 1),
133 (1, 1),
134 (1, 2),
135 (0, 2),
136 ]:
137 linerange1, skipped = filteredblocks(self.blocks, linerange2)
138 self.assertEqual(linerange1, (0, 3))
139 self.assertEqual(skipped, [False, True, True, True])
140
141 def testWithinLastBlockNeq(self):
142 """linerange within the last "!" block"""
143 # IDX 0 1
144 # 012345678901
145 # SRC NNOOOONNNNNN (New/Old)
146 # ^
147 # ^
148 # | (empty)
149 # ^^^^^^
150 # ^
151 for linerange2 in [
152 (6, 7),
153 (7, 8),
154 (7, 7),
155 (6, 12),
156 (11, 12),
157 ]:
158 linerange1, skipped = filteredblocks(self.blocks, linerange2)
159 self.assertEqual(linerange1, (7, 12))
160 self.assertEqual(skipped, [True, True, False, True])
161
162 def testAccrossTwoBlocks(self):
163 """linerange accross two blocks"""
164 # IDX 0 1
165 # 012345678901
166 # SRC NNOOOONNNNNN (New/Old)
167 # ^^^^
168 linerange2 = (1, 5)
169 linerange1, skipped = filteredblocks(self.blocks, linerange2)
170 self.assertEqual(linerange1, (0, 6))
171 self.assertEqual(skipped, [False, False, True, True])
172
173 def testCrossingSeveralBlocks(self):
174 """linerange accross three blocks"""
175 # IDX 0 1
176 # 012345678901
177 # SRC NNOOOONNNNNN (New/Old)
178 # ^^^^^^^
179 linerange2 = (1, 8)
180 linerange1, skipped = filteredblocks(self.blocks, linerange2)
181 self.assertEqual(linerange1, (0, 12))
182 self.assertEqual(skipped, [False, False, False, True])
183
184 def testStartInEqBlock(self):
185 """linerange starting in an "=" block"""
186 # IDX 0 1
187 # 012345678901
188 # SRC NNOOOONNNNNN (New/Old)
189 # ^^^^
190 # ^^^^^^^
191 for linerange2, expectedlinerange1 in [
192 ((5, 9), (6, 12)),
193 ((4, 11), (5, 12)),
194 ]:
195 linerange1, skipped = filteredblocks(self.blocks, linerange2)
196 self.assertEqual(linerange1, expectedlinerange1)
197 self.assertEqual(skipped, [True, False, False, True])
198
199 def testEndInEqBlock(self):
200 """linerange ending in an "=" block"""
201 # IDX 0 1
202 # 012345678901
203 # SRC NNOOOONNNNNN (New/Old)
204 # ^^
205 # ^^^^^
206 for linerange2, expectedlinerange1 in [
207 ((1, 3), (0, 4)),
208 ((0, 4), (0, 5)),
209 ]:
210 linerange1, skipped = filteredblocks(self.blocks, linerange2)
211 self.assertEqual(linerange1, expectedlinerange1)
212 self.assertEqual(skipped, [False, False, True, True])
213
214 def testOutOfRange(self):
215 """linerange exceeding file size"""
216 exctype = error.Abort
217 for linerange2 in [
218 (0, 34),
219 (15, 12),
220 ]:
221 # Could be `with self.assertRaises(error.Abort)` but python2.6
222 # does not have assertRaises context manager.
223 try:
224 mdiff.blocksinrange(self.blocks, linerange2)
225 except exctype as exc:
226 self.assertTrue('line range exceeds file size' in str(exc))
227 else:
228 self.fail('%s not raised' % exctype.__name__)
229
230 if __name__ == '__main__':
231 import silenttestrunner
232 silenttestrunner.main(__name__)
@@ -113,6 +113,45 b' def splitblock(base1, lines1, base2, lin'
113 s1 = i1
113 s1 = i1
114 s2 = i2
114 s2 = i2
115
115
116 def blocksinrange(blocks, rangeb):
117 """filter `blocks` like (a1, a2, b1, b2) from items outside line range
118 `rangeb` from ``(b1, b2)`` point of view.
119
120 Return `filteredblocks, rangea` where:
121
122 * `filteredblocks` is list of ``block = (a1, a2, b1, b2), stype`` items of
123 `blocks` that are inside `rangeb` from ``(b1, b2)`` point of view; a
124 block ``(b1, b2)`` being inside `rangeb` if
125 ``rangeb[0] < b2 and b1 < rangeb[1]``;
126 * `rangea` is the line range w.r.t. to ``(a1, a2)`` parts of `blocks`.
127 """
128 lbb, ubb = rangeb
129 lba, uba = None, None
130 filteredblocks = []
131 for block in blocks:
132 (a1, a2, b1, b2), stype = block
133 if lbb >= b1 and ubb <= b2 and stype == '=':
134 # rangeb is within a single "=" hunk, restrict back linerange1
135 # by offsetting rangeb
136 lba = lbb - b1 + a1
137 uba = ubb - b1 + a1
138 else:
139 if b1 <= lbb < b2:
140 if stype == '=':
141 lba = a2 - (b2 - lbb)
142 else:
143 lba = a1
144 if b1 < ubb <= b2:
145 if stype == '=':
146 uba = a1 + (ubb - b1)
147 else:
148 uba = a2
149 if lbb < b2 and b1 < ubb:
150 filteredblocks.append(block)
151 if lba is None or uba is None or uba < lba:
152 raise error.Abort(_('line range exceeds file size'))
153 return filteredblocks, (lba, uba)
154
116 def allblocks(text1, text2, opts=None, lines1=None, lines2=None):
155 def allblocks(text1, text2, opts=None, lines1=None, lines2=None):
117 """Return (block, type) tuples, where block is an mdiff.blocks
156 """Return (block, type) tuples, where block is an mdiff.blocks
118 line entry. type is '=' for blocks matching exactly one another
157 line entry. type is '=' for blocks matching exactly one another
General Comments 0
You need to be logged in to leave comments. Login now