Skip to content

Commit

Permalink
Merge 46f13f5 into 14a5d6a
Browse files Browse the repository at this point in the history
  • Loading branch information
thp committed Oct 26, 2013
2 parents 14a5d6a + 46f13f5 commit 016f57c
Show file tree
Hide file tree
Showing 31 changed files with 460 additions and 117 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ python:
- "pypy"

install:
- pip install -r requirements-test.txt
- pip install nose coverage coveralls

script:
- coverage run --source=podcastparser test.py
- make test

after_script:
- coveralls
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
include README
include AUTHORS
include LICENSE
include requirements-test.txt
include makefile
include README.md
5 changes: 5 additions & 0 deletions README
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
podcastparser: Simple, fast and efficient podcast parser
========================================================

The podcast parser project is a library from the gPodder project to provide an
easy and reliable way of parsing RSS-based podcast feeds in Python.
6 changes: 0 additions & 6 deletions README.md

This file was deleted.

30 changes: 24 additions & 6 deletions makefile
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
PACKAGE := podcastparser

NOSEOPTIONS := --cover-erase --with-coverage --cover-package=$(PACKAGE) --with-doctest

PYTHON ?= python
FIND ?= find
NOSETESTS ?= $(PYTHON) -m nose

help:
@echo ""
@echo "$(MAKE) test ......... Run unit tests"
@echo "$(MAKE) clean ........ Clean build directory"
@echo "$(MAKE) distclean .... $(MAKE) clean + remove 'dist/'"
@echo ""

test:
nosetests --cover-erase --with-coverage \
--cover-package=podcastparser test.py
$(NOSETESTS) $(NOSEOPTIONS)

clean:
find -name '*.pyc' -exec rm '{}' \;
rm -f .coverage MANIFEST
rm -rf dist __pycache__
$(FIND) . -name '*.pyc' -o -name __pycache__ -exec $(RM) -r '{}' +
$(RM) -r build
$(RM) .coverage MANIFEST

distclean: clean
$(RM) -r dist

.PHONY: test clean
.PHONY: help test clean
.DEFAULT: help
3 changes: 0 additions & 3 deletions requirements-test.txt

This file was deleted.

2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
WEBSITE = metadata['website']
LICENSE = metadata['license']
DESCRIPTION = docstrings[0]
LONG_DESCRIPTION = open('README.md').read()
LONG_DESCRIPTION = open('README').read()

# Extract name and e-mail ("Firstname Lastname <mail@example.org>")
AUTHOR, EMAIL = re.match(r'(.*) <(.*)>', AUTHOR_EMAIL).groups()
Expand Down
98 changes: 0 additions & 98 deletions test.py

This file was deleted.

116 changes: 116 additions & 0 deletions test_podcastparser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#
# test_podcastparser: Test Runner for the podcastparser (2012-12-29)
# Copyright (c) 2012, 2013, Thomas Perl <m@thp.io>
# Copyright (c) 2013, Stefan Kögl <stefan@skoegl.net>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
#


import sys
import os
import glob
import json

from nose.tools import assert_equal

import podcastparser


def test_parse_feed():
def parse_feed():
URL = 'http://example.com/feed/'
FILE = 'tests/example-feed.xml'
return podcastparser.parse(URL, open(FILE))

def podcast(result):
assert_equal('Podcast Title', result['title'])
assert_equal('Some description', result['description'])
assert_equal('https://flattr.com/submit/auto?user_id=123&url='
'http%3A%2F%2Fexample.com%2F&language=de_DE&category'
'=audio&title=Podcast&description=A+Podcast&tags='
'podcast', result['payment_url'])
assert_equal('http://example.com/image.png',
result['cover_url'])
assert_equal('http://example.com', result['link'])
assert_equal(4, len(result['episodes']))

def episode1(result):
episode = result.get('episodes')[0]
assert_equal(8160, episode.get('total_time'))
assert_equal('https://flattr.com/submit/auto?user_id=123&url='
'http%3A%2F%2Fexample.com%2Fepisode-1&language=de_DE'
'&category=audio&title=An+episode&description=An+'
'episode+description&tags=podcast',
episode.get('payment_url'))
assert_equal('http://example.com/episode/1/', episode.get('link'))
assert_equal(1376662770, episode.get('published'))
assert_equal('Podcast Episode', episode.get('title'))
assert_equal('example-episode-12345', episode.get('guid'))
assert_equal('A description of the episode.',
episode.get('description'))
assert_equal(1, len(episode.get('enclosures', [])))

