[PATCH] This is what I call test suite for now. What a mouthful :-).
Sebastian Spaeth
Sebastian at SSpaeth.de
Fri Dec 10 21:47:52 GMT 2010
It contains 2 working and one non-functional test so far and of course some test infrastructure. Works against current master. Run with "python test.py" :)
Signed-off-by: Sebastian Spaeth <Sebastian at SSpaeth.de>
---
tests/test.py | 267 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 267 insertions(+), 0 deletions(-)
create mode 100644 tests/test.py
diff --git a/tests/test.py b/tests/test.py
new file mode 100644
index 0000000..7aabc78
--- /dev/null
+++ b/tests/test.py
@@ -0,0 +1,267 @@
+import offlineimap
+from offlineimap.repository import Repository
+import random
+import tempfile
+import unittest
+import os
+import shutil
+import threading
+
+class Folder2FolderScaffold:
+ """This provides 'scaffolding' for testing Maildir<->Maildir syncing"""
+
+ def __init__(self):
+ #list of files (or directories that we have to cleanup on exit
+ self._tmpfiles = []
+ # True if cleanup() has been run, otherwise run automatically on
+ # destruction
+ self._cleaned_up = False
+
+ #save the ConfigParser() object here
+ self.config = self.create_config()
+
+ # we need to set up the global ui variable, choose the silent one.
+ ui = offlineimap.ui.Noninteractive.Quiet(self.config, verbose=-1)
+ offlineimap.ui.UIBase.setglobalui(ui)
+
+ # Create the account object for test1 and create the account metadatadir
+ self.account = offlineimap.accounts.SyncableAccount(
+ self.config, 'test1')
+
+ #Create 2 temporary maildir repos, for the account
+ self.create_maildir_repos(self.account)
+
+ def create_config(self):
+ """Create a fake ConfigParser object for syncing
+
+ It defines 'Account test1' syncing between Maildir 'local1'
+ and Maildir 'remote1'. Also create the directories for the
+ temporary Maildirs."""
+
+ config=offlineimap.CustomConfig.CustomConfigParser()
+ config.add_section('general')
+ config.set('general', 'accounts', 'test1')
+
+ config.add_section('Account test1')
+ config.set('Account test1','localrepository','local1')
+ config.set('Account test1','remoterepository','remote1')
+
+ #create 2 temporary directories for maildirs,
+ #we are responsible for deleting those again
+ tmpd1 = tempfile.mkdtemp(prefix='offlineimap_repo_')
+ tmpd2 = tempfile.mkdtemp(prefix='offlineimap_repo_')
+ self._tmpfiles.extend([tmpd1, tmpd2])
+
+ config.add_section('Repository local1')
+ config.set('Repository local1','type','Maildir')
+ config.set('Repository local1','localfolders',tmpd1)
+
+ config.add_section('Repository remote1')
+ config.set('Repository remote1','type','Maildir')
+ config.set('Repository remote1','localfolders',tmpd2)
+
+ #create and set temporary metadatadir too
+ metad = tempfile.mkdtemp(prefix='offlineimap_meta_')
+ self._tmpfiles.append(metad)
+ config.set('general', 'metadata', metad)
+
+ return config
+
+ def create_maildir_repos(self, account):
+ """Create the Maildir structure for account. Both repos names
+ are currently hardcoded to 'local1' and 'remote1' this
+ requires create_config to have run before.
+
+ :param account: The SyncableAccount() object"""
+
+ #create the account metadatadir
+ os.mkdir(self.account.getaccountmeta())
+
+ #save the original current directory. Repository requires us to change it :-(
+ #This is a FIXME :-(.
+ origcwd = os.getcwd()
+
+ # Create Maildir repositories and the INBOX folder
+ repo = offlineimap.repository.Maildir.MaildirRepository(
+ 'local1', self.account)
+ os.chdir(repo.root)
+ repo.makefolder('INBOX')
+
+ repo = offlineimap.repository.Maildir.MaildirRepository(
+ 'remote1', self.account)
+ os.chdir(repo.root)
+ repo.makefolder('INBOX')
+
+ #and back to the original current dir
+ os.chdir(origcwd)
+
+
+ def putmail(self, repo, foldername, uid=None, new = False):
+ """Create a new mail file that is in repo.
+
+ The folder must already exist.
+ Mail files will be automatically cleaned up if they are in one
+ of the two repositories.
+
+ :param repo: A Repository() object.
+ :param foldername: A string for the mailbox, e.g. INBOX
+ :param UID: Assign a random UID if None, or use as mail UID
+ :param new: if True, create mail in 'new' otehrwise in 'cur'
+ """
+ rtime = random.randint(1,99999999999999999)
+ if uid is None:
+ uid = random.randint(1,99999999999999999)
+ content = "From: XX <xx at yy.com>\n"+\
+ "To: gark <ch at ch.ch>\n"+\
+ "Subject: Firk\n\nGah"
+ if new:
+ flags=[]
+ else:
+ flags=["S"]
+ maildirfolder = repo.getfolder(foldername)
+ maildirfolder.savemessage(uid, content, flags, rtime)
+
+
+ def cleanup(self):
+ """Delete temporary files that have been created"""
+ for file in self._tmpfiles:
+ try:
+ os.unlink(file)
+ except OSError as e:
+ if e.errno == 21:
+ #file is a directory
+ shutil.rmtree(file)
+ self._cleaned_up = True
+
+ def __del__(self):
+ if not self._cleaned_up:
+ print "clean up after yourself, son!\nMake sure you call "\
+ "scaffold.cleanup() when your tests are done!!! Thanks."
+
+
+
+class TestOfflineImapFunctions(unittest.TestCase):
+ """Test various sync situations for Maildiry<->Maildir"""
+
+ def __init__(self, test, scaffold):
+ self.scaffold = scaffold
+ unittest.TestCase.__init__(self, test)
+
+ def test_01_simplemailsync(self):
+ """with initially empty repositories, create a mail on the
+ local one and sync. The test succeeds if afterwards the number
+ of mails on the remote repository is 1 and both repos have the
+ same UIDs."""
+ ui = offlineimap.ui.UIBase.getglobalui()
+ acc = self.scaffold.account
+
+ remoterepo = offlineimap.repository.Maildir.MaildirRepository(
+ acc.getconf('remoterepository'), acc)
+ localrepo = offlineimap.repository.Maildir.MaildirRepository(
+ acc.getconf('localrepository'), acc)
+ statusrepo = Repository(acc, 'status')
+
+ self.scaffold.putmail(localrepo, 'INBOX')
+
+ #the following is basically copied out of the syncfolder function
+
+ #create the folders on the local and statusrepo as on the remote
+ remoterepo.syncfoldersto(localrepo, [statusrepo])
+ folders = remoterepo.getfolders()
+
+ for remotefolder in folders:
+ offlineimap.accounts.syncfolder(acc.name, remoterepo, remotefolder,
+ localrepo, statusrepo, quick=False)
+
+ # make sure both repos contain the same files afterwards
+ # 1) get localfolder object, 2) comapre UIDs in both folders
+ localfolder = localrepo.\
+ getfolder(remotefolder.getvisiblename().\
+ replace(remoterepo.getsep(), localrepo.getsep()))
+ #syncfolder does "ui.registerthread", so we need to kludge
+ #and delete the threads manually here
+ ui.threadaccounts= {}
+
+ #sync finished for this folder. Succeed?
+ # We should have 1 message
+ self.assertEqual(remotefolder.getmessagecount(),1)
+ # and the UIDs should be identical
+ self.assertEqual(remotefolder.getmessageuidlist(),
+ localfolder.getmessageuidlist())
+
+
+ def test_02_repeat_sync_without_changes(self):
+ """Using the result of test01 as basis, we sync without
+ changing anything. Make sure that the sync does not change
+ anything either."""
+ ui = offlineimap.ui.UIBase.getglobalui()
+ acc = self.scaffold.account
+
+ remoterepo = offlineimap.repository.Maildir.MaildirRepository(
+ acc.getconf('remoterepository'), acc)
+ localrepo = offlineimap.repository.Maildir.MaildirRepository(
+ acc.getconf('localrepository'), acc)
+ statusrepo = Repository(acc, 'status')
+
+ #sync without having changed anything since the last test to see that
+ folders = remoterepo.getfolders()
+ for remotefolder in folders:
+ offlineimap.accounts.syncfolder(acc.name, remoterepo,
+ remotefolder, localrepo, statusrepo, quick=False)
+
+ #syncfolder does "ui.registerthread", so we need to kludge
+ #and delete the stuff manually afterwards
+ ui.threadaccounts= {}
+
+ # make sure both repos contain the same files afterwards
+ # 1) get localfolder object, 2) comapre UIDs in both folders
+ localfolder = localrepo.\
+ getfolder(remotefolder.getvisiblename().\
+ replace(remoterepo.getsep(), localrepo.getsep()))
+
+ #folder sync finish, succeeded?
+ # We should have 1 message
+ self.assertEqual(remotefolder.getmessagecount(),1)
+ # and the UIDs should be identical
+ self.assertEqual(remotefolder.getmessageuidlist(),
+ localfolder.getmessageuidlist())
+
+ #debug print the messages in the maildir
+ #for f in os.listdir(remoterepo.root+'/INBOX/new'):
+ # print "Repo1", f
+ #for f in os.listdir(localrepo.root+'/INBOX/cur'):
+ # print "Repo2", f
+
+ def test_03_delete_remote_message_and_change_local_flag(self):
+ """Using the result of test02 as basis, we delete the remote
+ message and modify the local flags. Then sync and see what
+ happens.
+ FIXME: Not Implemented yet"""
+ ui = offlineimap.ui.UIBase.getglobalui()
+ acc = self.scaffold.account
+
+ remoterepo = offlineimap.repository.Maildir.MaildirRepository(
+ acc.getconf('remoterepository'), acc)
+ localrepo = offlineimap.repository.Maildir.MaildirRepository(
+ acc.getconf('localrepository'), acc)
+ statusrepo = Repository(acc, 'status')
+
+
+
+class MailDirTestSuite(unittest.TestSuite):
+ def __init__(self):
+ self.scaffold = Folder2FolderScaffold()
+ unittest.TestSuite.__init__(self)
+ self.addTest(TestOfflineImapFunctions("test_01_simplemailsync", self.scaffold))
+ self.addTest(TestOfflineImapFunctions("test_02_repeat_sync_without_changes", self.scaffold))
+ self.addTest(TestOfflineImapFunctions("test_03_delete_remote_message_and_change_local_flag", self.scaffold))
+
+ def __del__(self):
+ self.scaffold.cleanup()
+
+
+if __name__ == '__main__':
+ runner = unittest.TextTestRunner()
+ runner.run(MailDirTestSuite())
+
+ #unittest.main()
--
1.7.1
More information about the OfflineIMAP-project
mailing list