diff --git a/Lib/test/test_mhlib.py b/Lib/test/test_mhlib.py new file mode 100644 index 00000000000..a7879623f1e --- /dev/null +++ b/Lib/test/test_mhlib.py @@ -0,0 +1,328 @@ +""" + Tests for the mhlib module + Nick Mathewson +""" + +### BUG: This suite doesn't currently test the mime functionality of +### mhlib. It should. + +import unittest +from test_support import run_unittest, TESTFN +import os, StringIO + +import mhlib + +_mhroot = TESTFN+"_MH" +_mhpath = os.path.join(_mhroot, "MH") +_mhprofile = os.path.join(_mhroot, ".mh_profile") + +def normF(f): + return os.path.join(*f.split('/')) + +def writeFile(fname, contents): + dir = os.path.split(fname)[0] + if dir and not os.path.exists(dir): + mkdirs(dir) + f = open(fname, 'w') + f.write(contents) + f.close() + +def readFile(fname): + f = open(fname) + r = f.read() + f.close() + return r + +def writeProfile(dict): + contents = [ "%s: %s\n" % (k, v) for k, v in dict.iteritems() ] + writeFile(_mhprofile, "".join(contents)) + +def writeContext(folder): + folder = normF(folder) + writeFile(os.path.join(_mhpath, "context"), + "Current-Folder: %s\n" % folder) + +def writeCurMessage(folder, cur): + folder = normF(folder) + writeFile(os.path.join(_mhpath, folder, ".mh_sequences"), + "cur: %s\n"%cur) + +def writeMessage(folder, n, headers, body): + folder = normF(folder) + headers = "".join([ "%s: %s\n" % (k, v) for k, v in headers.iteritems() ]) + contents = "%s\n%s\n" % (headers,body) + mkdirs(os.path.join(_mhpath, folder)) + writeFile(os.path.join(_mhpath, folder, str(n)), contents) + +def getMH(): + return mhlib.MH(os.path.abspath(_mhpath), _mhprofile) + +def sortLines(s): + lines = s.split("\n") + lines = [ line.strip() for line in lines if len(line) >= 2 ] + lines.sort() + return lines + +# These next 2 functions are copied from test_glob.py. +def mkdirs(fname): + if os.path.exists(fname) or fname == '': + return + base, file = os.path.split(fname) + mkdirs(base) + os.mkdir(fname) + +def deltree(fname): + if not os.path.exists(fname): + return + for f in os.listdir(fname): + fullname = os.path.join(fname, f) + if os.path.isdir(fullname): + deltree(fullname) + else: + try: + os.unlink(fullname) + except: + pass + try: + os.rmdir(fname) + except: + pass + +class MhlibTests(unittest.TestCase): + def setUp(self): + deltree(_mhroot) + mkdirs(_mhpath) + writeProfile({'Path' : os.path.abspath(_mhpath), + 'Editor': 'emacs', + 'ignored-attribute': 'camping holiday'}) + # Note: These headers aren't really conformant to RFC822, but + # mhlib shouldn't care about that. + + # An inbox with a couple of messages. + writeMessage('inbox', 1, + {'From': 'Mrs. Premise', + 'To': 'Mrs. Conclusion', + 'Date': '18 July 2001'}, "Hullo, Mrs. Conclusion!\n") + writeMessage('inbox', 2, + {'From': 'Mrs. Conclusion', + 'To': 'Mrs. Premise', + 'Date': '29 July 2001'}, "Hullo, Mrs. Premise!\n") + + # A folder with many messages + for i in range(5, 101)+range(101, 201, 2): + writeMessage('wide', i, + {'From': 'nowhere', 'Subject': 'message #%s' % i}, + "This is message number %s\n" % i) + + # A deeply nested folder + def deep(folder, n): + writeMessage(folder, n, + {'Subject': 'Message %s/%s' % (folder, n) }, + "This is message number %s in %s\n" % (n, folder) ) + deep('deep/f1', 1) + deep('deep/f1', 2) + deep('deep/f1', 3) + deep('deep/f2', 4) + deep('deep/f2', 6) + deep('deep', 3) + deep('deep/f2/f3', 1) + deep('deep/f2/f3', 2) + + def tearDown(self): + deltree(_mhroot) + + def test_basic(self): + writeContext('inbox') + writeCurMessage('inbox', 2) + mh = getMH() + + eq = self.assertEquals + eq(mh.getprofile('Editor'), 'emacs') + eq(mh.getprofile('not-set'), None) + eq(mh.getpath(), os.path.abspath(_mhpath)) + eq(mh.getcontext(), 'inbox') + + mh.setcontext('wide') + eq(mh.getcontext(), 'wide') + eq(readFile(os.path.join(_mhpath, 'context')), + "Current-Folder: wide\n") + + mh.setcontext('inbox') + + inbox = mh.openfolder('inbox') + eq(inbox.getfullname(), + os.path.join(os.path.abspath(_mhpath), 'inbox')) + eq(inbox.getsequencesfilename(), + os.path.join(os.path.abspath(_mhpath), 'inbox', '.mh_sequences')) + eq(inbox.getmessagefilename(1), + os.path.join(os.path.abspath(_mhpath), 'inbox', '1')) + + def test_listfolders(self): + mh = getMH() + eq = self.assertEquals + + folders = mh.listfolders() + folders.sort() + eq(folders, ['deep', 'inbox', 'wide']) + + folders = mh.listallfolders() + folders.sort() + eq(folders, map(normF, ['deep', 'deep/f1', 'deep/f2', 'deep/f2/f3', + 'inbox', 'wide'])) + + folders = mh.listsubfolders('deep') + folders.sort() + eq(folders, map(normF, ['deep/f1', 'deep/f2'])) + + folders = mh.listallsubfolders('deep') + folders.sort() + eq(folders, map(normF, ['deep/f1', 'deep/f2', 'deep/f2/f3'])) + eq(mh.listsubfolders(normF('deep/f2')), [normF('deep/f2/f3')]) + + eq(mh.listsubfolders('inbox'), []) + eq(mh.listallsubfolders('inbox'), []) + + def test_sequence(self): + mh = getMH() + eq = self.assertEquals + writeCurMessage('wide', 55) + + f = mh.openfolder('wide') + all = f.listmessages() + eq(all, range(5, 101)+range(101, 201, 2)) + eq(f.getcurrent(), 55) + f.setcurrent(99) + eq(readFile(os.path.join(_mhpath, 'wide', '.mh_sequences')), + 'cur: 99\n') + + def seqeq(seq, val): + eq(f.parsesequence(seq), val) + + seqeq('5-55', range(5, 56)) + seqeq('90-108', range(90, 101)+range(101, 109, 2)) + seqeq('90-108', range(90, 101)+range(101, 109, 2)) + + seqeq('10:10', range(10, 20)) + seqeq('10:+10', range(10, 20)) + seqeq('101:10', range(101, 121, 2)) + + seqeq('cur', [99]) + seqeq('.', [99]) + seqeq('prev', [98]) + seqeq('next', [100]) + seqeq('cur:-3', [97, 98, 99]) + seqeq('first-cur', range(5, 100)) + seqeq('150-last', range(151, 201, 2)) + seqeq('prev-next', [98, 99, 100]) + + lowprimes = [5, 7, 11, 13, 17, 19, 23, 29] + lowcompos = [x for x in range(5, 31) if not x in lowprimes ] + f.putsequences({'cur': [5], + 'lowprime': lowprimes, + 'lowcompos': lowcompos}) + seqs = readFile(os.path.join(_mhpath, 'wide', '.mh_sequences')) + seqs = sortLines(seqs) + eq(seqs, ["cur: 5", + "lowcompos: 6 8-10 12 14-16 18 20-22 24-28 30", + "lowprime: 5 7 11 13 17 19 23 29"]) + + seqeq('lowprime', lowprimes) + seqeq('lowprime:1', [5]) + seqeq('lowprime:2', [5, 7]) + seqeq('lowprime:-2', [23, 29]) + + ## Not supported + #seqeq('lowprime:first', [5]) + #seqeq('lowprime:last', [29]) + #seqeq('lowprime:prev', [29]) + #seqeq('lowprime:next', [29]) + + def test_modify(self): + mh = getMH() + eq = self.assertEquals + + mh.makefolder("dummy1") + self.assert_("dummy1" in mh.listfolders()) + path = os.path.join(_mhpath, "dummy1") + self.assert_(os.path.exists(path)) + + f = mh.openfolder('dummy1') + def create(n): + msg = "From: foo\nSubject: %s\n\nDummy Message %s\n" % (n,n) + f.createmessage(n, StringIO.StringIO(msg)) + + create(7) + create(8) + create(9) + + eq(readFile(f.getmessagefilename(9)), + "From: foo\nSubject: 9\n\nDummy Message 9\n") + + eq(f.listmessages(), [7, 8, 9]) + files = os.listdir(path) + files.sort() + eq(files, ['7', '8', '9']) + + f.removemessages(['7', '8']) + files = os.listdir(path) + files.sort() + eq(files, [',7', ',8', '9']) + eq(f.listmessages(), [9]) + create(10) + create(11) + create(12) + + mh.makefolder("dummy2") + f2 = mh.openfolder("dummy2") + eq(f2.listmessages(), []) + f.movemessage(10, f2, 3) + f.movemessage(11, f2, 5) + eq(f.listmessages(), [9, 12]) + eq(f2.listmessages(), [3, 5]) + eq(readFile(f2.getmessagefilename(3)), + "From: foo\nSubject: 10\n\nDummy Message 10\n") + + f.copymessage(9, f2, 4) + eq(f.listmessages(), [9, 12]) + eq(readFile(f2.getmessagefilename(4)), + "From: foo\nSubject: 9\n\nDummy Message 9\n") + + f.refilemessages([9, 12], f2) + eq(f.listmessages(), []) + eq(f2.listmessages(), [3, 4, 5, 6, 7]) + eq(readFile(f2.getmessagefilename(7)), + "From: foo\nSubject: 12\n\nDummy Message 12\n") + # XXX This should check that _copysequences does the right thing. + + mh.deletefolder('dummy1') + mh.deletefolder('dummy2') + self.assert_('dummy1' not in mh.listfolders()) + self.assert_(not os.path.exists(path)) + + def test_read(self): + mh = getMH() + eq = self.assertEquals + + f = mh.openfolder('inbox') + msg = f.openmessage(1) + # Check some basic stuff from rfc822 + eq(msg.getheader('From'), "Mrs. Premise") + eq(msg.getheader('To'), "Mrs. Conclusion") + + # Okay, we have the right message. Let's check the stuff from + # mhlib. + lines = sortLines(msg.getheadertext()) + eq(lines, ["Date: 18 July 2001", + "From: Mrs. Premise", + "To: Mrs. Conclusion"]) + lines = sortLines(msg.getheadertext(lambda h: len(h)==4)) + eq(lines, ["Date: 18 July 2001", + "From: Mrs. Premise"]) + eq(msg.getbodytext(), "Hullo, Mrs. Conclusion!\n\n") + eq(msg.getbodytext(0), "Hullo, Mrs. Conclusion!\n\n") + + # XXXX there should be a better way to reclaim the file handle + msg.fp.close() + del msg + +run_unittest(MhlibTests)