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