Adding some unit tests
authorepage <eopage@byu.net>
Thu, 28 May 2009 03:04:59 +0000 (03:04 +0000)
committerepage <eopage@byu.net>
Thu, 28 May 2009 03:04:59 +0000 (03:04 +0000)
git-svn-id: file:///svnroot/gc-dialer/trunk@337 c39d3808-3fe2-4d86-a59f-b7f623ee9f21

src/dc_glade.py
support/builddeb.py
tests/basic_data/settings.ini [new file with mode: 0644]
tests/test_startup.py [new file with mode: 0644]
tests/test_utils.py [new file with mode: 0644]

index 845f57c..520fb92 100755 (executable)
@@ -19,6 +19,7 @@ License along with this library; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
 @bug Need to add unit tests
+@todo Look into an actor system
 @bug Session timeouts are bad, possible solutions:
        @li For every X minutes, if logged in, attempt login
        @li Restructure so code handling login/dial/sms is beneath everything else and login attempts are made if those fail
@@ -308,7 +309,7 @@ class Dialcentral(object):
                with gtk_toolbox.gtk_lock():
                        self.load_settings(config)
 
-               self.attempt_login(2)
+               gtk_toolbox.asynchronous_gtk_message(self._spawn_attempt_login)(2)
 
        def attempt_login(self, numOfAttempts = 10, force = False):
                """
@@ -341,6 +342,11 @@ class Dialcentral(object):
                        with gtk_toolbox.gtk_lock():
                                self._errorDisplay.push_exception(e)
 
+       def _spawn_attempt_login(self, *args):
+               backgroundLogin = threading.Thread(target=self.attempt_login, args=args)
+               backgroundLogin.setDaemon(True)
+               backgroundLogin.start()
+
        def refresh_session(self):
                """
                @note Thread agnostic
@@ -571,9 +577,7 @@ class Dialcentral(object):
                if status == conic.STATUS_CONNECTED:
                        self._deviceIsOnline = True
                        if self._initDone:
-                               backgroundLogin = threading.Thread(target=self.attempt_login, args=[2])
-                               backgroundLogin.setDaemon(True)
-                               backgroundLogin.start()
+                               self._spawn_attempt_login(2)
                elif status == conic.STATUS_DISCONNECTED:
                        self._deviceIsOnline = False
                        if self._initDone:
@@ -607,9 +611,7 @@ class Dialcentral(object):
                self._contactsViews[self._selectedBackendId].clear()
                self._change_loggedin_status(self.NULL_BACKEND)
 
-               backgroundLogin = threading.Thread(target=self.attempt_login, args=[2, True])
-               backgroundLogin.setDaemon(True)
-               backgroundLogin.start()
+               self._spawn_attempt_login(2, True)
 
        def _on_notebook_switch_page(self, notebook, page, page_num):
                if page_num == self.RECENT_TAB:
index fe2925d..6b3da48 100755 (executable)
@@ -1,6 +1,10 @@
 #!/usr/bin/python2.5
 
-from py2deb import *
+import os
+import sys
+
+import py2deb
+
 import constants
 
 
@@ -115,7 +119,7 @@ if __name__ == "__main__":
        except:
                pass
 
-       p = Py2deb(__appname__)
+       p = py2deb.Py2deb(__appname__)
        p.description = __description__
        p.author = __author__
        p.mail = __email__
