This merges in a pull request by snipem brining in some unit tests and
bug fixes:

---

Unit tests

I've created unit tests for the module, especially for feeds and
entries. Whilst entry tests aren't fully implemented yet, unit test
cases for feed creation are complete and working. They cover the
creation of a full blown feed in Atom and RSS.

Bugs fixed

Thanks to the unit test cases, I was able to discover three bugs
concerning the fields domain, ttl and skipHours. According to the
lastest test results, they are resolved and working with both Python 2
and Python 3.

Travis support

Additionally, I've added Travis support. Which is a Continuous
Integration platform powering Github.

---

Signed-off-by: Lars Kiesow <lkiesow@uos.de>
This commit is contained in:
Lars Kiesow 2014-07-20 22:52:16 +02:00
commit 858abbf79b
No known key found for this signature in database
GPG key ID: 5DAFE8D9C823CE73
7 changed files with 323 additions and 3 deletions

8
.gitignore vendored
View file

@ -2,3 +2,11 @@ venv
*.pyc
*.pyo
*.swp
feedgen/tests/tmp_Atomfeed.xml
feedgen/tests/tmp_Rssfeed.xml
tmp_Atomfeed.xml
tmp_Rssfeed.xml

10
.travis.yml Normal file
View file

@ -0,0 +1,10 @@
language: python
python:
- "2.6"
- "2.7"
- "3.2"
- "3.3"
# command to install dependencies
install: "pip install lxml python-dateutil"
# command to run tests
script: py.test

View file

