1 | """
|
---|
2 | Tests for the mhlib module
|
---|
3 | Nick Mathewson
|
---|
4 | """
|
---|
5 |
|
---|
6 | ### BUG: This suite doesn't currently test the mime functionality of
|
---|
7 | ### mhlib. It should.
|
---|
8 |
|
---|
9 | import unittest
|
---|
10 | from test.test_support import run_unittest, TESTFN, import_module
|
---|
11 | import os, StringIO
|
---|
12 | import sys
|
---|
13 | mhlib = import_module('mhlib', deprecated=True)
|
---|
14 |
|
---|
15 | if (sys.platform.startswith("win") or sys.platform=="riscos" or
|
---|
16 | sys.platform.startswith("atheos")):
|
---|
17 | # mhlib.updateline() renames a file to the name of a file that already
|
---|
18 | # exists. That causes a reasonable OS <wink> to complain in test_sequence
|
---|
19 | # here, like the "OSError: [Errno 17] File exists" raised on Windows.
|
---|
20 | # mhlib's listsubfolders() and listallfolders() do something with
|
---|
21 | # link counts, and that causes test_listfolders() here to get back
|
---|
22 | # an empty list from its call of listallfolders().
|
---|
23 | # The other tests here pass on Windows.
|
---|
24 | raise unittest.SkipTest("skipped on %s -- " % sys.platform +
|
---|
25 | "too many Unix assumptions")
|
---|
26 |
|
---|
27 | _mhroot = TESTFN+"_MH"
|
---|
28 | _mhpath = os.path.join(_mhroot, "MH")
|
---|
29 | _mhprofile = os.path.join(_mhroot, ".mh_profile")
|
---|
30 |
|
---|
31 | def normF(f):
|
---|
32 | return os.path.join(*f.split('/'))
|
---|
33 |
|
---|
34 | def writeFile(fname, contents):
|
---|
35 | dir = os.path.split(fname)[0]
|
---|
36 | if dir and not os.path.exists(dir):
|
---|
37 | mkdirs(dir)
|
---|
38 | f = open(fname, 'w')
|
---|
39 | f.write(contents)
|
---|
40 | f.close()
|
---|
41 |
|
---|
42 | def readFile(fname):
|
---|
43 | f = open(fname)
|
---|
44 | r = f.read()
|
---|
45 | f.close()
|
---|
46 | return r
|
---|
47 |
|
---|
48 | def writeProfile(dict):
|
---|
49 | contents = [ "%s: %s\n" % (k, v) for k, v in dict.iteritems() ]
|
---|
50 | writeFile(_mhprofile, "".join(contents))
|
---|
51 |
|
---|
52 | def writeContext(folder):
|
---|
53 | folder = normF(folder)
|
---|
54 | writeFile(os.path.join(_mhpath, "context"),
|
---|
55 | "Current-Folder: %s\n" % folder)
|
---|
56 |
|
---|
57 | def writeCurMessage(folder, cur):
|
---|
58 | folder = normF(folder)
|
---|
59 | writeFile(os.path.join(_mhpath, folder, ".mh_sequences"),
|
---|
60 | "cur: %s\n"%cur)
|
---|
61 |
|
---|
62 | def writeMessage(folder, n, headers, body):
|
---|
63 | folder = normF(folder)
|
---|
64 | headers = "".join([ "%s: %s\n" % (k, v) for k, v in headers.iteritems() ])
|
---|
65 | contents = "%s\n%s\n" % (headers,body)
|
---|
66 | mkdirs(os.path.join(_mhpath, folder))
|
---|
67 | writeFile(os.path.join(_mhpath, folder, str(n)), contents)
|
---|
68 |
|
---|
69 | def getMH():
|
---|
70 | return mhlib.MH(os.path.abspath(_mhpath), _mhprofile)
|
---|
71 |
|
---|
72 | def sortLines(s):
|
---|
73 | lines = s.split("\n")
|
---|
74 | lines = [ line.strip() for line in lines if len(line) >= 2 ]
|
---|
75 | lines.sort()
|
---|
76 | return lines
|
---|
77 |
|
---|
78 | # These next 2 functions are copied from test_glob.py.
|
---|
79 | def mkdirs(fname):
|
---|
80 | if os.path.exists(fname) or fname == '':
|
---|
81 | return
|
---|
82 | base, file = os.path.split(fname)
|
---|
83 | mkdirs(base)
|
---|
84 | os.mkdir(fname)
|
---|
85 |
|
---|
86 | def deltree(fname):
|
---|
87 | if not os.path.exists(fname):
|
---|
88 | return
|
---|
89 | for f in os.listdir(fname):
|
---|
90 | fullname = os.path.join(fname, f)
|
---|
91 | if os.path.isdir(fullname):
|
---|
92 | deltree(fullname)
|
---|
93 | else:
|
---|
94 | try:
|
---|
95 | os.unlink(fullname)
|
---|
96 | except:
|
---|
97 | pass
|
---|
98 | try:
|
---|
99 | os.rmdir(fname)
|
---|
100 | except:
|
---|
101 | pass
|
---|
102 |
|
---|
103 | class MhlibTests(unittest.TestCase):
|
---|
104 | def setUp(self):
|
---|
105 | deltree(_mhroot)
|
---|
106 | mkdirs(_mhpath)
|
---|
107 | writeProfile({'Path' : os.path.abspath(_mhpath),
|
---|
108 | 'Editor': 'emacs',
|
---|
109 | 'ignored-attribute': 'camping holiday'})
|
---|
110 | # Note: These headers aren't really conformant to RFC822, but
|
---|
111 | # mhlib shouldn't care about that.
|
---|
112 |
|
---|
113 | # An inbox with a couple of messages.
|
---|
114 | writeMessage('inbox', 1,
|
---|
115 | {'From': 'Mrs. Premise',
|
---|
116 | 'To': 'Mrs. Conclusion',
|
---|
117 | 'Date': '18 July 2001'}, "Hullo, Mrs. Conclusion!\n")
|
---|
118 | writeMessage('inbox', 2,
|
---|
119 | {'From': 'Mrs. Conclusion',
|
---|
120 | 'To': 'Mrs. Premise',
|
---|
121 | 'Date': '29 July 2001'}, "Hullo, Mrs. Premise!\n")
|
---|
122 |
|
---|
123 | # A folder with many messages
|
---|
124 | for i in range(5, 101)+range(101, 201, 2):
|
---|
125 | writeMessage('wide', i,
|
---|
126 | {'From': 'nowhere', 'Subject': 'message #%s' % i},
|
---|
127 | "This is message number %s\n" % i)
|
---|
128 |
|
---|
129 | # A deeply nested folder
|
---|
130 | def deep(folder, n):
|
---|
131 | writeMessage(folder, n,
|
---|
132 | {'Subject': 'Message %s/%s' % (folder, n) },
|
---|
133 | "This is message number %s in %s\n" % (n, folder) )
|
---|
134 | deep('deep/f1', 1)
|
---|
135 | deep('deep/f1', 2)
|
---|
136 | deep('deep/f1', 3)
|
---|
137 | deep('deep/f2', 4)
|
---|
138 | deep('deep/f2', 6)
|
---|
139 | deep('deep', 3)
|
---|
140 | deep('deep/f2/f3', 1)
|
---|
141 | deep('deep/f2/f3', 2)
|
---|
142 |
|
---|
143 | def tearDown(self):
|
---|
144 | deltree(_mhroot)
|
---|
145 |
|
---|
146 | def test_basic(self):
|
---|
147 | writeContext('inbox')
|
---|
148 | writeCurMessage('inbox', 2)
|
---|
149 | mh = getMH()
|
---|
150 |
|
---|
151 | eq = self.assertEqual
|
---|
152 | eq(mh.getprofile('Editor'), 'emacs')
|
---|
153 | eq(mh.getprofile('not-set'), None)
|
---|
154 | eq(mh.getpath(), os.path.abspath(_mhpath))
|
---|
155 | eq(mh.getcontext(), 'inbox')
|
---|
156 |
|
---|
157 | mh.setcontext('wide')
|
---|
158 | eq(mh.getcontext(), 'wide')
|
---|
159 | eq(readFile(os.path.join(_mhpath, 'context')),
|
---|
160 | "Current-Folder: wide\n")
|
---|
161 |
|
---|
162 | mh.setcontext('inbox')
|
---|
163 |
|
---|
164 | inbox = mh.openfolder('inbox')
|
---|
165 | eq(inbox.getfullname(),
|
---|
166 | os.path.join(os.path.abspath(_mhpath), 'inbox'))
|
---|
167 | eq(inbox.getsequencesfilename(),
|
---|
168 | os.path.join(os.path.abspath(_mhpath), 'inbox', '.mh_sequences'))
|
---|
169 | eq(inbox.getmessagefilename(1),
|
---|
170 | os.path.join(os.path.abspath(_mhpath), 'inbox', '1'))
|
---|
171 |
|
---|
172 | def test_listfolders(self):
|
---|
173 | mh = getMH()
|
---|
174 | eq = self.assertEqual
|
---|
175 |
|
---|
176 | folders = mh.listfolders()
|
---|
177 | folders.sort()
|
---|
178 | eq(folders, ['deep', 'inbox', 'wide'])
|
---|
179 |
|
---|
180 | folders = mh.listallfolders()
|
---|
181 | folders.sort()
|
---|
182 | tfolders = map(normF, ['deep', 'deep/f1', 'deep/f2', 'deep/f2/f3',
|
---|
183 | 'inbox', 'wide'])
|
---|
184 | tfolders.sort()
|
---|
185 | eq(folders, tfolders)
|
---|
186 |
|
---|
187 | folders = mh.listsubfolders('deep')
|
---|
188 | folders.sort()
|
---|
189 | eq(folders, map(normF, ['deep/f1', 'deep/f2']))
|
---|
190 |
|
---|
191 | folders = mh.listallsubfolders('deep')
|
---|
192 | folders.sort()
|
---|
193 | eq(folders, map(normF, ['deep/f1', 'deep/f2', 'deep/f2/f3']))
|
---|
194 | eq(mh.listsubfolders(normF('deep/f2')), [normF('deep/f2/f3')])
|
---|
195 |
|
---|
196 | eq(mh.listsubfolders('inbox'), [])
|
---|
197 | eq(mh.listallsubfolders('inbox'), [])
|
---|
198 |
|
---|
199 | def test_sequence(self):
|
---|
200 | mh = getMH()
|
---|
201 | eq = self.assertEqual
|
---|
202 | writeCurMessage('wide', 55)
|
---|
203 |
|
---|
204 | f = mh.openfolder('wide')
|
---|
205 | all = f.listmessages()
|
---|
206 | eq(all, range(5, 101)+range(101, 201, 2))
|
---|
207 | eq(f.getcurrent(), 55)
|
---|
208 | f.setcurrent(99)
|
---|
209 | eq(readFile(os.path.join(_mhpath, 'wide', '.mh_sequences')),
|
---|
210 | 'cur: 99\n')
|
---|
211 |
|
---|
212 | def seqeq(seq, val):
|
---|
213 | eq(f.parsesequence(seq), val)
|
---|
214 |
|
---|
215 | seqeq('5-55', range(5, 56))
|
---|
216 | seqeq('90-108', range(90, 101)+range(101, 109, 2))
|
---|
217 | seqeq('90-108', range(90, 101)+range(101, 109, 2))
|
---|
218 |
|
---|
219 | seqeq('10:10', range(10, 20))
|
---|
220 | seqeq('10:+10', range(10, 20))
|
---|
221 | seqeq('101:10', range(101, 121, 2))
|
---|
222 |
|
---|
223 | seqeq('cur', [99])
|
---|
224 | seqeq('.', [99])
|
---|
225 | seqeq('prev', [98])
|
---|
226 | seqeq('next', [100])
|
---|
227 | seqeq('cur:-3', [97, 98, 99])
|
---|
228 | seqeq('first-cur', range(5, 100))
|
---|
229 | seqeq('150-last', range(151, 201, 2))
|
---|
230 | seqeq('prev-next', [98, 99, 100])
|
---|
231 |
|
---|
232 | lowprimes = [5, 7, 11, 13, 17, 19, 23, 29]
|
---|
233 | lowcompos = [x for x in range(5, 31) if not x in lowprimes ]
|
---|
234 | f.putsequences({'cur': [5],
|
---|
235 | 'lowprime': lowprimes,
|
---|
236 | 'lowcompos': lowcompos})
|
---|
237 | seqs = readFile(os.path.join(_mhpath, 'wide', '.mh_sequences'))
|
---|
238 | seqs = sortLines(seqs)
|
---|
239 | eq(seqs, ["cur: 5",
|
---|
240 | "lowcompos: 6 8-10 12 14-16 18 20-22 24-28 30",
|
---|
241 | "lowprime: 5 7 11 13 17 19 23 29"])
|
---|
242 |
|
---|
243 | seqeq('lowprime', lowprimes)
|
---|
244 | seqeq('lowprime:1', [5])
|
---|
245 | seqeq('lowprime:2', [5, 7])
|
---|
246 | seqeq('lowprime:-2', [23, 29])
|
---|
247 |
|
---|
248 | ## Not supported
|
---|
249 | #seqeq('lowprime:first', [5])
|
---|
250 | #seqeq('lowprime:last', [29])
|
---|
251 | #seqeq('lowprime:prev', [29])
|
---|
252 | #seqeq('lowprime:next', [29])
|
---|
253 |
|
---|
254 | def test_modify(self):
|
---|
255 | mh = getMH()
|
---|
256 | eq = self.assertEqual
|
---|
257 |
|
---|
258 | mh.makefolder("dummy1")
|
---|
259 | self.assertIn("dummy1", mh.listfolders())
|
---|
260 | path = os.path.join(_mhpath, "dummy1")
|
---|
261 | self.assertTrue(os.path.exists(path))
|
---|
262 |
|
---|
263 | f = mh.openfolder('dummy1')
|
---|
264 | def create(n):
|
---|
265 | msg = "From: foo\nSubject: %s\n\nDummy Message %s\n" % (n,n)
|
---|
266 | f.createmessage(n, StringIO.StringIO(msg))
|
---|
267 |
|
---|
268 | create(7)
|
---|
269 | create(8)
|
---|
270 | create(9)
|
---|
271 |
|
---|
272 | eq(readFile(f.getmessagefilename(9)),
|
---|
273 | "From: foo\nSubject: 9\n\nDummy Message 9\n")
|
---|
274 |
|
---|
275 | eq(f.listmessages(), [7, 8, 9])
|
---|
276 | files = os.listdir(path)
|
---|
277 | files.sort()
|
---|
278 | eq(files, ['7', '8', '9'])
|
---|
279 |
|
---|
280 | f.removemessages(['7', '8'])
|
---|
281 | files = os.listdir(path)
|
---|
282 | files.sort()
|
---|
283 | eq(files, [',7', ',8', '9'])
|
---|
284 | eq(f.listmessages(), [9])
|
---|
285 | create(10)
|
---|
286 | create(11)
|
---|
287 | create(12)
|
---|
288 |
|
---|
289 | mh.makefolder("dummy2")
|
---|
290 | f2 = mh.openfolder("dummy2")
|
---|
291 | eq(f2.listmessages(), [])
|
---|
292 | f.movemessage(10, f2, 3)
|
---|
293 | f.movemessage(11, f2, 5)
|
---|
294 | eq(f.listmessages(), [9, 12])
|
---|
295 | eq(f2.listmessages(), [3, 5])
|
---|
296 | eq(readFile(f2.getmessagefilename(3)),
|
---|
297 | "From: foo\nSubject: 10\n\nDummy Message 10\n")
|
---|
298 |
|
---|
299 | f.copymessage(9, f2, 4)
|
---|
300 | eq(f.listmessages(), [9, 12])
|
---|
301 | eq(readFile(f2.getmessagefilename(4)),
|
---|
302 | "From: foo\nSubject: 9\n\nDummy Message 9\n")
|
---|
303 |
|
---|
304 | f.refilemessages([9, 12], f2)
|
---|
305 | eq(f.listmessages(), [])
|
---|
306 | eq(f2.listmessages(), [3, 4, 5, 6, 7])
|
---|
307 | eq(readFile(f2.getmessagefilename(7)),
|
---|
308 | "From: foo\nSubject: 12\n\nDummy Message 12\n")
|
---|
309 | # XXX This should check that _copysequences does the right thing.
|
---|
310 |
|
---|
311 | mh.deletefolder('dummy1')
|
---|
312 | mh.deletefolder('dummy2')
|
---|
313 | self.assertNotIn('dummy1', mh.listfolders())
|
---|
314 | self.assertTrue(not os.path.exists(path))
|
---|
315 |
|
---|
316 | def test_read(self):
|
---|
317 | mh = getMH()
|
---|
318 | eq = self.assertEqual
|
---|
319 |
|
---|
320 | f = mh.openfolder('inbox')
|
---|
321 | msg = f.openmessage(1)
|
---|
322 | # Check some basic stuff from rfc822
|
---|
323 | eq(msg.getheader('From'), "Mrs. Premise")
|
---|
324 | eq(msg.getheader('To'), "Mrs. Conclusion")
|
---|
325 |
|
---|
326 | # Okay, we have the right message. Let's check the stuff from
|
---|
327 | # mhlib.
|
---|
328 | lines = sortLines(msg.getheadertext())
|
---|
329 | eq(lines, ["Date: 18 July 2001",
|
---|
330 | "From: Mrs. Premise",
|
---|
331 | "To: Mrs. Conclusion"])
|
---|
332 | lines = sortLines(msg.getheadertext(lambda h: len(h)==4))
|
---|
333 | eq(lines, ["Date: 18 July 2001",
|
---|
334 | "From: Mrs. Premise"])
|
---|
335 | eq(msg.getbodytext(), "Hullo, Mrs. Conclusion!\n\n")
|
---|
336 | eq(msg.getbodytext(0), "Hullo, Mrs. Conclusion!\n\n")
|
---|
337 |
|
---|
338 | # XXXX there should be a better way to reclaim the file handle
|
---|
339 | msg.fp.close()
|
---|
340 | del msg
|
---|
341 |
|
---|
342 |
|
---|
343 | def test_main():
|
---|
344 | run_unittest(MhlibTests)
|
---|
345 |
|
---|
346 |
|
---|
347 | if __name__ == "__main__":
|
---|
348 | test_main()
|
---|