blob: dc2abe874af1d18dca3cd3b43e0bd6b173c1318e [file] [log] [blame]
Barry Warsaw04f357c2002-07-23 19:04:11 +00001import difflib
Benjamin Petersonee8712c2008-05-20 21:35:26 +00002from test.support import run_unittest, findfile
Neal Norwitze7dfe212003-07-01 14:59:46 +00003import unittest
Raymond Hettinger43d790c2003-07-16 04:34:56 +00004import doctest
Gustavo Niemeyer548148812006-01-31 18:34:13 +00005import sys
Neal Norwitze7dfe212003-07-01 14:59:46 +00006
7class TestSFbugs(unittest.TestCase):
8
9 def test_ratio_for_null_seqn(self):
10 # Check clearing of SF bug 763023
11 s = difflib.SequenceMatcher(None, [], [])
12 self.assertEqual(s.ratio(), 1)
13 self.assertEqual(s.quick_ratio(), 1)
14 self.assertEqual(s.real_quick_ratio(), 1)
15
Brett Cannond2c5b4b2004-07-10 23:54:07 +000016 def test_comparing_empty_lists(self):
17 # Check fix for bug #979794
18 group_gen = difflib.SequenceMatcher(None, [], []).get_grouped_opcodes()
Georg Brandla18af4e2007-04-21 15:47:16 +000019 self.assertRaises(StopIteration, next, group_gen)
Brett Cannond2c5b4b2004-07-10 23:54:07 +000020 diff_gen = difflib.unified_diff([], [])
Georg Brandla18af4e2007-04-21 15:47:16 +000021 self.assertRaises(StopIteration, next, diff_gen)
Brett Cannond2c5b4b2004-07-10 23:54:07 +000022
Senthil Kumarand884f8a2009-11-23 19:06:11 +000023 def test_added_tab_hint(self):
24 # Check fix for bug #1488943
25 diff = list(difflib.Differ().compare(["\tI am a buggy"],["\t\tI am a bug"]))
26 self.assertEqual("- \tI am a buggy", diff[0])
27 self.assertEqual("? --\n", diff[1])
28 self.assertEqual("+ \t\tI am a bug", diff[2])
29 self.assertEqual("? +\n", diff[3])
30
Martin v. Löwise064b412004-08-29 16:34:40 +000031patch914575_from1 = """
32 1. Beautiful is beTTer than ugly.
33 2. Explicit is better than implicit.
34 3. Simple is better than complex.
35 4. Complex is better than complicated.
36"""
37
38patch914575_to1 = """
39 1. Beautiful is better than ugly.
40 3. Simple is better than complex.
41 4. Complicated is better than complex.
42 5. Flat is better than nested.
43"""
44
45patch914575_from2 = """
46\t\tLine 1: preceeded by from:[tt] to:[ssss]
47 \t\tLine 2: preceeded by from:[sstt] to:[sssst]
48 \t \tLine 3: preceeded by from:[sstst] to:[ssssss]
49Line 4: \thas from:[sst] to:[sss] after :
50Line 5: has from:[t] to:[ss] at end\t
51"""
52
53patch914575_to2 = """
54 Line 1: preceeded by from:[tt] to:[ssss]
55 \tLine 2: preceeded by from:[sstt] to:[sssst]
56 Line 3: preceeded by from:[sstst] to:[ssssss]
57Line 4: has from:[sst] to:[sss] after :
Tim Peters48bd7f32004-08-29 22:38:38 +000058Line 5: has from:[t] to:[ss] at end
Martin v. Löwise064b412004-08-29 16:34:40 +000059"""
60
61patch914575_from3 = """line 0
621234567890123456789012345689012345
63line 1
64line 2
65line 3
Tim Peters48bd7f32004-08-29 22:38:38 +000066line 4 changed
67line 5 changed
68line 6 changed
Martin v. Löwise064b412004-08-29 16:34:40 +000069line 7
70line 8 subtracted
71line 9
721234567890123456789012345689012345
73short line
74just fits in!!
75just fits in two lines yup!!
76the end"""
77
78patch914575_to3 = """line 0
791234567890123456789012345689012345
80line 1
81line 2 added
82line 3
Tim Peters48bd7f32004-08-29 22:38:38 +000083line 4 chanGEd
84line 5a chanGed
85line 6a changEd
Martin v. Löwise064b412004-08-29 16:34:40 +000086line 7
87line 8
88line 9
891234567890
90another long line that needs to be wrapped
91just fitS in!!
92just fits in two lineS yup!!
93the end"""
94
95class TestSFpatches(unittest.TestCase):
96
97 def test_html_diff(self):
98 # Check SF patch 914575 for generating HTML differences
99 f1a = ((patch914575_from1 + '123\n'*10)*3)
100 t1a = (patch914575_to1 + '123\n'*10)*3
101 f1b = '456\n'*10 + f1a
102 t1b = '456\n'*10 + t1a
103 f1a = f1a.splitlines()
104 t1a = t1a.splitlines()
105 f1b = f1b.splitlines()
106 t1b = t1b.splitlines()
107 f2 = patch914575_from2.splitlines()
108 t2 = patch914575_to2.splitlines()
109 f3 = patch914575_from3
110 t3 = patch914575_to3
111 i = difflib.HtmlDiff()
112 j = difflib.HtmlDiff(tabsize=2)
113 k = difflib.HtmlDiff(wrapcolumn=14)
Tim Peters48bd7f32004-08-29 22:38:38 +0000114
Martin v. Löwise064b412004-08-29 16:34:40 +0000115 full = i.make_file(f1a,t1a,'from','to',context=False,numlines=5)
116 tables = '\n'.join(
117 [
Tim Peters48bd7f32004-08-29 22:38:38 +0000118 '<h2>Context (first diff within numlines=5(default))</h2>',
Martin v. Löwise064b412004-08-29 16:34:40 +0000119 i.make_table(f1a,t1a,'from','to',context=True),
Tim Peters48bd7f32004-08-29 22:38:38 +0000120 '<h2>Context (first diff after numlines=5(default))</h2>',
Martin v. Löwise064b412004-08-29 16:34:40 +0000121 i.make_table(f1b,t1b,'from','to',context=True),
Tim Peters48bd7f32004-08-29 22:38:38 +0000122 '<h2>Context (numlines=6)</h2>',
Martin v. Löwise064b412004-08-29 16:34:40 +0000123 i.make_table(f1a,t1a,'from','to',context=True,numlines=6),
Tim Peters48bd7f32004-08-29 22:38:38 +0000124 '<h2>Context (numlines=0)</h2>',
Martin v. Löwise064b412004-08-29 16:34:40 +0000125 i.make_table(f1a,t1a,'from','to',context=True,numlines=0),
Tim Peters48bd7f32004-08-29 22:38:38 +0000126 '<h2>Same Context</h2>',
Martin v. Löwise064b412004-08-29 16:34:40 +0000127 i.make_table(f1a,f1a,'from','to',context=True),
Tim Peters48bd7f32004-08-29 22:38:38 +0000128 '<h2>Same Full</h2>',
Martin v. Löwise064b412004-08-29 16:34:40 +0000129 i.make_table(f1a,f1a,'from','to',context=False),
130 '<h2>Empty Context</h2>',
131 i.make_table([],[],'from','to',context=True),
132 '<h2>Empty Full</h2>',
133 i.make_table([],[],'from','to',context=False),
134 '<h2>tabsize=2</h2>',
135 j.make_table(f2,t2),
136 '<h2>tabsize=default</h2>',
137 i.make_table(f2,t2),
138 '<h2>Context (wrapcolumn=14,numlines=0)</h2>',
139 k.make_table(f3.splitlines(),t3.splitlines(),context=True,numlines=0),
140 '<h2>wrapcolumn=14,splitlines()</h2>',
141 k.make_table(f3.splitlines(),t3.splitlines()),
142 '<h2>wrapcolumn=14,splitlines(True)</h2>',
143 k.make_table(f3.splitlines(True),t3.splitlines(True)),
144 ])
145 actual = full.replace('</body>','\n%s\n</body>' % tables)
Tim Peters48bd7f32004-08-29 22:38:38 +0000146
Philip Jenveya27c5bd2009-05-28 06:09:08 +0000147 # temporarily uncomment next two lines to baseline this test
148 #with open('test_difflib_expect.html','w') as fp:
149 # fp.write(actual)
Tim Peters48bd7f32004-08-29 22:38:38 +0000150
Philip Jenveya27c5bd2009-05-28 06:09:08 +0000151 with open(findfile('test_difflib_expect.html')) as fp:
152 self.assertEqual(actual, fp.read())
Martin v. Löwise064b412004-08-29 16:34:40 +0000153
Gustavo Niemeyer548148812006-01-31 18:34:13 +0000154 def test_recursion_limit(self):
155 # Check if the problem described in patch #1413711 exists.
156 limit = sys.getrecursionlimit()
157 old = [(i%2 and "K:%d" or "V:A:%d") % i for i in range(limit*2)]
158 new = [(i%2 and "K:%d" or "V:B:%d") % i for i in range(limit*2)]
159 difflib.SequenceMatcher(None, old, new).get_opcodes()
160
161
Raymond Hettinger37805422011-04-12 15:14:12 -0700162class TestOutputFormat(unittest.TestCase):
163 def test_tab_delimiter(self):
164 args = ['one', 'two', 'Original', 'Current',
165 '2005-01-26 23:30:50', '2010-04-02 10:20:52']
166 ud = difflib.unified_diff(*args, lineterm='')
167 self.assertEqual(list(ud)[0:2], [
168 "--- Original\t2005-01-26 23:30:50",
169 "+++ Current\t2010-04-02 10:20:52"])
170 cd = difflib.context_diff(*args, lineterm='')
171 self.assertEqual(list(cd)[0:2], [
172 "*** Original\t2005-01-26 23:30:50",
173 "--- Current\t2010-04-02 10:20:52"])
174
175 def test_no_trailing_tab_on_empty_filedate(self):
176 args = ['one', 'two', 'Original', 'Current']
177 ud = difflib.unified_diff(*args, lineterm='')
178 self.assertEqual(list(ud)[0:2], ["--- Original", "+++ Current"])
179
180 cd = difflib.context_diff(*args, lineterm='')
181 self.assertEqual(list(cd)[0:2], ["*** Original", "--- Current"])
182
183 def test_range_format_unified(self):
184 # Per the diff spec at http://www.unix.org/single_unix_specification/
185 spec = '''\
186 Each <range> field shall be of the form:
187 %1d", <beginning line number> if the range contains exactly one line,
188 and:
189 "%1d,%1d", <beginning line number>, <number of lines> otherwise.
190 If a range is empty, its beginning line number shall be the number of
191 the line just before the range, or 0 if the empty range starts the file.
192 '''
193 fmt = difflib._format_range_unified
194 self.assertEqual(fmt(3,3), '3,0')
195 self.assertEqual(fmt(3,4), '4')
196 self.assertEqual(fmt(3,5), '4,2')
197 self.assertEqual(fmt(3,6), '4,3')
198 self.assertEqual(fmt(0,0), '0,0')
199
200 def test_range_format_context(self):
201 # Per the diff spec at http://www.unix.org/single_unix_specification/
202 spec = '''\
203 The range of lines in file1 shall be written in the following format
204 if the range contains two or more lines:
205 "*** %d,%d ****\n", <beginning line number>, <ending line number>
206 and the following format otherwise:
207 "*** %d ****\n", <ending line number>
208 The ending line number of an empty range shall be the number of the preceding line,
209 or 0 if the range is at the start of the file.
210
211 Next, the range of lines in file2 shall be written in the following format
212 if the range contains two or more lines:
213 "--- %d,%d ----\n", <beginning line number>, <ending line number>
214 and the following format otherwise:
215 "--- %d ----\n", <ending line number>
216 '''
217 fmt = difflib._format_range_context
218 self.assertEqual(fmt(3,3), '3')
219 self.assertEqual(fmt(3,4), '4')
220 self.assertEqual(fmt(3,5), '4,5')
221 self.assertEqual(fmt(3,6), '4,6')
222 self.assertEqual(fmt(0,0), '0')
223
224
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000225def test_main():
226 difflib.HtmlDiff._default_prefix = 0
227 Doctests = doctest.DocTestSuite(difflib)
Raymond Hettinger37805422011-04-12 15:14:12 -0700228 run_unittest(TestSFpatches, TestSFbugs, Doctests, TestOutputFormat)
Raymond Hettinger43d790c2003-07-16 04:34:56 +0000229
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000230if __name__ == '__main__':
231 test_main()