]>
Commit | Line | Data |
---|---|---|
4710c53d | 1 | import difflib\r |
2 | from test.test_support import run_unittest, findfile\r | |
3 | import unittest\r | |
4 | import doctest\r | |
5 | import sys\r | |
6 | \r | |
7 | \r | |
8 | class TestWithAscii(unittest.TestCase):\r | |
9 | def test_one_insert(self):\r | |
10 | sm = difflib.SequenceMatcher(None, 'b' * 100, 'a' + 'b' * 100)\r | |
11 | self.assertAlmostEqual(sm.ratio(), 0.995, places=3)\r | |
12 | self.assertEqual(list(sm.get_opcodes()),\r | |
13 | [ ('insert', 0, 0, 0, 1),\r | |
14 | ('equal', 0, 100, 1, 101)])\r | |
15 | sm = difflib.SequenceMatcher(None, 'b' * 100, 'b' * 50 + 'a' + 'b' * 50)\r | |
16 | self.assertAlmostEqual(sm.ratio(), 0.995, places=3)\r | |
17 | self.assertEqual(list(sm.get_opcodes()),\r | |
18 | [ ('equal', 0, 50, 0, 50),\r | |
19 | ('insert', 50, 50, 50, 51),\r | |
20 | ('equal', 50, 100, 51, 101)])\r | |
21 | \r | |
22 | def test_one_delete(self):\r | |
23 | sm = difflib.SequenceMatcher(None, 'a' * 40 + 'c' + 'b' * 40, 'a' * 40 + 'b' * 40)\r | |
24 | self.assertAlmostEqual(sm.ratio(), 0.994, places=3)\r | |
25 | self.assertEqual(list(sm.get_opcodes()),\r | |
26 | [ ('equal', 0, 40, 0, 40),\r | |
27 | ('delete', 40, 41, 40, 40),\r | |
28 | ('equal', 41, 81, 40, 80)])\r | |
29 | \r | |
30 | \r | |
31 | class TestAutojunk(unittest.TestCase):\r | |
32 | """Tests for the autojunk parameter added in 2.7"""\r | |
33 | def test_one_insert_homogenous_sequence(self):\r | |
34 | # By default autojunk=True and the heuristic kicks in for a sequence\r | |
35 | # of length 200+\r | |
36 | seq1 = 'b' * 200\r | |
37 | seq2 = 'a' + 'b' * 200\r | |
38 | \r | |
39 | sm = difflib.SequenceMatcher(None, seq1, seq2)\r | |
40 | self.assertAlmostEqual(sm.ratio(), 0, places=3)\r | |
41 | \r | |
42 | # Now turn the heuristic off\r | |
43 | sm = difflib.SequenceMatcher(None, seq1, seq2, autojunk=False)\r | |
44 | self.assertAlmostEqual(sm.ratio(), 0.9975, places=3)\r | |
45 | \r | |
46 | \r | |
47 | class TestSFbugs(unittest.TestCase):\r | |
48 | def test_ratio_for_null_seqn(self):\r | |
49 | # Check clearing of SF bug 763023\r | |
50 | s = difflib.SequenceMatcher(None, [], [])\r | |
51 | self.assertEqual(s.ratio(), 1)\r | |
52 | self.assertEqual(s.quick_ratio(), 1)\r | |
53 | self.assertEqual(s.real_quick_ratio(), 1)\r | |
54 | \r | |
55 | def test_comparing_empty_lists(self):\r | |
56 | # Check fix for bug #979794\r | |
57 | group_gen = difflib.SequenceMatcher(None, [], []).get_grouped_opcodes()\r | |
58 | self.assertRaises(StopIteration, group_gen.next)\r | |
59 | diff_gen = difflib.unified_diff([], [])\r | |
60 | self.assertRaises(StopIteration, diff_gen.next)\r | |
61 | \r | |
62 | def test_added_tab_hint(self):\r | |
63 | # Check fix for bug #1488943\r | |
64 | diff = list(difflib.Differ().compare(["\tI am a buggy"],["\t\tI am a bug"]))\r | |
65 | self.assertEqual("- \tI am a buggy", diff[0])\r | |
66 | self.assertEqual("? --\n", diff[1])\r | |
67 | self.assertEqual("+ \t\tI am a bug", diff[2])\r | |
68 | self.assertEqual("? +\n", diff[3])\r | |
69 | \r | |
70 | patch914575_from1 = """\r | |
71 | 1. Beautiful is beTTer than ugly.\r | |
72 | 2. Explicit is better than implicit.\r | |
73 | 3. Simple is better than complex.\r | |
74 | 4. Complex is better than complicated.\r | |
75 | """\r | |
76 | \r | |
77 | patch914575_to1 = """\r | |
78 | 1. Beautiful is better than ugly.\r | |
79 | 3. Simple is better than complex.\r | |
80 | 4. Complicated is better than complex.\r | |
81 | 5. Flat is better than nested.\r | |
82 | """\r | |
83 | \r | |
84 | patch914575_from2 = """\r | |
85 | \t\tLine 1: preceeded by from:[tt] to:[ssss]\r | |
86 | \t\tLine 2: preceeded by from:[sstt] to:[sssst]\r | |
87 | \t \tLine 3: preceeded by from:[sstst] to:[ssssss]\r | |
88 | Line 4: \thas from:[sst] to:[sss] after :\r | |
89 | Line 5: has from:[t] to:[ss] at end\t\r | |
90 | """\r | |
91 | \r | |
92 | patch914575_to2 = """\r | |
93 | Line 1: preceeded by from:[tt] to:[ssss]\r | |
94 | \tLine 2: preceeded by from:[sstt] to:[sssst]\r | |
95 | Line 3: preceeded by from:[sstst] to:[ssssss]\r | |
96 | Line 4: has from:[sst] to:[sss] after :\r | |
97 | Line 5: has from:[t] to:[ss] at end\r | |
98 | """\r | |
99 | \r | |
100 | patch914575_from3 = """line 0\r | |
101 | 1234567890123456789012345689012345\r | |
102 | line 1\r | |
103 | line 2\r | |
104 | line 3\r | |
105 | line 4 changed\r | |
106 | line 5 changed\r | |
107 | line 6 changed\r | |
108 | line 7\r | |
109 | line 8 subtracted\r | |
110 | line 9\r | |
111 | 1234567890123456789012345689012345\r | |
112 | short line\r | |
113 | just fits in!!\r | |
114 | just fits in two lines yup!!\r | |
115 | the end"""\r | |
116 | \r | |
117 | patch914575_to3 = """line 0\r | |
118 | 1234567890123456789012345689012345\r | |
119 | line 1\r | |
120 | line 2 added\r | |
121 | line 3\r | |
122 | line 4 chanGEd\r | |
123 | line 5a chanGed\r | |
124 | line 6a changEd\r | |
125 | line 7\r | |
126 | line 8\r | |
127 | line 9\r | |
128 | 1234567890\r | |
129 | another long line that needs to be wrapped\r | |
130 | just fitS in!!\r | |
131 | just fits in two lineS yup!!\r | |
132 | the end"""\r | |
133 | \r | |
134 | class TestSFpatches(unittest.TestCase):\r | |
135 | \r | |
136 | def test_html_diff(self):\r | |
137 | # Check SF patch 914575 for generating HTML differences\r | |
138 | f1a = ((patch914575_from1 + '123\n'*10)*3)\r | |
139 | t1a = (patch914575_to1 + '123\n'*10)*3\r | |
140 | f1b = '456\n'*10 + f1a\r | |
141 | t1b = '456\n'*10 + t1a\r | |
142 | f1a = f1a.splitlines()\r | |
143 | t1a = t1a.splitlines()\r | |
144 | f1b = f1b.splitlines()\r | |
145 | t1b = t1b.splitlines()\r | |
146 | f2 = patch914575_from2.splitlines()\r | |
147 | t2 = patch914575_to2.splitlines()\r | |
148 | f3 = patch914575_from3\r | |
149 | t3 = patch914575_to3\r | |
150 | i = difflib.HtmlDiff()\r | |
151 | j = difflib.HtmlDiff(tabsize=2)\r | |
152 | k = difflib.HtmlDiff(wrapcolumn=14)\r | |
153 | \r | |
154 | full = i.make_file(f1a,t1a,'from','to',context=False,numlines=5)\r | |
155 | tables = '\n'.join(\r | |
156 | [\r | |
157 | '<h2>Context (first diff within numlines=5(default))</h2>',\r | |
158 | i.make_table(f1a,t1a,'from','to',context=True),\r | |
159 | '<h2>Context (first diff after numlines=5(default))</h2>',\r | |
160 | i.make_table(f1b,t1b,'from','to',context=True),\r | |
161 | '<h2>Context (numlines=6)</h2>',\r | |
162 | i.make_table(f1a,t1a,'from','to',context=True,numlines=6),\r | |
163 | '<h2>Context (numlines=0)</h2>',\r | |
164 | i.make_table(f1a,t1a,'from','to',context=True,numlines=0),\r | |
165 | '<h2>Same Context</h2>',\r | |
166 | i.make_table(f1a,f1a,'from','to',context=True),\r | |
167 | '<h2>Same Full</h2>',\r | |
168 | i.make_table(f1a,f1a,'from','to',context=False),\r | |
169 | '<h2>Empty Context</h2>',\r | |
170 | i.make_table([],[],'from','to',context=True),\r | |
171 | '<h2>Empty Full</h2>',\r | |
172 | i.make_table([],[],'from','to',context=False),\r | |
173 | '<h2>tabsize=2</h2>',\r | |
174 | j.make_table(f2,t2),\r | |
175 | '<h2>tabsize=default</h2>',\r | |
176 | i.make_table(f2,t2),\r | |
177 | '<h2>Context (wrapcolumn=14,numlines=0)</h2>',\r | |
178 | k.make_table(f3.splitlines(),t3.splitlines(),context=True,numlines=0),\r | |
179 | '<h2>wrapcolumn=14,splitlines()</h2>',\r | |
180 | k.make_table(f3.splitlines(),t3.splitlines()),\r | |
181 | '<h2>wrapcolumn=14,splitlines(True)</h2>',\r | |
182 | k.make_table(f3.splitlines(True),t3.splitlines(True)),\r | |
183 | ])\r | |
184 | actual = full.replace('</body>','\n%s\n</body>' % tables)\r | |
185 | \r | |
186 | # temporarily uncomment next two lines to baseline this test\r | |
187 | #with open('test_difflib_expect.html','w') as fp:\r | |
188 | # fp.write(actual)\r | |
189 | \r | |
190 | with open(findfile('test_difflib_expect.html')) as fp:\r | |
191 | self.assertEqual(actual, fp.read())\r | |
192 | \r | |
193 | def test_recursion_limit(self):\r | |
194 | # Check if the problem described in patch #1413711 exists.\r | |
195 | limit = sys.getrecursionlimit()\r | |
196 | old = [(i%2 and "K:%d" or "V:A:%d") % i for i in range(limit*2)]\r | |
197 | new = [(i%2 and "K:%d" or "V:B:%d") % i for i in range(limit*2)]\r | |
198 | difflib.SequenceMatcher(None, old, new).get_opcodes()\r | |
199 | \r | |
200 | \r | |
201 | class TestOutputFormat(unittest.TestCase):\r | |
202 | def test_tab_delimiter(self):\r | |
203 | args = ['one', 'two', 'Original', 'Current',\r | |
204 | '2005-01-26 23:30:50', '2010-04-02 10:20:52']\r | |
205 | ud = difflib.unified_diff(*args, lineterm='')\r | |
206 | self.assertEqual(list(ud)[0:2], [\r | |
207 | "--- Original\t2005-01-26 23:30:50",\r | |
208 | "+++ Current\t2010-04-02 10:20:52"])\r | |
209 | cd = difflib.context_diff(*args, lineterm='')\r | |
210 | self.assertEqual(list(cd)[0:2], [\r | |
211 | "*** Original\t2005-01-26 23:30:50",\r | |
212 | "--- Current\t2010-04-02 10:20:52"])\r | |
213 | \r | |
214 | def test_no_trailing_tab_on_empty_filedate(self):\r | |
215 | args = ['one', 'two', 'Original', 'Current']\r | |
216 | ud = difflib.unified_diff(*args, lineterm='')\r | |
217 | self.assertEqual(list(ud)[0:2], ["--- Original", "+++ Current"])\r | |
218 | \r | |
219 | cd = difflib.context_diff(*args, lineterm='')\r | |
220 | self.assertEqual(list(cd)[0:2], ["*** Original", "--- Current"])\r | |
221 | \r | |
222 | def test_range_format_unified(self):\r | |
223 | # Per the diff spec at http://www.unix.org/single_unix_specification/\r | |
224 | spec = '''\\r | |
225 | Each <range> field shall be of the form:\r | |
226 | %1d", <beginning line number> if the range contains exactly one line,\r | |
227 | and:\r | |
228 | "%1d,%1d", <beginning line number>, <number of lines> otherwise.\r | |
229 | If a range is empty, its beginning line number shall be the number of\r | |
230 | the line just before the range, or 0 if the empty range starts the file.\r | |
231 | '''\r | |
232 | fmt = difflib._format_range_unified\r | |
233 | self.assertEqual(fmt(3,3), '3,0')\r | |
234 | self.assertEqual(fmt(3,4), '4')\r | |
235 | self.assertEqual(fmt(3,5), '4,2')\r | |
236 | self.assertEqual(fmt(3,6), '4,3')\r | |
237 | self.assertEqual(fmt(0,0), '0,0')\r | |
238 | \r | |
239 | def test_range_format_context(self):\r | |
240 | # Per the diff spec at http://www.unix.org/single_unix_specification/\r | |
241 | spec = '''\\r | |
242 | The range of lines in file1 shall be written in the following format\r | |
243 | if the range contains two or more lines:\r | |
244 | "*** %d,%d ****\n", <beginning line number>, <ending line number>\r | |
245 | and the following format otherwise:\r | |
246 | "*** %d ****\n", <ending line number>\r | |
247 | The ending line number of an empty range shall be the number of the preceding line,\r | |
248 | or 0 if the range is at the start of the file.\r | |
249 | \r | |
250 | Next, the range of lines in file2 shall be written in the following format\r | |
251 | if the range contains two or more lines:\r | |
252 | "--- %d,%d ----\n", <beginning line number>, <ending line number>\r | |
253 | and the following format otherwise:\r | |
254 | "--- %d ----\n", <ending line number>\r | |
255 | '''\r | |
256 | fmt = difflib._format_range_context\r | |
257 | self.assertEqual(fmt(3,3), '3')\r | |
258 | self.assertEqual(fmt(3,4), '4')\r | |
259 | self.assertEqual(fmt(3,5), '4,5')\r | |
260 | self.assertEqual(fmt(3,6), '4,6')\r | |
261 | self.assertEqual(fmt(0,0), '0')\r | |
262 | \r | |
263 | \r | |
264 | def test_main():\r | |
265 | difflib.HtmlDiff._default_prefix = 0\r | |
266 | Doctests = doctest.DocTestSuite(difflib)\r | |
267 | run_unittest(\r | |
268 | TestWithAscii, TestAutojunk, TestSFpatches, TestSFbugs,\r | |
269 | TestOutputFormat, Doctests)\r | |
270 | \r | |
271 | if __name__ == '__main__':\r | |
272 | test_main()\r |