Artem Egorkine avatar Artem Egorkine committed 15ee32a

First steps towards testing.
* tests/nose.cfg,
Makefile.am,
.hgignore:
Run tests using 'make test'. Using nose, coverage, mock, unittest.
* teamcityapplet/feed.py,
teamcityapplet/applet.py:
``FeedWatcher`` functionality merged into ``Feed``.
* tests/ut_feed.py:
Unit tests for ``teamcityapplet.Feed``.

Comments (0)

Files changed (6)

 
 ^TeamcityApplet.server$
 ^defs.py$
+
+# test system related
+
+^\.nose
+^\.coverage
+^tests/coverage
+
 	teamcityapplet/ui/__init__.py \
 	teamcityapplet/ui/statusbutton.py
 
+# tests
+
+test:
+	nosetests -c tests/nose.cfg
+

teamcityapplet/applet.py

 from settings import AppletConfig
-from feed import Feed,FeedWatcher
+from feed import Feed
 from ui.statusbutton import StatusButton
 from ui.feeds import FeedsDialog
 import config
     def on_menu_about( self, component, verb ):
         pass
 
-    def add_feed( self, url ):
-
-        self.feeds.append( FeedWatcher(url) )
-
     def update_menu( self ):
 
         menu = gtk.Menu()

teamcityapplet/feed.py

         self.url = url
         self.refresh = refresh
 
+        self.__feed_data = None
+
     def data( self ):
         return (self.url, self.refresh)
 
+    def update( self ):
+
+        feed_data = feedparser.parse( self.url )
+
+        # if the feed data retrned by the feedparser is not bogus,
+        # update private data item, otherwise set and exception
+        if 'bozo' in feed_data:
+            self.exception = feed_data.get( 'bozo_exception', 'exception' )
+        else:
+            self.exception = None
+            self.__feed_data = feed_data
+
+        return feed_data
+
+    @property
+    def feed( self ):
+
+        if not self.__feed_data:
+            self.update()
+
+        return self.__feed_data
+
     def __repr__( self ):
         return 'Feed(url="%s", refresh=%d)' % (self.url, self.refresh)
 
-class FeedWatcher( object ):
-
-    def __init__( self, url ):
-
-        self.url = url
-        self.feed_data = None
-
-    def refresh( self ):
-
-        print 'fetching', self.url
-        self.feed_data = feedparser.parse( self.url )
-
-        url = self.url
-        entries = self.feed_data[ 'entries' ]
-
-        print 'url:', url, 'entries:', len(entries)
-
-    @property
-    def feed( self ):
-
-        # use the cached feed data if available
-        if self.feed_data:
-            return self.feed_data
-
-        self.refresh()
-        return self.feed_data
-
+[nosetests]
+
+verbose=1
+verbosity=2
+
+match=(?:^test|ut_|ft_)
+
+with-coverage=1
+cover-inclusive=1
+cover-package=teamcityapplet
+cover-html=1
+cover-html-dir=tests/coverage
+
+import unittest
+import mock
+
+import feedparser
+
+from teamcityapplet.feed import Feed
+
+# mock feed data
+
+feed_data = {
+        'entries' : [ 'entry' ]
+        }
+
+feed_data1 = {
+        'entries' : [ 'entry' ]
+        }
+
+error_data = {
+        'bozo': 1,
+        'bozo_exception': 'exception',
+        'entries': []
+        }
+
+class FeedUT( unittest.TestCase ):
+
+    def setUp( self ):
+        feedparser.parse = mock.Mock()
+
+    def test_init_and_data( self ):
+        # Test that the Feed class is initialized according to the
+        # arguments and that ``data()`` method generates arguments that
+        # can be used to initialize a ``Feed`` object
+
+        f = Feed( 'http://test.com', 777 )
+
+        self.assertEqual( f.url, 'http://test.com' )
+        self.assertEqual( f.refresh, 777 )
+
+        f = Feed( *f.data() )
+
+        self.assertEqual( f.url, 'http://test.com' )
+        self.assertEqual( f.refresh, 777 )
+
+    def test_update( self ):
+        # Test that ``Feed.update()`` always calls ``feedparser.parse()``
+        # and does not cache any results
+
+        f = Feed( 'http://test.com', 777 )
+
+        feedparser.parse.return_value = feed_data
+        r = f.update()
+        self.assertEqual( feed_data, r )
+
+        feedparser.parse.return_value = feed_data1
+        r = f.update()
+        self.assertEqual( feed_data1, r )
+
+        self.assertEqual( feedparser.parse.call_count, 2 )
+
+    def test_feed( self ):
+        # Test the ``Feed.feed`` property is initialized once and only once
+        # from feedparser and the data is otherwise cached
+
+        f = Feed( 'http://test.com', 777 )
+
+        feedparser.parse.return_value = feed_data
+        r = f.feed
+        self.assertEqual( feed_data, r )
+
+        feedparser.parse.return_value = feed_data1
+        r = f.feed
+        self.assertEqual( feed_data, r )
+
+        self.assertEqual( feedparser.parse.call_count, 1 )
+
+    def test_update_errors( self ):
+        # Test that update always sets the exception based on the update
+        # result and, in case of errors, does not fudge the cached data
+
+        f = Feed( 'http://test.com', 777 )
+
+        feedparser.parse.return_value = feed_data
+        r = f.update()
+        self.assertEqual( f.exception, None )
+
+        feedparser.parse.return_value = error_data
+        r = f.update()
+        self.assertEqual( f.exception, 'exception' )
+        self.assertEqual( f.feed, feed_data )
+
+
+if __name__ == '__main__':
+    unittest.main()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.