blob: ad66edeed4ff1ba59ac4766232185414b7833ce8 [file] [log] [blame]
Guido van Rossum0263c802001-08-09 18:18:08 +00001"""
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
9import unittest
Barry Warsaw04f357c2002-07-23 19:04:11 +000010from test.test_support import run_unittest, TESTFN, TestSkipped
Guido van Rossum34d19282007-08-09 01:03:29 +000011import os
12import io
Tim Petersc7ca3ff2001-08-09 21:34:54 +000013import sys
Guido van Rossum0263c802001-08-09 18:18:08 +000014import mhlib
15
Tim Peters32ef1692002-08-04 22:35:31 +000016if (sys.platform.startswith("win") or sys.platform=="riscos" or
Tim Peters283ead82002-08-04 22:52:30 +000017 sys.platform.startswith("atheos")):
Tim Peters32ef1692002-08-04 22:35:31 +000018 # mhlib.updateline() renames a file to the name of a file that already
19 # exists. That causes a reasonable OS <wink> to complain in test_sequence
20 # here, like the "OSError: [Errno 17] File exists" raised on Windows.
21 # mhlib's listsubfolders() and listallfolders() do something with
22 # link counts, and that causes test_listfolders() here to get back
23 # an empty list from its call of listallfolders().
24 # The other tests here pass on Windows.
Tim Peters6782d6a2002-08-04 22:55:35 +000025 raise TestSkipped("skipped on %s -- " % sys.platform +
Tim Petersc7ca3ff2001-08-09 21:34:54 +000026 "too many Unix assumptions")
27
Guido van Rossum0263c802001-08-09 18:18:08 +000028_mhroot = TESTFN+"_MH"
29_mhpath = os.path.join(_mhroot, "MH")
30_mhprofile = os.path.join(_mhroot, ".mh_profile")
31
32def normF(f):
33 return os.path.join(*f.split('/'))
34
35def writeFile(fname, contents):
36 dir = os.path.split(fname)[0]
37 if dir and not os.path.exists(dir):
38 mkdirs(dir)
39 f = open(fname, 'w')
40 f.write(contents)
41 f.close()
42
43def readFile(fname):
44 f = open(fname)
45 r = f.read()
46 f.close()
47 return r
48
49def writeProfile(dict):
Guido van Rossumcc2b0162007-02-11 06:12:03 +000050 contents = [ "%s: %s\n" % (k, v) for k, v in dict.items() ]
Guido van Rossum0263c802001-08-09 18:18:08 +000051 writeFile(_mhprofile, "".join(contents))
52
53def writeContext(folder):
54 folder = normF(folder)
Tim Petersab9ba272001-08-09 21:40:30 +000055 writeFile(os.path.join(_mhpath, "context"),
Guido van Rossum0263c802001-08-09 18:18:08 +000056 "Current-Folder: %s\n" % folder)
57
58def writeCurMessage(folder, cur):
59 folder = normF(folder)
60 writeFile(os.path.join(_mhpath, folder, ".mh_sequences"),
61 "cur: %s\n"%cur)
62
63def writeMessage(folder, n, headers, body):
64 folder = normF(folder)
Guido van Rossumcc2b0162007-02-11 06:12:03 +000065 headers = "".join([ "%s: %s\n" % (k, v) for k, v in headers.items() ])
Guido van Rossum0263c802001-08-09 18:18:08 +000066 contents = "%s\n%s\n" % (headers,body)
67 mkdirs(os.path.join(_mhpath, folder))
68 writeFile(os.path.join(_mhpath, folder, str(n)), contents)
69
70def getMH():
71 return mhlib.MH(os.path.abspath(_mhpath), _mhprofile)
72
73def sortLines(s):
74 lines = s.split("\n")
75 lines = [ line.strip() for line in lines if len(line) >= 2 ]
76 lines.sort()
77 return lines
78
79# These next 2 functions are copied from test_glob.py.
80def mkdirs(fname):
81 if os.path.exists(fname) or fname == '':
82 return
83 base, file = os.path.split(fname)
84 mkdirs(base)
85 os.mkdir(fname)
86
87def deltree(fname):
88 if not os.path.exists(fname):
89 return
90 for f in os.listdir(fname):
91 fullname = os.path.join(fname, f)
92 if os.path.isdir(fullname):
93 deltree(fullname)
94 else:
95 try:
96 os.unlink(fullname)
97 except:
98 pass
99 try:
100 os.rmdir(fname)
101 except:
102 pass
103
104class MhlibTests(unittest.TestCase):
105 def setUp(self):
106 deltree(_mhroot)
107 mkdirs(_mhpath)
Tim Petersab9ba272001-08-09 21:40:30 +0000108 writeProfile({'Path' : os.path.abspath(_mhpath),
Guido van Rossum0263c802001-08-09 18:18:08 +0000109 'Editor': 'emacs',
110 'ignored-attribute': 'camping holiday'})
111 # Note: These headers aren't really conformant to RFC822, but
112 # mhlib shouldn't care about that.
113
114 # An inbox with a couple of messages.
Tim Petersab9ba272001-08-09 21:40:30 +0000115 writeMessage('inbox', 1,
Guido van Rossum0263c802001-08-09 18:18:08 +0000116 {'From': 'Mrs. Premise',
117 'To': 'Mrs. Conclusion',
118 'Date': '18 July 2001'}, "Hullo, Mrs. Conclusion!\n")
Tim Petersab9ba272001-08-09 21:40:30 +0000119 writeMessage('inbox', 2,
Guido van Rossum0263c802001-08-09 18:18:08 +0000120 {'From': 'Mrs. Conclusion',
121 'To': 'Mrs. Premise',
122 'Date': '29 July 2001'}, "Hullo, Mrs. Premise!\n")
Tim Petersab9ba272001-08-09 21:40:30 +0000123
Guido van Rossum0263c802001-08-09 18:18:08 +0000124 # A folder with many messages
Guido van Rossum805365e2007-05-07 22:24:25 +0000125 for i in list(range(5, 101))+list(range(101, 201, 2)):
Tim Petersab9ba272001-08-09 21:40:30 +0000126 writeMessage('wide', i,
Guido van Rossum0263c802001-08-09 18:18:08 +0000127 {'From': 'nowhere', 'Subject': 'message #%s' % i},
128 "This is message number %s\n" % i)
Tim Petersab9ba272001-08-09 21:40:30 +0000129
Guido van Rossum0263c802001-08-09 18:18:08 +0000130 # A deeply nested folder
131 def deep(folder, n):
Tim Petersab9ba272001-08-09 21:40:30 +0000132 writeMessage(folder, n,
Guido van Rossum0263c802001-08-09 18:18:08 +0000133 {'Subject': 'Message %s/%s' % (folder, n) },
134 "This is message number %s in %s\n" % (n, folder) )
135 deep('deep/f1', 1)
136 deep('deep/f1', 2)
137 deep('deep/f1', 3)
138 deep('deep/f2', 4)
139 deep('deep/f2', 6)
140 deep('deep', 3)
141 deep('deep/f2/f3', 1)
142 deep('deep/f2/f3', 2)
Tim Petersab9ba272001-08-09 21:40:30 +0000143
Guido van Rossum0263c802001-08-09 18:18:08 +0000144 def tearDown(self):
145 deltree(_mhroot)
Tim Petersab9ba272001-08-09 21:40:30 +0000146
Guido van Rossum0263c802001-08-09 18:18:08 +0000147 def test_basic(self):
148 writeContext('inbox')
149 writeCurMessage('inbox', 2)
150 mh = getMH()
151
152 eq = self.assertEquals
153 eq(mh.getprofile('Editor'), 'emacs')
154 eq(mh.getprofile('not-set'), None)
155 eq(mh.getpath(), os.path.abspath(_mhpath))
156 eq(mh.getcontext(), 'inbox')
157
158 mh.setcontext('wide')
159 eq(mh.getcontext(), 'wide')
160 eq(readFile(os.path.join(_mhpath, 'context')),
161 "Current-Folder: wide\n")
162
163 mh.setcontext('inbox')
164
165 inbox = mh.openfolder('inbox')
Tim Petersab9ba272001-08-09 21:40:30 +0000166 eq(inbox.getfullname(),
Guido van Rossum0263c802001-08-09 18:18:08 +0000167 os.path.join(os.path.abspath(_mhpath), 'inbox'))
Tim Petersab9ba272001-08-09 21:40:30 +0000168 eq(inbox.getsequencesfilename(),
Guido van Rossum0263c802001-08-09 18:18:08 +0000169 os.path.join(os.path.abspath(_mhpath), 'inbox', '.mh_sequences'))
Tim Petersab9ba272001-08-09 21:40:30 +0000170 eq(inbox.getmessagefilename(1),
Guido van Rossum0263c802001-08-09 18:18:08 +0000171 os.path.join(os.path.abspath(_mhpath), 'inbox', '1'))
Tim Petersab9ba272001-08-09 21:40:30 +0000172
Guido van Rossum0263c802001-08-09 18:18:08 +0000173 def test_listfolders(self):
174 mh = getMH()
175 eq = self.assertEquals
176
177 folders = mh.listfolders()
178 folders.sort()
179 eq(folders, ['deep', 'inbox', 'wide'])
Tim Petersab9ba272001-08-09 21:40:30 +0000180
Guido van Rossum0263c802001-08-09 18:18:08 +0000181 folders = mh.listallfolders()
182 folders.sort()
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000183 tfolders = sorted(map(normF, ['deep', 'deep/f1', 'deep/f2',
184 'deep/f2/f3', 'inbox', 'wide']))
Jack Jansendbc363c2001-08-11 23:22:43 +0000185 eq(folders, tfolders)
Guido van Rossum0263c802001-08-09 18:18:08 +0000186
187 folders = mh.listsubfolders('deep')
188 folders.sort()
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000189 eq(folders, list(map(normF, ['deep/f1', 'deep/f2'])))
Tim Petersab9ba272001-08-09 21:40:30 +0000190
Guido van Rossum0263c802001-08-09 18:18:08 +0000191 folders = mh.listallsubfolders('deep')
192 folders.sort()
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000193 eq(folders, list(map(normF, ['deep/f1', 'deep/f2', 'deep/f2/f3'])))
Guido van Rossum0263c802001-08-09 18:18:08 +0000194 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.assertEquals
202 writeCurMessage('wide', 55)
Tim Petersab9ba272001-08-09 21:40:30 +0000203
Guido van Rossum0263c802001-08-09 18:18:08 +0000204 f = mh.openfolder('wide')
205 all = f.listmessages()
Guido van Rossum805365e2007-05-07 22:24:25 +0000206 eq(all, list(range(5, 101))+list(range(101, 201, 2)))
Guido van Rossum0263c802001-08-09 18:18:08 +0000207 eq(f.getcurrent(), 55)
208 f.setcurrent(99)
Tim Petersab9ba272001-08-09 21:40:30 +0000209 eq(readFile(os.path.join(_mhpath, 'wide', '.mh_sequences')),
Guido van Rossum0263c802001-08-09 18:18:08 +0000210 'cur: 99\n')
211
212 def seqeq(seq, val):
213 eq(f.parsesequence(seq), val)
Tim Petersab9ba272001-08-09 21:40:30 +0000214
Guido van Rossum805365e2007-05-07 22:24:25 +0000215 seqeq('5-55', list(range(5, 56)))
216 seqeq('90-108', list(range(90, 101))+list(range(101, 109, 2)))
217 seqeq('90-108', list(range(90, 101))+list(range(101, 109, 2)))
Tim Petersab9ba272001-08-09 21:40:30 +0000218
Guido van Rossum805365e2007-05-07 22:24:25 +0000219 seqeq('10:10', list(range(10, 20)))
220 seqeq('10:+10', list(range(10, 20)))
221 seqeq('101:10', list(range(101, 121, 2)))
Guido van Rossum0263c802001-08-09 18:18:08 +0000222
223 seqeq('cur', [99])
224 seqeq('.', [99])
225 seqeq('prev', [98])
226 seqeq('next', [100])
227 seqeq('cur:-3', [97, 98, 99])
Guido van Rossum805365e2007-05-07 22:24:25 +0000228 seqeq('first-cur', list(range(5, 100)))
229 seqeq('150-last', list(range(151, 201, 2)))
Guido van Rossum0263c802001-08-09 18:18:08 +0000230 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.assertEquals
257
258 mh.makefolder("dummy1")
259 self.assert_("dummy1" in mh.listfolders())
260 path = os.path.join(_mhpath, "dummy1")
261 self.assert_(os.path.exists(path))
Tim Petersab9ba272001-08-09 21:40:30 +0000262
Guido van Rossum0263c802001-08-09 18:18:08 +0000263 f = mh.openfolder('dummy1')
264 def create(n):
265 msg = "From: foo\nSubject: %s\n\nDummy Message %s\n" % (n,n)
Guido van Rossum34d19282007-08-09 01:03:29 +0000266 f.createmessage(n, io.StringIO(msg))
Guido van Rossum0263c802001-08-09 18:18:08 +0000267
268 create(7)
269 create(8)
270 create(9)
Tim Petersab9ba272001-08-09 21:40:30 +0000271
Guido van Rossum0263c802001-08-09 18:18:08 +0000272 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()
Tim Petersab9ba272001-08-09 21:40:30 +0000278 eq(files, ['7', '8', '9'])
Guido van Rossum0263c802001-08-09 18:18:08 +0000279
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)
Tim Petersab9ba272001-08-09 21:40:30 +0000288
Guido van Rossum0263c802001-08-09 18:18:08 +0000289 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")
Tim Petersab9ba272001-08-09 21:40:30 +0000298
Guido van Rossum0263c802001-08-09 18:18:08 +0000299 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")
Tim Petersab9ba272001-08-09 21:40:30 +0000303
Guido van Rossum0263c802001-08-09 18:18:08 +0000304 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.assert_('dummy1' not in mh.listfolders())
314 self.assert_(not os.path.exists(path))
315
316 def test_read(self):
317 mh = getMH()
318 eq = self.assertEquals
Tim Petersab9ba272001-08-09 21:40:30 +0000319
Guido van Rossum0263c802001-08-09 18:18:08 +0000320 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())
Tim Petersab9ba272001-08-09 21:40:30 +0000329 eq(lines, ["Date: 18 July 2001",
Guido van Rossum0263c802001-08-09 18:18:08 +0000330 "From: Mrs. Premise",
331 "To: Mrs. Conclusion"])
332 lines = sortLines(msg.getheadertext(lambda h: len(h)==4))
Tim Petersab9ba272001-08-09 21:40:30 +0000333 eq(lines, ["Date: 18 July 2001",
Guido van Rossum0263c802001-08-09 18:18:08 +0000334 "From: Mrs. Premise"])
335 eq(msg.getbodytext(), "Hullo, Mrs. Conclusion!\n\n")
336 eq(msg.getbodytext(0), "Hullo, Mrs. Conclusion!\n\n")
Tim Petersab9ba272001-08-09 21:40:30 +0000337
Guido van Rossum0263c802001-08-09 18:18:08 +0000338 # XXXX there should be a better way to reclaim the file handle
339 msg.fp.close()
340 del msg
341
Fred Drake2e2be372001-09-20 21:33:42 +0000342
343def test_main():
344 run_unittest(MhlibTests)
345
346
347if __name__ == "__main__":
348 test_main()