@ -342,7 +342,7 @@ class FeedGenerator(object):
textInput.attrib['link'] = self.__rss_textInput.get('link')
if self.__rss_ttl:
ttl = etree.SubElement(channel, 'ttl')
ttl.text = self.__rss_ttl
ttl.text = str(self.__rss_ttl)
if self.__rss_webMaster:
webMaster = etree.SubElement(channel, 'webMaster')
webMaster.text = self.__rss_webMaster
@ -626,7 +626,7 @@ class FeedGenerator(object):
:returns: Dictionary containing the cloud data.
'''
if not domain is None:
self.__rss_cloud = {'donain':domain, 'port':port, 'path':path,
self.__rss_cloud = {'domain':domain, 'port':port, 'path':path,
'registerProcedure':registerProcedure, 'protocol':protocol}
return self.__rss_cloud
@ -881,7 +881,7 @@ class FeedGenerator(object):
if not (isinstance(hours, list) or isinstance(hours, set)):
hours = [hours]
for h in hours:
if not h in xrange(24):
if not h in range(24):
raise ValueError('Invalid hour %s' % h)
if replace or not self.__rss_skipHours:
self.__rss_skipHours = set()

View file

View file

@ -0,0 +1,82 @@
# -*- coding: utf-8 -*-
"""
Tests for a basic entry
These test cases contain test cases for a basic entry.
"""
import unittest
from lxml import etree
from ..feed import FeedGenerator
class TestSequenceFunctions(unittest.TestCase):
def setUp(self):
fg = FeedGenerator()
self.feedId = 'http://example.com'
self.title = 'Some Testfeed'
fg.id(self.feedId)
fg.title(self.title)
fe = fg.add_entry()
fe.id('http://lernfunk.de/media/654321/1')
fe.title('The First Episode')
#Use also the different name add_item
fe = fg.add_item()
fe.id('http://lernfunk.de/media/654321/1')
fe.title('The Second Episode')
fe = fg.add_entry()
fe.id('http://lernfunk.de/media/654321/1')
fe.title('The Third Episode')
self.fg = fg
def test_checkEntryNumbers(self):
fg = self.fg
assert len(fg.entry()) == 3
def test_checkItemNumbers(self):
fg = self.fg
assert len(fg.item()) == 3
def test_checkEntryContent(self):
fg = self.fg
assert len(fg.entry()) != None
def test_removeEntryByIndex(self):
fg = FeedGenerator()
self.feedId = 'http://example.com'
self.title = 'Some Testfeed'
fe = fg.add_entry()
fe.id('http://lernfunk.de/media/654321/1')
fe.title('The Third Episode')
assert len(fg.entry()) == 1
fg.remove_entry(0)
assert len(fg.entry()) == 0
def test_removeEntryByEntry(self):
fg = FeedGenerator()
self.feedId = 'http://example.com'
self.title = 'Some Testfeed'
fe = fg.add_entry()
fe.id('http://lernfunk.de/media/654321/1')
fe.title('The Third Episode')
assert len(fg.entry()) == 1
fg.remove_entry(fe)
assert len(fg.entry()) == 0

View file

220
feedgen/tests/test_feed.py Normal file
View file

@ -0,0 +1,220 @@
# -*- coding: utf-8 -*-
"""
Tests for a basic feed
These test cases contain test cases for a basic feed. A basic feed does not contain entries so far.
"""
import unittest
from lxml import etree
from ..feed import FeedGenerator
class TestSequenceFunctions(unittest.TestCase):
def setUp(self):
fg = FeedGenerator()
self.nsAtom = "http://www.w3.org/2005/Atom"
self.nsRss = "http://purl.org/rss/1.0/modules/content/"
self.feedId = 'http://lernfunk.de/media/654321'
self.title = 'Some Testfeed'
self.authorName = 'John Doe'
self.authorMail = 'john@example.de'
self.author = {'name': self.authorName,'email': self.authorMail}
self.linkHref = 'http://example.com'
self.linkRel = 'alternate'
self.logo = 'http://ex.com/logo.jpg'
self.subtitle = 'This is a cool feed!'
self.link2Href = 'http://larskiesow.de/test.atom'
self.link2Rel = 'self'
self.language = 'en'
self.categoryTerm = 'This category term'
self.categoryScheme = 'This category scheme'
self.categoryLabel = 'This category label'
self.cloudDomain = 'example.com'
self.cloudPort = '4711'
self.cloudPath = '/ws/example'
self.cloudRegisterProcedure = 'registerProcedure'
self.cloudProtocol = 'SOAP 1.1'
self.icon = "http://example.com/icon.png"
self.contributor = {'name':"Contributor Name", 'uri':"Contributor Uri", 'email': 'Contributor email'}
self.copyright = "The copyright notice"
self.docs = 'http://www.rssboard.org/rss-specification'
self.managingEditor = 'mail@example.com'
self.rating = '(PICS-1.1 "http://www.classify.org/safesurf/" 1 r (SS~~000 1))'
self.skipDays = 'Tuesday'
self.skipHours = 23
self.textInputTitle = "Text input title"
self.textInputDescription = "Text input description"
self.textInputName = "Text input name"
self.textInputLink = "Text input link"
self.ttl = 900
self.webMaster = 'webmaster@example.com'
fg.id(self.feedId)
fg.title(self.title)
fg.author(self.author)
fg.link( href=self.linkHref, rel=self.linkRel )
fg.logo(self.logo)
fg.subtitle(self.subtitle)
fg.link( href=self.link2Href, rel=self.link2Rel )
fg.language(self.language)
fg.cloud(domain=self.cloudDomain, port=self.cloudPort, path=self.cloudPath, registerProcedure=self.cloudRegisterProcedure, protocol=self.cloudProtocol)
fg.icon(self.icon)
fg.category(term=self.categoryTerm, scheme=self.categoryScheme, label=self.categoryLabel)
fg.contributor(self.contributor)
fg.copyright(self.copyright)
fg.docs(docs=self.docs)
fg.managingEditor(self.managingEditor)
fg.rating(self.rating)
fg.skipDays(self.skipDays)
fg.skipHours(self.skipHours)
fg.textInput(title=self.textInputTitle, description=self.textInputDescription, name=self.textInputName, link=self.textInputLink)
fg.ttl(self.ttl)
fg.webMaster(self.webMaster)
self.fg = fg
def test_baseFeed(self):
fg = self.fg
assert fg.id() == self.feedId
assert fg.title() == self.title
assert fg.author()[0]['name'] == self.authorName
assert fg.author()[0]['email'] == self.authorMail
assert fg.link()[0]['href'] == self.linkHref
assert fg.link()[0]['rel'] == self.linkRel
assert fg.logo() == self.logo
assert fg.subtitle() == self.subtitle
assert fg.link()[1]['href'] == self.link2Href
assert fg.link()[1]['rel'] == self.link2Rel
assert fg.language() == self.language
def test_atomFeedFile(self):
fg = self.fg
filename = 'tmp_Atomfeed.xml'
fg.atom_file(filename=filename, pretty=True)
with open (filename, "r") as myfile:
atomString=myfile.read().replace('\n', '')
self.checkAtomString(atomString)
def test_atomFeedString(self):
fg = self.fg
atomString = fg.atom_str(pretty=True)
self.checkAtomString(atomString)
def checkAtomString(self, atomString):
feed = etree.fromstring(atomString)
print (atomString)
nsAtom = self.nsAtom
assert feed.find("{%s}title" % nsAtom).text == self.title
assert feed.find("{%s}updated" % nsAtom).text != None
assert feed.find("{%s}id" % nsAtom).text == self.feedId
assert feed.find("{%s}category" % nsAtom).get('term') == self.categoryTerm
assert feed.find("{%s}category" % nsAtom).get('label') == self.categoryLabel
assert feed.find("{%s}author" % nsAtom).find("{%s}name" % nsAtom).text == self.authorName
assert feed.find("{%s}author" % nsAtom).find("{%s}email" % nsAtom).text == self.authorMail
assert feed.findall("{%s}link" % nsAtom)[0].get('href') == self.linkHref
assert feed.findall("{%s}link" % nsAtom)[0].get('rel') == self.linkRel
assert feed.findall("{%s}link" % nsAtom)[1].get('href') == self.link2Href
assert feed.findall("{%s}link" % nsAtom)[1].get('rel') == self.link2Rel
assert feed.find("{%s}logo" % nsAtom).text == self.logo
assert feed.find("{%s}icon" % nsAtom).text == self.icon
assert feed.find("{%s}subtitle" % nsAtom).text == self.subtitle
assert feed.find("{%s}contributor" % nsAtom).find("{%s}name" % nsAtom).text == self.contributor['name']
assert feed.find("{%s}contributor" % nsAtom).find("{%s}email" % nsAtom).text == self.contributor['email']
assert feed.find("{%s}contributor" % nsAtom).find("{%s}url" % nsAtom).text == self.contributor['uri']
assert feed.find("{%s}rights" % nsAtom).text == self.copyright
def test_rssFeedFile(self):
fg = self.fg
filename = 'tmp_Rssfeed.xml'
fg.rss_file(filename=filename, pretty=True)
with open (filename, "r") as myfile:
rssString=myfile.read().replace('\n', '')
self.checkRssString(rssString)
def test_rssFeedString(self):
fg = self.fg
rssString = fg.rss_str(pretty=True)
self.checkRssString(rssString)
def test_loadPodcastExtension(self):
fg = self.fg
fg.load_extension('podcast', atom=True, rss=True)
def test_loadDcExtension(self):
fg = self.fg
fg.load_extension('dc', atom=True, rss=True)
def checkRssString(self, rssString):
feed = etree.fromstring(rssString)
nsAtom = self.nsAtom
nsRss = self.nsRss
print (rssString)
channel = feed.find("channel")
assert channel != None
assert channel.find("title").text == self.title
assert channel.find("description").text == self.subtitle
assert channel.find("lastBuildDate").text != None
assert channel.find("docs").text == "http://www.rssboard.org/rss-specification"
assert channel.find("generator").text == "python-feedgen"
assert channel.findall("{%s}link" % nsAtom)[0].get('href') == self.link2Href
assert channel.findall("{%s}link" % nsAtom)[0].get('rel') == self.link2Rel
assert channel.find("image").find("url").text == self.logo
assert channel.find("image").find("title").text == self.title
assert channel.find("image").find("link").text == self.link2Href
assert channel.find("category").text == self.categoryLabel
assert channel.find("cloud").get('domain') == self.cloudDomain
assert channel.find("cloud").get('port') == self.cloudPort
assert channel.find("cloud").get('path') == self.cloudPath
assert channel.find("cloud").get('registerProcedure') == self.cloudRegisterProcedure
assert channel.find("cloud").get('protocol') == self.cloudProtocol
assert channel.find("copyright").text == self.copyright
assert channel.find("docs").text == self.docs
assert channel.find("managingEditor").text == self.managingEditor
assert channel.find("rating").text == self.rating
assert channel.find("skipDays").find("day").text == self.skipDays
assert int(channel.find("skipHours").find("hour").text) == self.skipHours
assert channel.find("textInput").get('title') == self.textInputTitle
assert channel.find("textInput").get('description') == self.textInputDescription
assert channel.find("textInput").get('name') == self.textInputName
assert channel.find("textInput").get('link') == self.textInputLink
assert int(channel.find("ttl").text) == self.ttl
assert channel.find("webMaster").text == self.webMaster
if __name__ == '__main__':
unittest.main()