enclosure = episode.get('enclosures')[0]
assert_equal('http://example.com/episode/1.ogg',
enclosure.get('url'))
assert_equal('audio/ogg', enclosure.get('mime_type'))
assert_equal(50914623, enclosure.get('file_size'))

def episode2(result):
episode = result.get('episodes')[1]
assert_equal('http://example.com/podcast/episode/2/',
episode.get('guid'))

# the GUID is used as the link
assert_equal(episode.get('link'), episode.get('guid'))

# the basename of the enclosure is used as a title
assert_equal('episode-2', episode.get('title'))

def episode3(result):
episode = result.get('episodes')[2]
assert_equal('http://example.com/episode/3/', episode.get('link'))

# the link is used as the GUID
assert_equal(episode.get('link'), episode.get('guid'))

def episode4(result):
episode = result.get('episodes')[3]
# the enclosure URL is used as the GUID
assert_equal('http://example.com/episode/episode-4.ogg',
episode.get('guid'))

test_parts = [podcast, episode1, episode2, episode3, episode4]

for test_part in test_parts:
yield test_part, parse_feed()

def test_max_episodes():
""" Parse example podcast with an episode limit """
URL = 'http://example.com/feed/'
FILE = 'tests/example-feed.xml'
result = podcastparser.parse(URL, open(FILE), max_episodes=2)
assert_equal(2, len(result['episodes']))

def test_rss_parsing():
def test_parse_rss(rss_filename):
basename, _ = os.path.splitext(rss_filename)
json_filename = basename + '.json'

expected = json.load(open(json_filename))
parsed = podcastparser.parse('file://' + rss_filename, open(rss_filename))
assert_equal(expected, parsed)

for rss_filename in glob.glob(os.path.join('tests', 'data', '*.rss')):
yield test_parse_rss, rss_filename
15 changes: 15 additions & 0 deletions tests/data/duration.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"title": "duration",
"episodes": [
{
"title": "Episode",
"total_time": 3600,
"guid": "episode",
"enclosures": [],
"payment_url": null,
"link": "episode",
"published": 0,
"description": ""
}
]
}
9 changes: 9 additions & 0 deletions tests/data/duration.rss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd">
<channel>
<item>
<title>Episode</title>
<itunes:duration>01:00:00</itunes:duration>
<guid>episode</guid>
</item>
</channel>
</rss>
4 changes: 4 additions & 0 deletions tests/data/empty.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"title": "empty",
"episodes": []
}
4 changes: 4 additions & 0 deletions tests/data/empty.rss
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<rss>
<channel>
</channel>
</rss>
26 changes: 26 additions & 0 deletions tests/data/image_and_audio.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"title": "Podcast",
"episodes": [
{
"title": "Episode",
"guid": "episode::x",
"total_time": 0,
"description": "",
"payment_url": null,
"published": 0,
"link": "episode::x",
"enclosures": [
{
"file_size": -1,
"mime_type": "image/jpeg",
"url": "http://example.org/coverart.jpg"
},
{
"file_size": -1,
"mime_type": "audio/mpeg",
"url": "http://example.net/audio.mp3"
}
]
}
]
}
11 changes: 11 additions & 0 deletions tests/data/image_and_audio.rss
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<rss>
<channel>
<title>Podcast</title>
<item>
<title>Episode</title>
<guid>episode::x</guid>
<enclosure type="image/jpeg" url="http://example.org/coverart.jpg"/>
<enclosure type="audio/mpeg" url="http://example.net/audio.mp3"/>
</item>
</channel>
</rss>
15 changes: 15 additions & 0 deletions tests/data/itunes_duration.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"title": "itunes_duration",
"episodes": [
{
"guid": "a",
"title": "Namespace Test",
"enclosures": [],
"payment_url": null,
"total_time": 1200,
"link": "a",
"published": 0,
"description": ""
}
]
}
9 changes: 9 additions & 0 deletions tests/data/itunes_duration.rss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<rss xmlns:it="http://www.itunes.com/dtds/podcast-1.0.dtd">
<channel>
<item>
<title>Namespace Test</title>
<guid>a</guid>
<it:duration>00:20:00</it:duration>
</item>
</channel>
</rss>

0 comments on commit 016f57c

Please sign in to comment.