diff --git a/tests/basic_data/settings.ini b/tests/basic_data/settings.ini
new file mode 100644 (file)
index 0000000..d9a6c81
--- /dev/null
@@ -0,0 +1,37 @@
+[1 - Contacts]
+
+[2 - Account Info]
+callback = 
+
+[1 - Recent Calls]
+
+[2 - Messages]
+
+[0 - Messages]
+
+[1 - Messages]
+
+[2 - Dialpad]
+
+[2 - Contacts]
+
+[0 - Recent Calls]
+
+[DialCentral]
+active = 0
+bin_blob_0 = 
+bin_blob_1 = 
+
+[1 - Account Info]
+callback = 
+
+[1 - Dialpad]
+
+[0 - Dialpad]
+
+[0 - Account Info]
+
+[0 - Contacts]
+
+[2 - Recent Calls]
+
diff --git a/tests/test_startup.py b/tests/test_startup.py
new file mode 100644 (file)
index 0000000..88d8807
--- /dev/null
@@ -0,0 +1,90 @@
+from __future__ import with_statement
+
+import os
+import time
+
+import test_utils
+
+import sys
+sys.path.append("../src")
+
+import dc_glade
+
+
+def test_startup_with_no_data_dir():
+       dc_glade.Dialcentral._data_path = os.path.join(os.path.dirname(__file__), "notexistent_data")
+       dc_glade.Dialcentral._user_settings = "%s/settings.ini" % dc_glade.Dialcentral._data_path
+
+       try:
+               handle = dc_glade.Dialcentral()
+               with test_utils.expected(AssertionError("Attempting login before app is fully loaded")):
+                       handle.refresh_session()
+
+               for i in xrange(10):
+                       if handle._initDone:
+                               print "Completed init on iteration %d" % i
+                               break
+                       time.sleep(1)
+               assert handle._initDone
+
+               with test_utils.expected(RuntimeError("Login Failed")):
+                       handle.refresh_session()
+
+               handle._save_settings()
+
+               del handle
+       finally:
+               os.remove(dc_glade.Dialcentral._user_settings)
+               os.removedirs(dc_glade.Dialcentral._data_path)
+
+
+def test_startup_with_empty_data_dir():
+       dc_glade.Dialcentral._data_path = os.path.join(os.path.dirname(__file__), "empty_data")
+       dc_glade.Dialcentral._user_settings = "%s/settings.ini" % dc_glade.Dialcentral._data_path
+
+       try:
+               os.makedirs(dc_glade.Dialcentral._data_path)
+
+               handle = dc_glade.Dialcentral()
+               with test_utils.expected(AssertionError("Attempting login before app is fully loaded")):
+                       handle.refresh_session()
+
+               for i in xrange(10):
+                       if handle._initDone:
+                               print "Completed init on iteration %d" % i
+                               break
+                       time.sleep(1)
+               assert handle._initDone
+
+               with test_utils.expected(RuntimeError("Login Failed")):
+                       handle.refresh_session()
+
+               handle._save_settings()
+
+               del handle
+       finally:
+               os.remove(dc_glade.Dialcentral._user_settings)
+               os.removedirs(dc_glade.Dialcentral._data_path)
+
+
+def test_startup_with_basic_data_dir():
+       dc_glade.Dialcentral._data_path = os.path.join(os.path.dirname(__file__), "basic_data")
+       dc_glade.Dialcentral._user_settings = "%s/settings.ini" % dc_glade.Dialcentral._data_path
+
+       handle = dc_glade.Dialcentral()
+       with test_utils.expected(AssertionError("Attempting login before app is fully loaded")):
+               handle.refresh_session()
+
+       for i in xrange(10):
+               if handle._initDone:
+                       print "Completed init on iteration %d" % i
+                       break
+               time.sleep(1)
+       assert handle._initDone
+
+       with test_utils.expected(RuntimeError("Login Failed")):
+               handle.refresh_session()
+
+       handle._save_settings()
+
+       del handle
diff --git a/tests/test_utils.py b/tests/test_utils.py
new file mode 100644 (file)
index 0000000..a2da797
--- /dev/null
@@ -0,0 +1,130 @@
+#!/usr/bin/env python
+
+
+from __future__ import with_statement
+
+import inspect
+import contextlib
+import functools
+
+
+def TODO(func):
+       """
+       unittest test method decorator that ignores
+       exceptions raised by test
+
+       Used to annotate test methods for code that may
+       not be written yet.  Ignores failures in the
+       annotated test method; fails if the text
+       unexpectedly succeeds.
+       !author http://kbyanc.blogspot.com/2007/06/pythons-unittest-module-aint-that-bad.html
+
+       Example:
+       >>> import unittest
+       >>> class ExampleTestCase(unittest.TestCase):
+       ...     @TODO
+       ...     def testToDo(self):
+       ...             MyModule.DoesNotExistYet('boo')
+       ...
+       """
+
+       @functools.wraps(func)
+       def wrapper(*args, **kw):
+               try:
+                       func(*args, **kw)
+                       succeeded = True
+               except:
+                       succeeded = False
+               assert succeeded is False, \
+                       "%s marked TODO but passed" % func.__name__
+       return wrapper
+
+
+def PlatformSpecific(platformList):
+       """
+       unittest test method decorator that only
+       runs test method if os.name is in the
+       given list of platforms
+       !author http://kbyanc.blogspot.com/2007/06/pythons-unittest-module-aint-that-bad.html
+       Example:
+       >>> import unittest
+       >>> class ExampleTestCase(unittest.TestCase):
+       ...     @PlatformSpecific(('mac', ))
+       ...     def testMacOnly(self):
+       ...             MyModule.SomeMacSpecificFunction()
+       ...
+       """
+
+       def decorator(func):
+               import os
+
+               @functools.wraps(func)
+               def wrapper(*args, **kw):
+                       if os.name in platformList:
+                               return func(*args, **kw)
+               return wrapper
+       return decorator
+
+
+def CheckReferences(func):
+       """
+       !author http://kbyanc.blogspot.com/2007/06/pythons-unittest-module-aint-that-bad.html
+       """
+
+       @functools.wraps(func)
+       def wrapper(*args, **kw):
+               refCounts = []
+               for i in range(5):
+                       func(*args, **kw)
+                       refCounts.append(XXXGetRefCount())
+               assert min(refCounts) != max(refCounts), "Reference counts changed - %r" % refCounts
+
+       return wrapper
+
+
+@contextlib.contextmanager
+def expected(exception):
+       """
+       >>> with expected2(ZeroDivisionError):
+       ...     1 / 0
+       >>> with expected2(AssertionError("expected ZeroDivisionError to have been thrown")):
+       ...     with expected(ZeroDivisionError):
+       ...             1 / 2
+       Traceback (most recent call last):
+               File "/usr/lib/python2.5/doctest.py", line 1228, in __run
+                       compileflags, 1) in test.globs
+               File "<doctest libraries.recipes.context.expected[1]>", line 3, in <module>
+                       1 / 2
+               File "/media/data/Personal/Development/bzr/Recollection-trunk/src/libraries/recipes/context.py", line 139, in __exit__
+                       assert t is not None, ("expected {0:%s} to have been thrown" % (self._t.__name__))
+       AssertionError: expected {0:ZeroDivisionError} to have been thrown
+       >>> with expected2(Exception("foo")):
+       ...     raise Exception("foo")
+       >>> with expected2(Exception("bar")):
+       ...     with expected(Exception("foo")): # this won't catch it
+       ...             raise Exception("bar")
+       ...     assert False, "should not see me"
+       >>> with expected2(Exception("can specify")):
+       ...     raise Exception("can specify prefixes")
+       >>> with expected2(Exception("Base class fun")):
+       True
+       >>> True
+       False
+       """
+       if isinstance(exception, Exception):
+               excType, excValue = type(exception), str(exception)
+       elif isinstance(exception, type):
+               excType, excValue = exception, ""
+
+       try:
+               yield
+       except Exception, e:
+               if not (excType in inspect.getmro(type(e)) and str(e).startswith(excValue)):
+                       raise
+       else:
+               raise AssertionError("expected {0:%s} to have been thrown" % excType.__name__)
+
+
+if __name__ == "__main__":
+       import doctest
+       doctest.testmod()