Merge branch 'master' of https://github.com/snipem/python-feedgen
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:
commit
858abbf79b
7 changed files with 323 additions and 3 deletions
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -2,3 +2,11 @@ venv
|
||||||
*.pyc
|
*.pyc
|
||||||
*.pyo
|
*.pyo
|
||||||
*.swp
|
*.swp
|
||||||
|
|
||||||
|
feedgen/tests/tmp_Atomfeed.xml
|
||||||
|
|
||||||
|
feedgen/tests/tmp_Rssfeed.xml
|
||||||
|
|
||||||
|
tmp_Atomfeed.xml
|
||||||
|
|
||||||
|
tmp_Rssfeed.xml
|
||||||
|
|
10
.travis.yml
Normal file
10
.travis.yml
Normal 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
|
|
@ -342,7 +342,7 @@ class FeedGenerator(object):
|
||||||
textInput.attrib['link'] = self.__rss_textInput.get('link')
|
textInput.attrib['link'] = self.__rss_textInput.get('link')
|
||||||
if self.__rss_ttl:
|
if self.__rss_ttl:
|
||||||
ttl = etree.SubElement(channel, 'ttl')
|
ttl = etree.SubElement(channel, 'ttl')
|
||||||
ttl.text = self.__rss_ttl
|
ttl.text = str(self.__rss_ttl)
|
||||||
if self.__rss_webMaster:
|
if self.__rss_webMaster:
|
||||||
webMaster = etree.SubElement(channel, 'webMaster')
|
webMaster = etree.SubElement(channel, 'webMaster')
|
||||||
webMaster.text = self.__rss_webMaster
|
webMaster.text = self.__rss_webMaster
|
||||||
|
@ -626,7 +626,7 @@ class FeedGenerator(object):
|
||||||
:returns: Dictionary containing the cloud data.
|
:returns: Dictionary containing the cloud data.
|
||||||
'''
|
'''
|
||||||
if not domain is None:
|
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}
|
'registerProcedure':registerProcedure, 'protocol':protocol}
|
||||||
return self.__rss_cloud
|
return self.__rss_cloud
|
||||||
|
|
||||||
|
@ -881,7 +881,7 @@ class FeedGenerator(object):
|
||||||
if not (isinstance(hours, list) or isinstance(hours, set)):
|
if not (isinstance(hours, list) or isinstance(hours, set)):
|
||||||
hours = [hours]
|
hours = [hours]
|
||||||
for h in hours:
|
for h in hours:
|
||||||
if not h in xrange(24):
|
if not h in range(24):
|
||||||
raise ValueError('Invalid hour %s' % h)
|
raise ValueError('Invalid hour %s' % h)
|
||||||
if replace or not self.__rss_skipHours:
|
if replace or not self.__rss_skipHours:
|
||||||
self.__rss_skipHours = set()
|
self.__rss_skipHours = set()
|
||||||
|
|
0
feedgen/tests/__init__.py
Normal file
0
feedgen/tests/__init__.py
Normal file
82
feedgen/tests/test_entry.py
Normal file
82
feedgen/tests/test_entry.py
Normal 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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
0
feedgen/tests/test_extension.py
Normal file
0
feedgen/tests/test_extension.py
Normal file
220
feedgen/tests/test_feed.py
Normal file
220
feedgen/tests/test_feed.py
Normal 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()
|
Loading…
Reference in a new issue