Hello community,

here is the log from the commit of package offlineimap for openSUSE:Factory 
checked in at 2012-02-14 19:04:25
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/offlineimap (Old)
 and      /work/SRC/openSUSE:Factory/.offlineimap.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "offlineimap", Maintainer is "gre...@suse.com"

Changes:
--------
--- /work/SRC/openSUSE:Factory/offlineimap/offlineimap.changes  2012-01-19 
16:56:12.000000000 +0100
+++ /work/SRC/openSUSE:Factory/.offlineimap.new/offlineimap.changes     
2012-02-14 19:04:31.000000000 +0100
@@ -1,0 +2,14 @@
+Sun Feb  5 11:21:16 UTC 2012 - g...@opensuse.org
+
+- update to version 6.5.2.1
+  * fix python2.6 compatibility with the TTYUI backend (crash)
+  * fix TTYUI regression from 6.5.2 in refresh loop (crash)
+  * fix crashes related to UIDVALIDITY returning "None"
+  * make folders containing quotes work rather than crashing
+  * remove the Gmail "realdelete" option, as it could lead to
+    potential data loss.
+  * improve delete msg performance with SQLITE backend
+  * enforce basic UI when using the --info switch
+  * beginning of a test suite
+
+-------------------------------------------------------------------

Old:
----
  offlineimap-v6.5.2.tar.gz

New:
----
  offlineimap-v6.5.2.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ offlineimap.spec ++++++
--- /var/tmp/diff_new_pack.eY4StL/_old  2012-02-14 19:04:35.000000000 +0100
+++ /var/tmp/diff_new_pack.eY4StL/_new  2012-02-14 19:04:35.000000000 +0100
@@ -15,8 +15,9 @@
 # Please submit bugfixes or comments via http://bugs.opensuse.org/
 #
 
+
 Name:           offlineimap
-Version:        6.5.2
+Version:        6.5.2.1
 Release:        0
 Summary:        Powerful IMAP/Maildir Synchronization Tool
 License:        GPL-2.0+
@@ -47,7 +48,7 @@
 
 
 %prep
-%setup -q -n spaetz-offlineimap-091272d
+%setup -q -n %{name}
 sed -i '/^#!\/usr\/bin\/env/d' offlineimap/imaplib2.py
 
 %build

++++++ offlineimap-v6.5.2.tar.gz -> offlineimap-v6.5.2.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spaetz-offlineimap-091272d/Changelog.rst 
new/offlineimap/Changelog.rst
--- old/spaetz-offlineimap-091272d/Changelog.rst        2012-01-17 
01:40:41.000000000 +0100
+++ new/offlineimap/Changelog.rst       2012-02-04 21:11:44.000000000 +0100
@@ -11,6 +11,28 @@
   on releases. And because I'm lazy, it will also be used as a draft for the
   releases announces.
 
+OfflineIMAP v6.5.2.1 (2012-04-04)
+=====================================
+
+* Fix python2.6 compatibility with the TTYUI backend (crash)
+
+* Fix TTYUI regression from 6.5.2 in refresh loop (crash)
+
+* Fix crashes related to UIDVALIDITY returning "None"
+
+* Beginning of a test suite. So far there is only one test. Configure
+  test/credentials.conf and invoke with "python setup.py test"
+
+* Make folders containing quotes work rather than crashing
+  (reported by Mark Eichin)
+
+* Improve delete msg performance with SQLITE backend
+
+* Enforce basic UI when using the --info switch
+
+* Remove the Gmail "realdelete" option, as it could lead to potential
+  data loss.
+
 OfflineIMAP v6.5.2 (2012-01-17)
 ===============================
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spaetz-offlineimap-091272d/SubmittingPatches.rst 
new/offlineimap/SubmittingPatches.rst
--- old/spaetz-offlineimap-091272d/SubmittingPatches.rst        2012-01-17 
01:40:41.000000000 +0100
+++ new/offlineimap/SubmittingPatches.rst       2012-02-04 21:11:44.000000000 
+0100
@@ -1,6 +1,7 @@
 .. -*- coding: utf-8 -*-
 
 .. _mailing list: 
http://lists.alioth.debian.org/mailman/listinfo/offlineimap-project
+.. _commits mailing list: 
http://lists.offlineimap.org/listinfo.cgi/commits-offlineimap.org
 
 =================================================
 Checklist (and a short version for the impatient)
@@ -320,6 +321,12 @@
   tell you if your patch is merged in pu if you rebase on top of
   master).
 
+* You can follow upstream commits on
+`CIA.vc <http://cia.vc/stats/project/offlineimap>`,
+`Ohloh <http://www.ohloh.net/p/offlineimap>`,
+`GitHub <https://github.com/spaetz/offlineimap/commits/>`,
+or on the `commits mailing list`_.
+  
 .. * Read the git mailing list, the maintainer regularly posts messages
   entitled "What's cooking in git.git" and "What's in git.git" giving
   the status of various proposed changes.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spaetz-offlineimap-091272d/docs/FAQ.rst 
