Home | History | Annotate | Download | only in test
      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()
    349