#!/usr/bin/python ### ### Unit tests for the arch_index.py program ### ### Note that these rely on having at least one private and one ### private list for testing various combinations of options; if ### you don't have any public (or private) lists, these tests ### may not be very useful to you, or may need to be modified. ### ### Anthony R. Thompson, June 2010 ### Contact: put @ between art and sigilservices.com ### ### Copyright (C) 1998-2010 by the Free Software Foundation, Inc. ### ### This program is free software; you can redistribute it and/or ### modify it under the terms of the GNU General Public License ### as published by the Free Software Foundation; either version 2 ### of the License, or (at your option) any later version. ### ### This program is distributed in the hope that it will be useful, ### but WITHOUT ANY WARRANTY; without even the implied warranty of ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ### GNU General Public License for more details. ### ### You should have received a copy of the GNU General Public License ### along with this program; if not, write to the Free Software ### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ### 02110-1301, USA. http://www.fsf.org/licensing/licenses/gpl.html ### import os, sys, re, unittest, random, time import arch_index import ListStream class ArchIndexTests(unittest.TestCase): __argv__ = sys.argv[:] def save_output(self): self.output = ListStream.ListStream() sys.stdout = self.output def clear_output(self): self.output.clear() def get_output(self): return self.output.contents() def restore_output(self): sys.stdout = sys.__stdout__ def save_error(self): self.error = ListStream.ListStream() sys.stderr = self.error def clear_error(self): self.error.clear() def get_error(self): return self.error.contents() def restore_error(self): sys.stderr = sys.__stderr__ def text_in_list(self, find_text, search_list): found = False for line in search_list: if (re.search(find_text, line)): found = True break return found def text_in_file(self, find_text, file_path): fh = open(file_path) found = False for line in fh.readlines(): if (re.search(find_text, line)): found = True break return found def setUp(self): # Make believe that it was called from the command line self.testprog = 'arch_index.py' self.archivedir = '/var/lib/mailman/archives' # INSTALL self.swish = '/usr/bin/swish-e' # INSTALL self.swishcgi = '/usr/lib/swish-e/swish.cgi' # INSTALL self.privlist = 'some-private-listname' # INSTALL self.privlistdir = "%s/private/%s" % (self.archivedir, self.privlist) self.publist = 'some-public-listname' # INSTALL self.publistdir = "%s/private/%s" % (self.archivedir, self.publist) # find the first message archive file, such as 000013.html self.testmsg = '' for root, dirs, files in os.walk(self.privlistdir): for filename in files: if (re.search('^\d{6}\.html$', filename)): self.testmsg = os.path.join(root, filename) if (self.testmsg == ''): self.fail("Unable to find a message to test") def testUsage(self): # First check stdout self.save_output() try: arch_index.usage(0, 'apples') except SystemExit, v: self.assertEquals(str(v), '0') output = self.get_output() self.assertTrue(self.text_in_list('--help', output)) self.assertTrue(self.text_in_list('apples', output)) self.restore_output() # Now check stderr self.save_error() try: arch_index.usage(37, 'bananas') except SystemExit, v: self.assertEquals(str(v), '37') error = self.get_error() self.assertTrue(self.text_in_list('--help', error)) self.assertTrue(self.text_in_list('bananas', error)) self.restore_error() def testArchivedListNames(self): self.save_error() try: arch_index.archived_list_names('/bad/dir') except SystemExit: pass error = self.get_error() self.assertTrue(self.text_in_list('does not exist', error)) self.clear_error() try: arch_index.archived_list_names(self.archivedir, 'badfilter') except SystemExit: pass error = self.get_error() self.assertTrue(self.text_in_list('unknown filter option', error)) self.clear_error() self.restore_error() outlist = arch_index.archived_list_names(self.archivedir, 'all') self.assertTrue(self.publist in outlist) self.assertTrue(self.privlist in outlist) self.assertTrue(len(outlist) > 5) outlist = arch_index.archived_list_names(self.archivedir) self.assertTrue(self.publist in outlist) self.assertTrue(self.privlist in outlist) self.assertTrue(len(outlist) > 5) outlist = arch_index.archived_list_names(self.archivedir, 'public') self.assertTrue(self.publist in outlist) self.assertTrue(self.privlist not in outlist) outlist = arch_index.archived_list_names(self.archivedir, 'private') self.assertTrue(self.publist not in outlist) self.assertTrue(self.privlist in outlist) # Test just the logic for selecting/excluding lists, with cmd line opts def testListsToIndex(self): # No options should have all lists EXCEPT mailman default list index_lists = arch_index.lists_to_index(self.archivedir) self.assertTrue(self.publist in index_lists) self.assertTrue(self.privlist in index_lists) self.assertTrue('mailman' not in index_lists) # Now let's try forcing it to include mailman default list index_lists = arch_index.lists_to_index(self.archivedir, include_mm=True) self.assertTrue(self.publist in index_lists) self.assertTrue(self.privlist in index_lists) self.assertTrue('mailman' in index_lists) # See if public only negates the effect of includemm index_lists = arch_index.lists_to_index(self.archivedir, include_mm=True, public_only=True) self.assertTrue(self.publist in index_lists) self.assertTrue(self.privlist not in index_lists) self.assertTrue('mailman' not in index_lists) # Test regular usage of public option index_lists = arch_index.lists_to_index(self.archivedir, public_only=True) self.assertTrue(self.publist in index_lists) self.assertTrue(self.privlist not in index_lists) self.assertTrue('mailman' not in index_lists) # Test regular usage of private option index_lists = arch_index.lists_to_index(self.archivedir, private_only=True) self.assertTrue(self.publist not in index_lists) self.assertTrue(self.privlist in index_lists) self.assertTrue('mailman' not in index_lists) # Test private only with includemm index_lists = arch_index.lists_to_index(self.archivedir, private_only=True, include_mm=True) self.assertTrue(self.publist not in index_lists) self.assertTrue(self.privlist in index_lists) self.assertTrue('mailman' in index_lists) # Test regular usage of specific list option for one and many lists index_lists = arch_index.lists_to_index(self.archivedir, specific_list=self.publist) self.assertTrue(self.publist in index_lists) self.assertTrue(len(index_lists) == 1) twolists = "%s,%s" % (self.publist, self.privlist) index_lists = arch_index.lists_to_index(self.archivedir, specific_list=twolists) self.assertTrue(self.publist in index_lists) self.assertTrue(self.privlist in index_lists) self.assertTrue(len(index_lists) == 2) # See whether specific list messed up by includemm, public, private twolists = "%s,%s" % (self.publist, self.privlist) index_lists = arch_index.lists_to_index(self.archivedir, include_mm=True, specific_list=twolists) self.assertTrue(self.publist in index_lists) self.assertTrue(self.privlist in index_lists) self.assertTrue(len(index_lists) == 2) index_lists = arch_index.lists_to_index(self.archivedir, public_only=True, specific_list=twolists) self.assertTrue(self.publist in index_lists) self.assertTrue(self.privlist in index_lists) self.assertTrue(len(index_lists) == 2) index_lists = arch_index.lists_to_index(self.archivedir, private_only=True, specific_list=twolists) self.assertTrue(self.publist in index_lists) self.assertTrue(self.privlist in index_lists) self.assertTrue(len(index_lists) == 2) index_lists = arch_index.lists_to_index(self.archivedir, public_only=True, private_only=True, specific_list=twolists) self.assertTrue(self.publist in index_lists) self.assertTrue(self.privlist in index_lists) self.assertTrue(len(index_lists) == 2) index_lists = arch_index.lists_to_index(self.archivedir, include_mm=True, public_only=True, private_only=True, specific_list=twolists) self.assertTrue(self.publist in index_lists) self.assertTrue(self.privlist in index_lists) self.assertTrue(len(index_lists) == 2) # Test regular usage of -x/--exclude option index_lists = arch_index.lists_to_index(self.archivedir, exclude_list=self.publist) self.assertTrue(self.privlist in index_lists) self.assertTrue(self.publist not in index_lists) # Test whether -x/--exclude can be used to prune --private index_lists = arch_index.lists_to_index(self.archivedir, private_only=True, exclude_list=self.privlist) self.assertTrue(self.privlist not in index_lists) # Test whether using -x/--exclude results in NO lists left to index index_lists = arch_index.lists_to_index(self.archivedir, specific_list=self.publist, exclude_list=self.publist) self.assertTrue(len(index_lists) == 0) # Need to test three cases: # 1. Config file does not exist = create a new config file # 2. Config file exists, no overwrite = no change to config file # 3. Config file exists, overwrite = existing config file replaced # Also need to test different behavior for public vs private list def testSetupSwishConfig(self): confpath = "%s/%s.swish-conf" % (self.privlistdir, self.privlist) confpath = os.path.normpath(confpath) # 1. Config file does not exist = create a new config file # If there is a current config file, save it by renaming. conf_existed = False if (os.path.isfile(confpath)): conf_existed = True # Tried using tempfile module but on Python 2.4 it doesn't # support NamedTemporaryFile's delete=False option tmppath = "./%s.tmp" % (str(random.randint(1, 90000000))) os.rename(confpath, tmppath) newconf = arch_index.setup_swish_config(self.archivedir,self.privlist) self.assertEquals(newconf, confpath) self.assertTrue(os.path.isfile(confpath)) # 2. Config file exists, no overwrite = no change to config file before_mtime = os.path.getmtime(confpath) newconf = arch_index.setup_swish_config(self.archivedir,self.privlist) after_mtime = os.path.getmtime(confpath) self.assertEquals(before_mtime, after_mtime) self.assertTrue(self.text_in_file('/mailman/private', newconf)) # 3. Config file exists, overwrite = existing config file replaced before_mtime = os.path.getmtime(confpath) time.sleep(2) # otherwise this happens so fast that testing fails newconf = arch_index.setup_swish_config(self.archivedir, self.privlist, public=True, overwrite=True) after_mtime = os.path.getmtime(confpath) self.assertTrue(before_mtime < after_mtime) self.assertTrue(self.text_in_file('/pipermail', newconf)) # Cleanup os.remove(confpath) if (conf_existed): os.rename(tmppath, confpath) # Need to test three cases: # 1. Cgi file does not exist = create a new cgi file # 2. Cgi file exists, no overwrite = no change to cgi file # 3. Cgi file exists, overwrite = existing cgi file replaced # Also need to test different behavior for public vs private list def testSetupSwishCgi(self): cgipath = "%s/swish.cgi" % (self.privlistdir) cgipath = os.path.normpath(cgipath) # 1. Cgi file does not exist = create a new cgi file # If there is a current cgi file, save it by renaming. cgi_existed = False if (os.path.isfile(cgipath)): cgi_existed = True # Tried using tempfile module but on Python 2.4 it doesn't # support NamedTemporaryFile's delete=False option tmppath = "./%s.tmp" % (str(random.randint(1, 90000000))) os.rename(cgipath, tmppath) newcgi = arch_index.setup_swish_cgi(self.archivedir, self.privlist, self.swishcgi) self.assertEquals(newcgi, cgipath) self.assertTrue(os.path.isfile(cgipath)) # 2. Cgi file exists, no overwrite = no change to cgi file before_mtime = os.path.getmtime(cgipath) newcgi = arch_index.setup_swish_cgi(self.archivedir, self.privlist, self.swishcgi) after_mtime = os.path.getmtime(cgipath) self.assertEquals(before_mtime, after_mtime) # 3. Cgi file exists, overwrite = existing cgi file replaced before_mtime = os.path.getmtime(cgipath) time.sleep(2) # otherwise this happens so fast that testing fails newcgi = arch_index.setup_swish_cgi(self.archivedir, self.privlist, self.swishcgi, overwrite=True) after_mtime = os.path.getmtime(cgipath) self.assertTrue(before_mtime < after_mtime) # Cleanup os.remove(cgipath) if (cgi_existed): os.rename(tmppath, cgipath) # Need to test three cases: # 1. Config file does not exist = create a new config file # 2. Config file exists, no overwrite = no change to config file # 3. Config file exists, overwrite = existing config file replaced # Also need to test different behavior for public vs private list def testSetupSwishCgiConfig(self): confpath = "%s/%s.swish-cgi-conf" % (self.privlistdir, self.privlist) confpath = os.path.normpath(confpath) # 1. Config file does not exist = create a new config file # If there is a current config file, save it by renaming. conf_existed = False if (os.path.isfile(confpath)): conf_existed = True # Tried using tempfile module but on Python 2.4 it doesn't # support NamedTemporaryFile's delete=False option tmppath = "./%s.tmp" % (str(random.randint(1, 90000000))) os.rename(confpath, tmppath) newconf = arch_index.setup_swish_cgi_config(self.archivedir, self.privlist, self.swish) self.assertEquals(newconf, confpath) self.assertTrue(os.path.isfile(confpath)) self.assertTrue(self.text_in_file('swishlastmodified', newconf)) # 2. Config file exists, no overwrite = no change to config file before_mtime = os.path.getmtime(confpath) newconf = arch_index.setup_swish_cgi_config(self.archivedir, self.privlist, self.swish) after_mtime = os.path.getmtime(confpath) self.assertEquals(before_mtime, after_mtime) # 3. Config file exists, overwrite = existing config file replaced before_mtime = os.path.getmtime(confpath) time.sleep(2) # otherwise this happens so fast that testing fails newconf = arch_index.setup_swish_cgi_config(self.archivedir, self.privlist, self.swish, overwrite=True) after_mtime = os.path.getmtime(confpath) self.assertTrue(before_mtime < after_mtime) # Cleanup os.remove(confpath) if (conf_existed): os.rename(tmppath, confpath) def testGetFirstMessageDateTime(self): datefound = arch_index.get_first_message_datetime(self.testmsg) self.assertTrue(re.search('^[a-zA-Z]{3}\s+[a-zA-Z]{3}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2}\s+[a-zA-Z]{3}\s+\d{4}', datefound)) def testUpdateMtimeFromMessage(self): oldmtime = os.path.getmtime(self.testmsg) # save orig mtime newmtime = arch_index.update_mtime_from_message(self.testmsg) self.assertTrue(newmtime > 0) # if returned None, will fail self.assertEquals(os.path.getmtime(self.testmsg), newmtime) os.utime(self.testmsg, (oldmtime, oldmtime)) # restore orig mtime def testUpdateArchivePageMtimes(self): oldmtime = os.path.getmtime(self.testmsg) # save orig mtime os.utime(self.testmsg, (0, 0)) # set to epoch tmpts = "./%s.tmp" % (str(random.randint(1, 90000000))) open(tmpts, 'a').close() # touch dummy timestamp arch_index.update_archive_page_mtimes(self.privlistdir, tmpts) self.assertEquals(0, os.path.getmtime(self.testmsg)) # shouldn't update time.sleep(2) arch_index.update_archive_page_mtimes(self.privlistdir) self.assertNotEqual(0, os.path.getmtime(self.testmsg)) # should update os.utime(self.testmsg, (oldmtime, oldmtime)) # restore orig mtime os.remove(tmpts) # cleanup dummy timestamp # Right now this just tests a few basic options; should really # expand this at some point to test script behavior more fully def testMain(self): sys.argv = [self.testprog] self.save_error() try: processed_lists = arch_index.main() except SystemExit: pass err = self.get_error() self.assertTrue(self.text_in_list('archive directory is required',err)) self.restore_error() sys.argv = [self.testprog, '/bad/dir'] self.save_error() try: processed_lists = arch_index.main() except SystemExit: pass error = self.get_error() self.assertTrue(self.text_in_list('does not exist', error)) self.restore_error() sys.argv = [self.testprog, '-s', '/bad/swish/path', self.archivedir] self.save_error() try: processed_lists = arch_index.main() except SystemExit: pass error = self.get_error() self.assertTrue(self.text_in_list('unable to find', error)) self.restore_error() sys.argv = [self.testprog, '-c', '/bad/swishcgi/path', self.archivedir] self.save_error() try: processed_lists = arch_index.main() except SystemExit: pass error = self.get_error() self.assertTrue(self.text_in_list('unable to find', error)) self.restore_error() sys.argv = [self.testprog, '-z'] self.save_error() try: processed_lists = arch_index.main() except SystemExit: pass error = self.get_error() self.assertTrue(self.text_in_list('option -z not recognized', error)) self.restore_error() sys.argv = [self.testprog, '-h'] self.save_output() try: processed_lists = arch_index.main() except SystemExit: pass output = self.get_output() self.assertTrue(self.text_in_list('Usage:', output)) self.restore_output() # Don't forget about testing the "quiet" and "wipe" options def tearDown(self): sys.argv = ArchIndexTests.__argv__ if __name__ == '__main__': unittest.main() #suite = unittest.TestSuite() #suite.addTest(ArchIndexTests('testSetupSwishCgiConfig')) #suite.addTest(ArchIndexTests('testSearchPage')) #unittest.TextTestRunner().run(suite)