new/offlineimap/docs/FAQ.rst
--- old/spaetz-offlineimap-091272d/docs/FAQ.rst 2012-01-17 01:40:41.000000000 
+0100
+++ new/offlineimap/docs/FAQ.rst        2012-02-04 21:11:44.000000000 +0100
@@ -288,20 +288,30 @@
 
 The `sslcacertfile` file must contain an SSL certificate (or a concatenated
 certificates chain) in PEM format.  (See the documentation of
-`ssl.wrap_socket`_'s `certfile` parameter for the gory details.)  The following
-command should generate a file in the proper format::
+`ssl.wrap_socket`_'s `certfile` parameter for the gory details.)  You can use 
either openssl or gnutls to create a certificate file in the required format.
 
+#. via openssl::
     openssl s_client -CApath /etc/ssl/certs -connect ${hostname}:imaps 
-showcerts \
        | perl -ne 'print if /BEGIN/../END/; print STDERR if /return/' > 
$sslcacertfile
     ^D
 
-Before using the resulting file, ensure that openssl verified the certificate
-successfully.
+#. via gnutls::
+    gnutls-cli --print-cert -p imaps ${host} </dev/null | sed -n \
+    |     '/^-----BEGIN CERT/,/^-----END CERT/p' > $sslcacertfile
 
 The path `/etc/ssl/certs` is not standardized; your system may store
 SSL certificates elsewhere.  (On some systems it may be in
 `/usr/local/share/certs/`.)
 
+Before using the resulting file, ensure that openssl verified the certificate
+successfully. In case of problems, you can test the certificate using a 
command such as (credits to Daniel Shahaf for this) to verify the certificate::
+
+    % openssl s_client -CAfile $sslcacertfile -connect ${hostname}:imaps 2>&1 
</dev/null
+
+If the server uses STARTTLS, pass the -starttls option and the 'imap' port.
+
+Also, you can test using gnutls::
+  gnutls-cli --x509cafile certs/mail.mydomain.eu.cert -p 993 mail.mydomain.eu
 
 IMAP Server Notes
 =================
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spaetz-offlineimap-091272d/docs/dev-doc-src/API.rst 
new/offlineimap/docs/dev-doc-src/API.rst
--- old/spaetz-offlineimap-091272d/docs/dev-doc-src/API.rst     2012-01-17 
01:40:41.000000000 +0100
+++ new/offlineimap/docs/dev-doc-src/API.rst    2012-02-04 21:11:44.000000000 
+0100
@@ -13,6 +13,11 @@
 
  from offlineimap import OfflineImap
 
+The file ``SubmittingPatches.rst`` in the source distribution documents a
+number of resources and conventions you may find useful.  It will eventually
+be merged into the main documentation.
+.. TODO: merge SubmittingPatches.rst to the main documentation
+
 :mod:`offlineimap` -- The OfflineImap module
 =============================================
  
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spaetz-offlineimap-091272d/offlineimap/__init__.py 
new/offlineimap/offlineimap/__init__.py
--- old/spaetz-offlineimap-091272d/offlineimap/__init__.py      2012-01-17 
01:40:41.000000000 +0100
+++ new/offlineimap/offlineimap/__init__.py     2012-02-04 21:11:44.000000000 
+0100
@@ -1,7 +1,7 @@
 __all__ = ['OfflineImap']
 
 __productname__ = 'OfflineIMAP'
-__version__     = "6.5.2"
+__version__     = "6.5.2.1"
 __copyright__   = "Copyright 2002-2012 John Goerzen & contributors"
 __author__      = "John Goerzen"
 __author_email__= "j...@complete.org"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spaetz-offlineimap-091272d/offlineimap/accounts.py 
new/offlineimap/offlineimap/accounts.py
--- old/spaetz-offlineimap-091272d/offlineimap/accounts.py      2012-01-17 
01:40:41.000000000 +0100
+++ new/offlineimap/offlineimap/accounts.py     2012-02-04 21:11:44.000000000 
+0100
@@ -378,9 +378,11 @@
         statusfolder = statusrepos.getfolder(remotefolder.getvisiblename().\
                                              replace(remoterepos.getsep(),
                                                      statusrepos.getsep()))
-        if localfolder.getuidvalidity() == None:
-            # This is a new folder, so delete the status cache to be sure
-            # we don't have a conflict.
+        if localfolder.get_uidvalidity() == None:
+            # This is a new folder, so delete the status cache to be
+            # sure we don't have a conflict.
+            # TODO: This does not work. We always return a value, need
+            # to rework this...
             statusfolder.deletemessagelist()
 
         statusfolder.cachemessagelist()
@@ -398,22 +400,24 @@
         localfolder.cachemessagelist()
         ui.messagelistloaded(localrepos, localfolder, 
localfolder.getmessagecount())
 
-        # If either the local or the status folder has messages and there is a 
UID
-        # validity problem, warn and abort.  If there are no messages, UW IMAPd
-        # loses UIDVALIDITY.  But we don't really need it if both local 
folders are
-        # empty.  So, in that case, just save it off.
+        # If either the local or the status folder has messages and
+        # there is a UID validity problem, warn and abort.  If there are
+        # no messages, UW IMAPd loses UIDVALIDITY.  But we don't really
+        # need it if both local folders are empty.  So, in that case,
+        # just save it off.
         if localfolder.getmessagecount() or statusfolder.getmessagecount():
-            if not localfolder.isuidvalidityok():
+            if not localfolder.check_uidvalidity():
                 ui.validityproblem(localfolder)
                 localrepos.restore_atime()
                 return
-            if not remotefolder.isuidvalidityok():
+            if not remotefolder.check_uidvalidity():
                 ui.validityproblem(remotefolder)
                 localrepos.restore_atime()
                 return
         else:
-            localfolder.saveuidvalidity()
-            remotefolder.saveuidvalidity()
+            # Both folders empty, just save new UIDVALIDITY
+            localfolder.save_uidvalidity()
+            remotefolder.save_uidvalidity()
 
         # Load remote folder.
         ui.loadmessagelist(remoterepos, remotefolder)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/spaetz-offlineimap-091272d/offlineimap/folder/Base.py 
new/offlineimap/offlineimap/folder/Base.py
--- old/spaetz-offlineimap-091272d/offlineimap/folder/Base.py   2012-01-17 
01:40:41.000000000 +0100
+++ new/offlineimap/offlineimap/folder/Base.py  2012-02-04 21:11:44.000000000 
+0100
@@ -109,23 +109,32 @@
         basename = re.sub('(^|\/)\.$','\\1dot', basename)
         return basename
 
-    def isuidvalidityok(self):
-        """Does the cached UID match the real UID
+    def check_uidvalidity(self):
+        """Tests if the cached UIDVALIDITY match the real current one
 
-        If required it caches the UID. In this case the function is not
-        threadsafe. So don't attempt to call it from concurrent threads."""
+        If required it saves the UIDVALIDITY value. In this case the
+        function is not threadsafe. So don't attempt to call it from
+        concurrent threads.
 
-        if self.getsaveduidvalidity() != None:
-            return self.getsaveduidvalidity() == self.getuidvalidity()
+        :returns: Boolean indicating the match. Returns True in case it
+        implicitely saved the UIDVALIDITY."""
+
+        if self.get_saveduidvalidity() != None:
+            return self.get_saveduidvalidity() == self.get_uidvalidity()
         else:
-            self.saveuidvalidity()
-            return 1
+            self.save_uidvalidity()
+            return True
 
     def _getuidfilename(self):
+        """provides UIDVALIDITY cache filename for class internal purposes"""
         return os.path.join(self.repository.getuiddir(),
                             self.getfolderbasename())
 
-    def getsaveduidvalidity(self):
+    def get_saveduidvalidity(self):
+        """Return the previously cached UIDVALIDITY value
+
+        :returns: UIDVALIDITY as (long) number or None, if None had been
+            saved yet."""
         if hasattr(self, '_base_saved_uidvalidity'):
             return self._base_saved_uidvalidity
         uidfilename = self._getuidfilename()
@@ -137,21 +146,24 @@
             file.close()
         return self._base_saved_uidvalidity
 
-    def saveuidvalidity(self):
-        """Save the UID value of the folder to the status
+    def save_uidvalidity(self):
+        """Save the UIDVALIDITY value of the folder to the cache
 
         This function is not threadsafe, so don't attempt to call it
         from concurrent threads."""
-        newval = self.getuidvalidity()
+        newval = self.get_uidvalidity()
         uidfilename = self._getuidfilename()
 
-        file = open(uidfilename + ".tmp", "wt")
-        file.write("%d\n" % newval)
-        file.close()
+        with open(uidfilename + ".tmp", "wt") as file:
+            file.write("%d\n" % newval)
         os.rename(uidfilename + ".tmp", uidfilename)
         self._base_saved_uidvalidity = newval
 
-    def getuidvalidity(self):
+    def get_uidvalidity(self):
+        """Retrieve the current connections UIDVALIDITY value
+
+        This function needs to be implemented by each Backend
+        :returns: UIDVALIDITY as a (long) number"""
         raise NotImplementedException
 
     def cachemessagelist(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/spaetz-offlineimap-091272d/offlineimap/folder/Gmail.py 
new/offlineimap/offlineimap/folder/Gmail.py
--- old/spaetz-offlineimap-091272d/offlineimap/folder/Gmail.py  2012-01-17 
01:40:41.000000000 +0100
+++ new/offlineimap/offlineimap/folder/Gmail.py 2012-02-04 21:11:44.000000000 
+0100
@@ -18,16 +18,18 @@
 
 """Folder implementation to support features of the Gmail IMAP server.
 """
-
 from IMAP import IMAPFolder
-from offlineimap import imaputil
-
 
 class GmailFolder(IMAPFolder):
     """Folder implementation to support features of the Gmail IMAP server.
-    Specifically, deleted messages are moved to folder `Gmail.TRASH_FOLDER`
-    (by default: ``[Gmail]/Trash``) prior to expunging them, since
-    Gmail maps to IMAP ``EXPUNGE`` command to "remove label".
+
+    Removing a message from a folder will only remove the "label" from
+    the message and keep it in the "All mails" folder. To really delete
+    a message it needs to be copied to the Trash folder. However, this
+    is dangerous as our folder moves are implemented as a 1) delete in
+    one folder and 2) append to the other. If 2 comes before 1, this
+    will effectively delete the message from all folders. So we cannot
+    do that until we have a smarter folder move mechanism.
 
     For more information on the Gmail IMAP server:
       http://mail.google.com/support/bin/answer.py?answer=77657&topic=12815
@@ -35,31 +37,6 @@
 
     def __init__(self, imapserver, name, repository):
         super(GmailFolder, self).__init__(imapserver, name, repository)
-        self.realdelete = repository.getrealdelete(name)
         self.trash_folder = repository.gettrashfolder(name)
-        #: Gmail will really delete messages upon EXPUNGE in these folders
+        # Gmail will really delete messages upon EXPUNGE in these folders
         self.real_delete_folders =  [ self.trash_folder, 
repository.getspamfolder() ]
-
-    def deletemessages_noconvert(self, uidlist):
-        uidlist = [uid for uid in uidlist if uid in self.messagelist]
-        if not len(uidlist):
-            return
-
-        if self.realdelete and not (self.getname() in 
self.real_delete_folders):
-            # IMAP expunge is just "remove label" in this folder,
-            # so map the request into a "move into Trash"
-
-            imapobj = self.imapserver.acquireconnection()
-            try:
-                imapobj.select(self.getfullname())
-                result = imapobj.uid('copy',
-                                     imaputil.uid_sequence(uidlist),
-                                     self.trash_folder)
-                assert result[0] == 'OK', \
-                       "Bad IMAPlib result: %s" % result[0]
-            finally:
-                self.imapserver.releaseconnection(imapobj)
-            for uid in uidlist:
-                del self.messagelist[uid]
-        else:
-            IMAPFolder.deletemessages_noconvert(self, uidlist)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/spaetz-offlineimap-091272d/offlineimap/folder/IMAP.py 
new/offlineimap/offlineimap/folder/IMAP.py
--- old/spaetz-offlineimap-091272d/offlineimap/folder/IMAP.py   2012-01-17 
01:40:41.000000000 +0100
+++ new/offlineimap/offlineimap/folder/IMAP.py  2012-02-04 21:11:44.000000000 
+0100
@@ -65,17 +65,23 @@
     def getcopyinstancelimit(self):
         return 'MSGCOPY_' + self.repository.getname()
 
-    def getuidvalidity(self):
+    def get_uidvalidity(self):
+        """Retrieve the current connections UIDVALIDITY value
+
+        UIDVALIDITY value will be cached on the first call.
+        :returns: The UIDVALIDITY as (long) number."""
+        if hasattr(self, '_uidvalidity'):
+            # use cached value if existing
+            return self._uidvalidity
         imapobj = self.imapserver.acquireconnection()
         try:
-            # SELECT receives UIDVALIDITY response
+            # SELECT (if not already done) and get current UIDVALIDITY
             self.selectro(imapobj)
-            # note: we would want to use .response() here but that
-            # often seems to return [None], even though we have
-            # data. TODO
-            uidval = imapobj._get_untagged_response('UIDVALIDITY')
-            assert uidval != [None], "response('UIDVALIDITY') returned [None]!"
-            return long(uidval[-1])
+            typ, uidval = imapobj.response('UIDVALIDITY')
+            assert uidval != [None] and uidval != None, \
+                "response('UIDVALIDITY') returned [None]!"
+            self._uidvalidity = long(uidval[-1])
+            return self._uidvalidity
         finally:
             self.imapserver.releaseconnection(imapobj)
 
@@ -532,12 +538,13 @@
                     self.ui.msgtoreadonly(self, uid, content, flags)
                     return uid
 
-                #Do the APPEND
+                # Clean out existing APPENDUID responses and do APPEND
                 try:
-                    (typ, dat) = imapobj.append(self.getfullname(),
+                    imapobj.response('APPENDUID') # flush APPENDUID responses
+                    typ, dat = imapobj.append(self.getfullname(),
                                        imaputil.flagsmaildir2imap(flags),
                                        date, content)
-                    retry_left = 0 # Mark as success
+                    retry_left = 0                # Mark as success
                 except imapobj.abort, e:
                     # connection has been reset, release connection and retry.
                     retry_left -= 1
@@ -560,43 +567,38 @@
                         "failed (error). Server reponded: %s\nMessage content 
was: "
                         "%s" % (self, self.getrepository(), str(e), 
dbg_output),
                                            OfflineImapError.ERROR.MESSAGE)
-            # Checkpoint. Let it write out stuff, etc. Eg searches for
-            # just uploaded messages won't work if we don't do this.
-            (typ,dat) = imapobj.check()
-            assert(typ == 'OK')
-
-            # get the new UID. Test for APPENDUID response even if the
-            # server claims to not support it, as e.g. Gmail does :-(
-            if use_uidplus or imapobj._get_untagged_response('APPENDUID', 
True):
+
+            # get the new UID, default to 0 (=unknown)
+            uid = 0
+            if use_uidplus:
                 # get new UID from the APPENDUID response, it could look
                 # like OK [APPENDUID 38505 3955] APPEND completed with
-                # 38505 bein folder UIDvalidity and 3955 the new UID.
-                # note: we would want to use .response() here but that
-                # often seems to return [None], even though we have
-                # data. TODO
-                resp = imapobj._get_untagged_response('APPENDUID')
-                if resp == [None]:
+                # 38505 being folder UIDvalidity and 3955 the new UID.
+                typ, resp = imapobj.response('APPENDUID')
+                if resp == [None] or resp == None:
                     self.ui.warn("Server supports UIDPLUS but got no APPENDUID 
"
                                  "appending a message.")
-                    return 0
-                uid = long(resp[-1].split(' ')[1])
+                else:
+                    uid = long(resp[-1].split(' ')[1])
 
             else:
-                # we don't support UIDPLUS
+                # Don't support UIDPLUS
+                # Checkpoint. Let it write out stuff, etc. Eg searches for
+                # just uploaded messages won't work if we don't do this.
+                typ, dat = imapobj.check()
+                assert(typ == 'OK')
+
                 uid = self.savemessage_searchforheader(imapobj, headername,
                                                        headervalue)
-                # See docs for savemessage in Base.py for explanation of this 
and other return values
+                # If everything failed up to here, search the message
+                # manually TODO: rather than inserting and searching for our
+                # custom header, we should be searching the Message-ID and
+                # compare the message size...
                 if uid == 0:
-                    self.ui.debug('imap', 'savemessage: first attempt to get 
new UID failed. \
-                            Going to run a NOOP and try again.')
-                    assert(imapobj.noop()[0] == 'OK')
-                    uid = self.savemessage_searchforheader(imapobj, headername,
-                                                       headervalue)
-                    if uid == 0:
-                        self.ui.debug('imap', 'savemessage: second attempt to 
get new UID failed. \
-                                Going to try search headers manually')
-                        uid = self.savemessage_fetchheaders(imapobj, 
headername, headervalue)
-
+                    self.ui.debug('imap', 'savemessage: attempt to get new UID 
'
+                                  'UID failed. Search headers manually.')
+                    uid = self.savemessage_fetchheaders(imapobj, headername,
+                                                        headervalue)
         finally:
             self.imapserver.releaseconnection(imapobj)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/spaetz-offlineimap-091272d/offlineimap/folder/LocalStatusSQLite.py 
new/offlineimap/offlineimap/folder/LocalStatusSQLite.py
--- old/spaetz-offlineimap-091272d/offlineimap/folder/LocalStatusSQLite.py      
2012-01-17 01:40:41.000000000 +0100
+++ new/offlineimap/offlineimap/folder/LocalStatusSQLite.py     2012-02-04 
21:11:44.000000000 +0100
@@ -73,24 +73,32 @@
             if version < LocalStatusSQLiteFolder.cur_version:
                 self.upgrade_db(version)
 
-    def sql_write(self, sql, vars=None):
-        """execute some SQL retrying if the db was locked.
+    def sql_write(self, sql, vars=None, executemany=False):
+        """Execute some SQL, retrying if the db was locked.
 
-        :param sql: the SQL string passed to execute() :param args: the
-            variable values to `sql`. E.g. (1,2) or {uid:1, flags:'T'}. See
-            sqlite docs for possibilities.
+        :param sql: the SQL string passed to execute()
+        :param vars: the variable values to `sql`. E.g. (1,2) or {uid:1,
+            flags:'T'}. See sqlite docs for possibilities.
+        :param executemany: bool indicating whether we want to
+            perform conn.executemany() or conn.execute().
         :returns: the Cursor() or raises an Exception"""
         success = False
         while not success:
             self._dblock.acquire()
             try:
                 if vars is None:
-                    cursor = self.connection.execute(sql)
+                    if executemany:
+                        cursor = self.connection.executemany(sql)
+                    else:
+                        cursor = self.connection.execute(sql)
                 else:
-                    cursor = self.connection.execute(sql, vars)
+                    if executemany:
+                        cursor = self.connection.executemany(sql, vars)
+                    else:
+                        cursor = self.connection.execute(sql, vars)
                 success = True
                 self.connection.commit()
-            except sqlite.OperationalError, e:
+            except sqlite.OperationalError as e:
                 if e.args[0] == 'cannot commit - no transaction is active':
                     pass
                 elif e.args[0] == 'database is locked':
@@ -231,12 +239,23 @@
         flags = ''.join(sorted(flags))
         self.sql_write('UPDATE status SET flags=? WHERE id=?',(flags,uid))
 
+    def deletemessage(self, uid):
+        if not uid in self.messagelist:
+            return
+        self.sql_write('DELETE FROM status WHERE id=?', (uid, ))
+        del(self.messagelist[uid])
+
     def deletemessages(self, uidlist):
+        """Delete list of UIDs from status cache
+
+        This function uses sqlites executemany() function which is
+        much faster than iterating through deletemessage() when we have
+        many messages to delete."""
         # Weed out ones not in self.messagelist
         uidlist = [uid for uid in uidlist if uid in self.messagelist]
         if not len(uidlist):
             return
+        # arg2 needs to be an iterable of 1-tuples [(1,),(2,),...]
+        self.sql_write('DELETE FROM status WHERE id=?', zip(uidlist, ), True)
         for uid in uidlist:
             del(self.messagelist[uid])
-            #TODO: we want a way to do executemany(.., uidlist) to delete all
-            self.sql_write('DELETE FROM status WHERE id=?', (uid, ))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/spaetz-offlineimap-091272d/offlineimap/folder/Maildir.py 
new/offlineimap/offlineimap/folder/Maildir.py
--- old/spaetz-offlineimap-091272d/offlineimap/folder/Maildir.py        
2012-01-17 01:40:41.000000000 +0100
+++ new/offlineimap/offlineimap/folder/Maildir.py       2012-02-04 
21:11:44.000000000 +0100
@@ -83,8 +83,10 @@
         """Return the absolute file path to the Maildir folder (sans 
cur|new)"""
         return self._fullname
 
-    def getuidvalidity(self):
-        """Maildirs have no notion of uidvalidity, so we just return a magic
+    def get_uidvalidity(self):
+        """Retrieve the current connections UIDVALIDITY value
+
+        Maildirs have no notion of uidvalidity, so we just return a magic
         token."""
         return 42
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spaetz-offlineimap-091272d/offlineimap/imaputil.py 
new/offlineimap/offlineimap/imaputil.py
--- old/spaetz-offlineimap-091272d/offlineimap/imaputil.py      2012-01-17 
01:40:41.000000000 +0100
+++ new/offlineimap/offlineimap/imaputil.py     2012-02-04 21:11:44.000000000 
+0100
@@ -25,7 +25,12 @@
 except NameError:
     from sets import Set as set
 
-quotere = re.compile('^("(?:[^"]|\\\\")*")')
+# find the first quote in a string
+quotere = re.compile(
+    r"""(?P<quote>"(?:\\"|[^"])*") # Quote, possibly containing encoded
+                                   # quotation mark
+        \s*(?P<rest>.*)$           # Whitespace & remainder of string""",
+    re.VERBOSE)
 
 def debug(*args):
     msg = []
@@ -71,7 +76,7 @@
 
 def flags2hash(flags):
     """Converts IMAP response string from eg IMAP4.fetch() to a hash.
-    
+
     E.g. '(FLAGS (\\Seen Old) UID 4807)' leads to
     {'FLAGS': '(\\Seen Old)', 'UID': '4807'}"""
     return options2hash(flagsplit(flags))
@@ -124,10 +129,11 @@
                 retval.extend(imapsplit(arg))
         debug("imapsplit() non-string: returning %s" % str(retval))
         return retval
-        
+
     workstr = imapstring.strip()
     retval = []
     while len(workstr):
+        # handle parenthized fragments (...()...)
         if workstr[0] == '(':
             rparenc = 1 # count of right parenthesis to match
             rpareni = 1 # position to examine
@@ -141,9 +147,10 @@
             workstr = workstr[rpareni:].lstrip()
             retval.append(parenlist)
         elif workstr[0] == '"':
-            quotelist = quotere.search(workstr).group(1)
-            workstr = workstr[len(quotelist):].lstrip()
-            retval.append(quotelist)
+            # quoted fragments '"...\"..."'
+            m = quotere.match(workstr)
+            retval.append(m.group('quote'))
+            workstr = m.group('rest')
         else:
             splits = string.split(workstr, maxsplit = 1)
             splitslen = len(splits)
@@ -162,7 +169,7 @@
                 # There was not even an unquoted word.
                 break
     return retval
-            
+
 flagmap = [('\\Seen', 'S'),
            ('\\Answered', 'R'),
            ('\\Flagged', 'F'),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spaetz-offlineimap-091272d/offlineimap/init.py 
new/offlineimap/offlineimap/init.py
--- old/spaetz-offlineimap-091272d/offlineimap/init.py  2012-01-17 
01:40:41.000000000 +0100
+++ new/offlineimap/offlineimap/init.py 2012-02-04 21:11:44.000000000 +0100
@@ -200,6 +200,7 @@
             # TODO, make use of chosen ui for logging
             logging.warning('Using old interface name, consider using one '
                             'of %s' % ', '.join(UI_LIST.keys()))
+        if options.diagnostics: ui_type = 'basic' # enforce basic UI for --info
         try:
             # create the ui class
             self.ui = UI_LIST[ui_type.lower()](config)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/spaetz-offlineimap-091272d/offlineimap/repository/Gmail.py 
new/offlineimap/offlineimap/repository/Gmail.py
--- old/spaetz-offlineimap-091272d/offlineimap/repository/Gmail.py      
2012-01-17 01:40:41.000000000 +0100
+++ new/offlineimap/offlineimap/repository/Gmail.py     2012-02-04 
21:11:44.000000000 +0100
@@ -64,11 +64,6 @@
     def getfoldertype(self):
         return folder.Gmail.GmailFolder
 
-    def getrealdelete(self, foldername):
-        # XXX: `foldername` is currently ignored - the `realdelete`
-        # setting is repository-wide
-        return self.getconfboolean('realdelete', 0)
-
     def gettrashfolder(self, foldername):
         #: Where deleted mail should be moved
         return  self.getconf('trashfolder','[Gmail]/Trash')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spaetz-offlineimap-091272d/offlineimap/ui/Machine.py 
new/offlineimap/offlineimap/ui/Machine.py
--- old/spaetz-offlineimap-091272d/offlineimap/ui/Machine.py    2012-01-17 
01:40:41.000000000 +0100
+++ new/offlineimap/offlineimap/ui/Machine.py   2012-02-04 21:11:44.000000000 
+0100
@@ -45,7 +45,7 @@
     def _msg(s, msg):
         s._printData('_display', msg)
 
-    def warn(s, msg, minor = 0):
+    def warn(self, msg, minor = 0):
         # TODO, remove and cleanup the unused minor stuff
         self.logger.warning("%s:%s:%s:%s" % (
                 'warn', '', currentThread().getName(), msg))
@@ -70,7 +70,7 @@
     def validityproblem(s, folder):
         s._printData('validityproblem', "%s\n%s\n%s\n%s" % \
                 (folder.getname(), folder.getrepository().getname(),
-                 folder.getsaveduidvalidity(), folder.getuidvalidity()))
+                 folder.get_saveduidvalidity(), folder.get_uidvalidity()))
 
     def connecting(s, hostname, port):
         s._printData('connecting', "%s\n%s" % (hostname, str(port)))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spaetz-offlineimap-091272d/offlineimap/ui/TTY.py 
new/offlineimap/offlineimap/ui/TTY.py
--- old/spaetz-offlineimap-091272d/offlineimap/ui/TTY.py        2012-01-17 
01:40:41.000000000 +0100
+++ new/offlineimap/offlineimap/ui/TTY.py       2012-02-04 21:11:44.000000000 
+0100
@@ -17,6 +17,7 @@
 
 import logging
 import sys
+import time
 from getpass import getpass
 from offlineimap import banner
 from offlineimap.ui.UIBase import UIBase
@@ -24,12 +25,14 @@
 class TTYFormatter(logging.Formatter):
     """Specific Formatter that adds thread information to the log output"""
     def __init__(self, *args, **kwargs):
-        super(TTYFormatter, self).__init__(*args, **kwargs)
+        #super() doesn't work in py2.6 as 'logging' uses old-style class
+        logging.Formatter.__init__(self, *args, **kwargs)
         self._last_log_thread = None
 
     def format(self, record):
         """Override format to add thread information"""
-        log_str = super(TTYFormatter, self).format(record)
+        #super() doesn't work in py2.6 as 'logging' uses old-style class
+        log_str = logging.Formatter.format(self, record)
         # If msg comes from a different thread than our last, prepend
         # thread info.  Most look like 'Account sync foo' or 'Folder
         # sync foo'.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spaetz-offlineimap-091272d/offlineimap/ui/UIBase.py 
new/offlineimap/offlineimap/ui/UIBase.py
--- old/spaetz-offlineimap-091272d/offlineimap/ui/UIBase.py     2012-01-17 
01:40:41.000000000 +0100
+++ new/offlineimap/offlineimap/ui/UIBase.py    2012-02-04 21:11:44.000000000 
+0100
@@ -309,9 +309,9 @@
     def validityproblem(self, folder):
         self.logger.warning("UID validity problem for folder %s (repo %s) "
                             "(saved %d; got %d); skipping it. Please see FAQ "
-                            "and manual how to handle this." % \
+                            "and manual on how to handle this." % \
                (folder, folder.getrepository(),
-                folder.getsaveduidvalidity(), folder.getuidvalidity()))
+                folder.get_saveduidvalidity(), folder.get_uidvalidity()))
 
     def loadmessagelist(self, repos, folder):
         self.logger.debug("Loading message list for %s[%s]" % (
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spaetz-offlineimap-091272d/offlineimap.conf 
new/offlineimap/offlineimap.conf
--- old/spaetz-offlineimap-091272d/offlineimap.conf     2012-01-17 
01:40:41.000000000 +0100
+++ new/offlineimap/offlineimap.conf    2012-02-04 21:11:44.000000000 +0100
@@ -1,27 +1,11 @@
-# Sample configuration file
-# Copyright (C) 2002-2011 John Goerzen & contributors
-#
-#    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
+# Offlineimap sample configuration file
 # This file documents all possible options and can be quite scary.
 # Looking for a quick start?  Take a look at offlineimap.conf.minimal.
 
-# Settings support interpolation. This means values can contain python
-# format strings which refer to other values in the same section, or
-# values in a special DEFAULT section. This allows you for example to
-# use common settings for multiple accounts:
+# Settings generally support interpolation. This means values can
+# contain python format strings which refer to other values in the same
+# section, or values in a special DEFAULT section. This allows you for
+# example to use common settings for multiple accounts:
 #
 # [Repository Gmail1]
 # trashfolder: %(gmailtrashfolder)s
@@ -33,7 +17,6 @@
 # gmailtrashfolder = [Google Mail]/Papierkorb
 #
 # would set the trashfolder setting for your German gmail accounts.
-    
 
 
 ##################################################
@@ -45,13 +28,12 @@
 # This specifies where offlineimap is to store its metadata.
 # This directory will be created if it does not already exist.
 
-metadata = ~/.offlineimap
+#metadata = ~/.offlineimap
 
 # This variable specifies which accounts are defined.  Separate them
 # with commas.  Account names should be alphanumeric only.
 # You will need to specify one section per account below.  You may
 # not use "general" for an account name.
-#
 
 accounts = Test
 
@@ -65,7 +47,7 @@
 # since any given sync run never "finishes" due to a timer, you will never
 # sync your additional accounts if this is 1.
 
-maxsyncaccounts = 1
+#maxsyncaccounts = 1
 
 # You can specify one or more user interface modules for OfflineIMAP
 # to use.  OfflineIMAP will try the first in the list, and if it
@@ -82,7 +64,7 @@
 #
 # You can override this with a command-line option -u.
 
-ui = Blinkenlights
+#ui = basic
 
 # If you try to synchronize messages to a folder which the IMAP server
 # considers read-only, OfflineIMAP will generate a warning.  If you want
@@ -92,7 +74,8 @@
 # will prevent OfflineIMAP from propagating those changes to the IMAP
 # server.  Note that ignore-readonly is unrelated to the "readonly"
 # setting which prevents a repository from being modified at all.
-ignore-readonly = no
+
+#ignore-readonly = no
 
 ########## Advanced settings
 
@@ -334,8 +317,9 @@
 # fingerprint here. OfflineImap will verify that the server fingerprint
 # has not changed on each connect and refuse to connect otherwise.
 # You can also configure this in addition to CA certificate validation
-# above and it will check both ways.  cert_fingerprint =
-# <SHA1_of_server_certificate_here>
+# above and it will check both ways.
+
+#cert_fingerprint = <SHA1_of_server_certificate_here>
 
 # Specify the port.  If not specified, use a default port.
 # remoteport = 993
@@ -421,7 +405,7 @@
 # cases, it may slow things down.  The safe answer is 1.  You should
 # probably never set it to a value more than 5.
 
-maxconnections = 2
+#maxconnections = 2
 
 # OfflineIMAP normally closes IMAP server connections between refreshes if
 # the global option autorefresh is specified.  If you wish it to keep the
@@ -429,15 +413,15 @@
 # false.  Keeping the connection open means a faster sync start the
 # next time and may use fewer server resources on connection, but uses
 # more server memory.  This setting has no effect if autorefresh is not set.
-
-holdconnectionopen = no
+#
+#holdconnectionopen = no
 
 # If you want to have "keepalives" sent while waiting between syncs,
 # specify the amount of time IN SECONDS between keepalives here.  Note that
 # sometimes more than this amount of time might pass, so don't make it
 # tight.  This setting has no effect if autorefresh and holdconnectionopen
 # are not both set.
-
+#
 # keepalive = 60
 
 # Normally, OfflineIMAP will expunge deleted messages from the server.
@@ -447,11 +431,12 @@
 # setting; otherwise, the messgaes will just pile up there forever.
 # Therefore, this setting is definitely NOT recommended.
 #
-# expunge = no
+#expunge = no
 
 # Specify whether to process all mail folders on the server, or only
 # those listed as "subscribed".
-subscribedonly = no
+#
+#subscribedonly = no
 
 # You can specify a folder translator.  This must be a eval-able
 # Python expression that takes a foldername arg and returns the new
@@ -525,7 +510,8 @@
 # repository will not be modified during synchronization. Use to
 # e.g. backup an IMAP server. The readonly setting can be applied to any
 # type of Repository (Maildir, Imap, etc).
-readonly = False
+#
+#readonly = False
 
 [Repository GmailExample]
 
@@ -539,28 +525,12 @@
 # `remoteport`, `tunnel` and `ssl`.  (See
 # http://mail.google.com/support/bin/answer.py?answer=78799&topic=12814)
 # Any attempt to set those parameters will be silently ignored.
-#
 
 type = Gmail
 
 # Specify the Gmail user name. This is the only mandatory parameter.
 remoteuser = usern...@gmail.com
 
-# WARNING: READ THIS BEFORE CONSIDERING TO CHANGE IT!  Deleting a
-# message from a Gmail folder via the IMAP interface will just remove
-# that folder's label from the message: the message will continue to
-# exist in the '[Gmail]/All Mail' folder.  If `realdelete` is set to
-# `True`, then deleted messages will be moved to the '[Gmail]/Trash'
-# folder.  BEWARE: this will immediately delete a messages from *all
-# folders* it belongs to! AS OFFLINEIMAP IMPLEMENTS FOLDER MOVES AS 1)
-# AN ADD and 2) A DELETE (the order can vary), THIS MEANS THAT A FOLDER
-# MOVE CAN CAUSE DATALOSS. DO NOT USE IT AND MOVE MAIL TO
-# "[Gmail]/Trash" TO DELETE MAIL FROM "[Gmail]/All Mail"!  See the
-# analysis at
-# http://article.gmane.org/gmane.mail.imap.offlineimap.general/5265 See
-# http://mail.google.com/support/bin/answer.py?answer=77657&topic=12815
-# realdelete = no !!!READ ABOVE BEFORE USING
-
 # The trash folder name may be different from [Gmail]/Trash
 # for example on german googlemail, this setting should be
 #
@@ -571,4 +541,5 @@
 # spamfolder = [Google Mail]/Spam
 
 # Enable 1-way synchronization. See above for explanation.
-readonly = False
+#
+#readonly = False
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spaetz-offlineimap-091272d/setup.py 
new/offlineimap/setup.py
--- old/spaetz-offlineimap-091272d/setup.py     2012-01-17 01:40:41.000000000 
+0100
+++ new/offlineimap/setup.py    2012-02-04 21:11:44.000000000 +0100
@@ -20,11 +20,32 @@
 #    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-# END OF COPYRIGHT #
 
-
-from distutils.core import setup
+import os
+from distutils.core import setup, Command
 import offlineimap
+import logging
+from test.OLItest import TextTestRunner, TestLoader, OLITestLib
+
+class TestCommand(Command):
+    """runs the OLI testsuite"""
+    description = "Runs the test suite"
+    user_options = []
+
+    def initialize_options(self):
+        pass
+
+    def finalize_options(self):
+        pass
+
+    def run(self):
+        logging.basicConfig(format='%(message)s')
+        # set credentials and OfflineImap command to be executed:
+        OLITestLib(cred_file='./test/credentials.conf', cmd='./offlineimap.py')
+        suite = TestLoader().discover('./test/tests')
+        #TODO: failfast does not seem to exist in python2.6?
+        TextTestRunner(verbosity=2,failfast=True).run(suite)
+
 
 setup(name = "offlineimap",
       version = offlineimap.__version__,
@@ -36,6 +57,7 @@
                   'offlineimap.repository', 'offlineimap.ui'],
       scripts = ['bin/offlineimap'],
       license = offlineimap.__copyright__ + \
-                ", Licensed under the GPL version 2"
+                ", Licensed under the GPL version 2",
+      cmdclass = { 'test': TestCommand}
 )
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spaetz-offlineimap-091272d/test/.gitignore 
new/offlineimap/test/.gitignore
--- old/spaetz-offlineimap-091272d/test/.gitignore      1970-01-01 
01:00:00.000000000 +0100
+++ new/offlineimap/test/.gitignore     2012-02-04 21:11:44.000000000 +0100
@@ -0,0 +1,2 @@
+credentials.conf
+tmp_*
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/spaetz-offlineimap-091272d/test/OLItest/TestRunner.py 
new/offlineimap/test/OLItest/TestRunner.py
--- old/spaetz-offlineimap-091272d/test/OLItest/TestRunner.py   1970-01-01 
01:00:00.000000000 +0100
+++ new/offlineimap/test/OLItest/TestRunner.py  2012-02-04 21:11:44.000000000 
+0100
@@ -0,0 +1,132 @@
+# Copyright (C) 2012- Sebastian Spaeth & contributors
+#
+#    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 St, Fifth Floor, Boston, MA  02110-1301 USA
+import unittest
+import logging
+import os
+import sys
+import shutil
+import subprocess
+import tempfile
+from ConfigParser import SafeConfigParser
+from . import default_conf
+
+class OLITestLib():
+    cred_file = None
+    testdir = None
+    """Absolute path of the current temporary test directory"""
+    cmd = None
+    """command that will be executed to invoke offlineimap"""
+
+    def __init__(self, cred_file = None, cmd='offlineimap'):
+        """
+
+        :param cred_file: file of the configuration
+            snippet for authenticating against the test IMAP server(s).
+        :param cmd: command that will be executed to invoke offlineimap"""
+        OLITestLib.cred_file = cred_file
+        if not os.path.isfile(cred_file):
+            raise UserWarning("Please copy 'credentials.conf.sample' to '%s' "
+                "and set your credentials there." % cred_file)
+        OLITestLib.cmd = cmd
+
+    @classmethod
+    def create_test_dir(cls, suffix=''):
+        """Creates a test directory and places OLI config there
+
+        Note that this is a class method. There can only be one test
+        directory at a time. OLITestLib is not suited for running
+        several tests in parallel.  The user is responsible for
+        cleaning that up herself."""
+        # creating temporary dir for testing in same dir as credentials.conf
+        cls.testdir = os.path.abspath(
+            tempfile.mkdtemp(prefix='tmp_%s_'%suffix,
+                             dir=os.path.dirname(cls.cred_file)))
+        cls.create_config_file()
+        return cls.testdir
+
+    @classmethod
+    def create_config_file(cls):
+        """Creates a OLI configuration file
+
+        It is created in testdir (so create_test_dir has to be called
+        earlier) using the credentials information given (so they had to
+        be set earlier). Failure to do either of them will raise an
+        AssertionException."""
+        assert cls.cred_file != None
+        assert cls.testdir != None
+        config = SafeConfigParser()
+        config.readfp(default_conf)
+        config.read(cls.cred_file)
+        config.set("general", "metadata", cls.testdir)
+        localfolders = os.path.join(cls.testdir, 'mail')
+        config.set("Repository Maildir", "localfolders", localfolders)
+        with open(os.path.join(cls.testdir, 'offlineimap.conf'), "wa") as f:
+            config.write(f)
+
+    @classmethod
+    def delete_test_dir(cls):
+        """Deletes the current test directory
+
+        The users is responsible for cleaning that up herself."""
+        if os.path.isdir(cls.testdir):
+            shutil.rmtree(cls.testdir)
+
+    @classmethod
+    def run_OLI(cls):
+        """Runs OfflineImap
+
+        :returns: (rescode, stdout)
+        """
+        try:
+            output = subprocess.check_output(
+                [cls.cmd,
+                 "-c%s" % os.path.join(cls.testdir, 'offlineimap.conf')],
+                shell=False)
+        except subprocess.CalledProcessError as e:
+            return (e.returncode, e.output)
+        return (0, output)
+
+    @classmethod
+    def create_maildir(cls, folder):
+        """Create empty maildir 'folder' in our test maildir
+
+        Does not fail if it already exists"""
+        assert cls.testdir != None
+        maildir = os.path.join(cls.testdir, 'mail', folder)
+        for subdir in ('','tmp','cur','new'):
+            try:
+                os.mkdir(os.path.join(maildir, subdir))
+            except OSError as e:
+                if e.errno != 17: # 'already exists' is ok.
+                    raise
+
+    @classmethod
+    def count_maildir_mails(cls, folder):
+        """Returns the number of mails in maildir 'folder'
+
+        Counting only those in cur&new (ignoring tmp)."""
+        assert cls.testdir != None
+        maildir = os.path.join(cls.testdir, 'mail', folder)
+
+        boxes, mails = 0, 0
+        for dirpath, dirs, files in os.walk(maildir, False):
+            if set(dirs) == set(['cur', 'new', 'tmp']):
+                # New maildir folder
+                boxes += 1
+                #raise RuntimeError("%s is not Maildir" % maildir)
+            if dirpath.endswith(('/cur', '/new')):
+                mails += len(files)
+        return boxes, mails
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spaetz-offlineimap-091272d/test/OLItest/__init__.py 
new/offlineimap/test/OLItest/__init__.py
--- old/spaetz-offlineimap-091272d/test/OLItest/__init__.py     1970-01-01 
01:00:00.000000000 +0100
+++ new/offlineimap/test/OLItest/__init__.py    2012-02-04 21:11:44.000000000 
+0100
@@ -0,0 +1,34 @@
+# OfflineImap test library
+# Copyright (C) 2012- Sebastian Spaeth & contributors
+#
+#    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 St, Fifth Floor, Boston, MA  02110-1301 USA
+
+__all__ = ['OLITestLib', 'TextTestRunner','TestLoader']
+
+__productname__ = 'OfflineIMAP Test suite'
+__version__     = '0'
+__copyright__   = "Copyright 2012- Sebastian Spaeth & contributors"
+__author__      = 'Sebastian Spaeth'
+__author_email__= 'sebast...@sspaeth.de'
+__description__ = 'Moo'
+__license__  = "Licensed under the GNU GPL v2+ (v2 or any later version)"
+__homepage__ = "http://offlineimap.org";
+banner = """%(__productname__)s %(__version__)s
+  %(__license__)s""" % locals()
+
+import unittest
+from unittest import TestLoader, TextTestRunner
+from globals import default_conf
+from TestRunner import OLITestLib
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spaetz-offlineimap-091272d/test/OLItest/globals.py 
new/offlineimap/test/OLItest/globals.py
--- old/spaetz-offlineimap-091272d/test/OLItest/globals.py      1970-01-01 
01:00:00.000000000 +0100
+++ new/offlineimap/test/OLItest/globals.py     2012-02-04 21:11:44.000000000 
+0100
@@ -0,0 +1,37 @@
+#Constants, that don't rely on anything else in the module
+# Copyright (C) 2012- Sebastian Spaeth & contributors
+#
+#    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 St, Fifth Floor, Boston, MA  02110-1301 USA
+from cStringIO import StringIO
+
+default_conf=StringIO("""[general]
+#will be set automatically
+metadata = 
+accounts = test
+ui = quiet
+
+[Account test]
+localrepository = Maildir
+remoterepository = IMAP
+
+[Repository Maildir]
+Type = Maildir
+# will be set automatically during tests
+localfolders = 
+
+[Repository IMAP]
+type=IMAP
+folderfilter= lambda f: f.startswith('INBOX.OLItest')
+""")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spaetz-offlineimap-091272d/test/README 
new/offlineimap/test/README
--- old/spaetz-offlineimap-091272d/test/README  1970-01-01 01:00:00.000000000 
+0100
+++ new/offlineimap/test/README 2012-02-04 21:11:44.000000000 +0100
@@ -0,0 +1,18 @@
+Documentation for the OfflineImap Test suite.
+
+How to run the tests
+====================
+
+- Copy the credentials.conf.sample to credentials.conf and insert
+  credentials for an IMAP account and a Gmail account. Delete the Gmail
+  section if you don't have a Gmail account. Do note, that the tests
+  will change the account and upload/delete/modify it's contents and
+  folder structure. So don't use a real used account here...
+
+- go to the top level dir (one above this one) and execute:
+  'python setup.py test'
+
+System requirements
+===================
+
+This test suite depend on python>=2.7 to run out of the box. If you want to 
run this with python 2.6 you will need to install the backport from 
http://pypi.python.org/pypi/unittest2 instead.
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/spaetz-offlineimap-091272d/test/credentials.conf.sample 
new/offlineimap/test/credentials.conf.sample
--- old/spaetz-offlineimap-091272d/test/credentials.conf.sample 1970-01-01 
01:00:00.000000000 +0100
+++ new/offlineimap/test/credentials.conf.sample        2012-02-04 
21:11:44.000000000 +0100
@@ -0,0 +1,13 @@
+[Repository IMAP]
+type = IMAP
+remotehost = localhost
+ssl = no
+#sslcacertfile = 
+#cert_fingerprint = 
+remoteuser = user@domain
+remotepass = SeKr3t
+
+[Repository Gmail]
+type = Gmail
+remoteuser = user@domain
+remotepass = SeKr3t
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/spaetz-offlineimap-091272d/test/tests/test_01_basic.py 
new/offlineimap/test/tests/test_01_basic.py
--- old/spaetz-offlineimap-091272d/test/tests/test_01_basic.py  1970-01-01 
01:00:00.000000000 +0100
+++ new/offlineimap/test/tests/test_01_basic.py 2012-02-04 21:11:44.000000000 
+0100
@@ -0,0 +1,74 @@
+# Copyright (C) 2012- Sebastian Spaeth & contributors
+#
+#    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 St, Fifth Floor, Boston, MA  02110-1301 USA
+import random
+import unittest
+import logging
+import os, sys
+from test.OLItest import OLITestLib
+
+def setUpModule():
+    logging.info("Set Up test module %s" % __name__)
+    tdir = OLITestLib.create_test_dir(suffix=__name__)
+
+def tearDownModule():
+    logging.info("Tear Down test module")
+    OLITestLib.delete_test_dir()
+
+#Stuff that can be used
+#self.assertEqual(self.seq, range(10))
+# should raise an exception for an immutable sequence
+#self.assertRaises(TypeError, random.shuffle, (1,2,3))
+#self.assertTrue(element in self.seq)
+#self.assertFalse(element in self.seq)
+
+class TestBasicFunctions(unittest.TestCase):
+    #@classmethod
+    #def setUpClass(cls):
+    #This is run before all tests in this class
+    #    cls._connection = createExpensiveConnectionObject()
+
+    #@classmethod
+    #This is run after all tests in this class
+    #def tearDownClass(cls):
+    #    cls._connection.destroy()
+
+    # This will be run before each test
+    #def setUp(self):
+    #    self.seq = range(10)
+
+    def test_01_olistartup(self):
+        """Tests if OLI can be invoked without exceptions
+
+        It syncs all "OLItest* (specified in the default config) to our
+        local Maildir at keeps it there."""
+        code, res = OLITestLib.run_OLI()
+        self.assertEqual(res, "")
+
+        boxes, mails = OLITestLib.count_maildir_mails('')
+        logging.warn("%d boxes and %d mails" % (boxes, mails))
+
+    def test_02_createdir(self):
+        """Create local OLItest 1 & OLItest "1" maildir, sync
+
+        Folder names with quotes used to fail and have been fixed, so
+        one is included here as a small challenge."""
+        OLITestLib.create_maildir('INBOX.OLItest 1')
+        OLITestLib.create_maildir('INBOX.OLItest "1"')
+        code, res = OLITestLib.run_OLI()
+        #logging.warn("%s %s "% (code, res))
+        self.assertEqual(res, "")
+        boxes, mails = OLITestLib.count_maildir_mails('')
+        logging.warn("%d boxes and %d mails" % (boxes, mails))

-- 
To unsubscribe, e-mail: opensuse-commit+unsubscr...@opensuse.org
For additional commands, e-mail: opensuse-commit+h...@opensuse.org

Reply via email to