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