summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dependencyinjection/dicontainer.php6
-rw-r--r--tests/classloader.php2
-rw-r--r--tests/unit/utility/FeedFetcherTest.php231
-rw-r--r--utility/feedfetcher.php127
4 files changed, 310 insertions, 56 deletions
diff --git a/dependencyinjection/dicontainer.php b/dependencyinjection/dicontainer.php
index e637b51a2..9ea52b505 100644
--- a/dependencyinjection/dicontainer.php
+++ b/dependencyinjection/dicontainer.php
@@ -147,7 +147,11 @@ class DIContainer extends BaseContainer {
});
$this['FeedFetcher'] = $this->share(function($c){
- return new FeedFetcher($c['API'], $c['FaviconFetcher'],
+ return new FeedFetcher(
+ $c['API'],
+ $c['SimplePieAPIFactory'],
+ $c['FaviconFetcher'],
+ $c['TimeFactory'],
$c['simplePieCacheDirectory'],
$c['simplePieCacheDuration']);
});
diff --git a/tests/classloader.php b/tests/classloader.php
index 310dbea07..d52656a05 100644
--- a/tests/classloader.php
+++ b/tests/classloader.php
@@ -21,6 +21,8 @@
*
*/
+require_once __DIR__ . '/../../appframework/3rdparty/SimplePie/autoloader.php';
+
// to execute without owncloud, we need to create our own classloader
spl_autoload_register(function ($className){
if (strpos($className, 'OCA\\') === 0) {
diff --git a/tests/unit/utility/FeedFetcherTest.php b/tests/unit/utility/FeedFetcherTest.php
index ba4a53db1..bb027a778 100644
--- a/tests/unit/utility/FeedFetcherTest.php
+++ b/tests/unit/utility/FeedFetcherTest.php
@@ -25,15 +25,89 @@
namespace OCA\News\Utility;
+use \OCA\News\Db\Item;
+use \OCA\News\Db\Feed;
+
require_once(__DIR__ . "/../../classloader.php");
class FeedFetcherTest extends \OCA\AppFramework\Utility\TestUtility {
private $fetcher;
+ private $core;
+ private $coreFactory;
+ private $faviconFetcher;
+ private $url;
+ private $cacheDirectory;
+ private $cacheDuration;
+ private $time;
+ private $item;
+
+ // items
+ private $permalink;
+ private $title;
+ private $guid;
+ private $pub;
+ private $body;
+ private $author;
+ private $enclosureLink;
+
+ // feed
+ private $feedTitle;
+ private $feedLink;
+ private $feedImage;
+ private $webFavicon;
protected function setUp(){
- $this->fetcher = new FeedFetcher($this->getAPIMock(), 'dir', 300);
+ $this->core = $this->getMockBuilder(
+ '\SimplePie_Core')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->coreFactory = $this->getMockBuilder(
+ '\OCA\AppFramework\Utility\SimplePieAPIFactory')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->coreFactory->expects($this->any())
+ ->method('getCore')
+ ->will($this->returnValue($this->core));
+ $this->item = $this->getMockBuilder(
+ '\SimplePie_Item')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->faviconFetcher = $this->getMockBuilder(
+ '\OCA\AppFramework\Utility\FaviconFetcher')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->time = 2323;
+ $timeFactory = $this->getMockBuilder(
+ '\OCA\AppFramework\Utility\TimeFactory')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $timeFactory->expects($this->any())
+ ->method('getTime')
+ ->will($this->returnValue($this->time));
+ $this->cacheDuration = 100;
+ $this->cacheDirectory = 'dir/';
+ $this->fetcher = new FeedFetcher($this->getAPIMock(),
+ $this->coreFactory,
+ $this->faviconFetcher,
+ $timeFactory,
+ $this->cacheDirectory,
+ $this->cacheDuration);
+ $this->url = 'tests';
+
+ $this->permalink = 'http://permalink';
+ $this->title = 'my title';
+ $this->guid = 'hey guid here';
+ $this->body = 'let the bodies hit the floor';
+ $this->pub = 23111;
+ $this->author = 'boogieman';
+ $this->enclosureLink = 'http://enclosure.you';
+
+ $this->feedTitle = '&lte;its a title';
+ $this->feedLink = 'http://goatse';
+ $this->feedImage = '/an/image';
+ $this->webFavicon = 'http://anon.google.com';
}
@@ -43,5 +117,156 @@ class FeedFetcherTest extends \OCA\AppFramework\Utility\TestUtility {
$this->assertTrue($this->fetcher->canHandle($url));
}
- // TODO: write tests for the remaining methods
-} \ No newline at end of file
+
+ public function testFetchThrowsExceptionWhenInitFailed() {
+ $this->core->expects($this->once())
+ ->method('set_feed_url')
+ ->with($this->equalTo($this->url));
+ $this->core->expects($this->once())
+ ->method('enable_cache')
+ ->with($this->equalTo(true));
+ $this->core->expects($this->once())
+ ->method('set_cache_location')
+ ->with($this->equalTo($this->cacheDirectory));
+ $this->core->expects($this->once())
+ ->method('set_cache_duration')
+ ->with($this->equalTo($this->cacheDuration));
+ $this->setExpectedException('\OCA\News\Utility\FetcherException');
+ $this->fetcher->fetch($this->url);
+ }
+
+
+ public function testShouldCatchExceptionsAndThrowOwnException() {
+ $this->core->expects($this->once())
+ ->method('init')
+ ->will($this->returnValue(true));
+ $this->core->expects($this->once())
+ ->method('get_items')
+ ->will($this->throwException(new \Exception('oh noes!')));
+ $this->setExpectedException('\OCA\News\Utility\FetcherException');
+ $this->fetcher->fetch($this->url);
+ }
+
+
+ private function expectCore($method, $return) {
+ $this->core->expects($this->once())
+ ->method($method)
+ ->will($this->returnValue($return));
+ }
+
+ private function expectItem($method, $return) {
+ $this->item->expects($this->once())
+ ->method($method)
+ ->will($this->returnValue($return));
+ }
+
+
+ private function createItem($author=false, $enclosureType=null) {
+ $this->expectItem('get_permalink', $this->permalink);
+ $this->expectItem('get_title', $this->title);
+ $this->expectItem('get_id', $this->guid);
+ $this->expectItem('get_content', $this->body);
+ $this->expectItem('get_date', $this->pub);
+
+ $item = new Item();
+ $item->setStatus(0);
+ $item->setUnread();
+ $item->setUrl($this->permalink);
+ $item->setTitle($this->title);
+ $item->setGuid($this->guid);
+ $item->setGuidHash(md5($this->guid));
+ $item->setBody($this->body);
+ $item->setPubDate($this->pub);
+ $item->setLastModified($this->time);
+ if($author) {
+ $mock = $this->getMock('author', array('get_name'));
+ $mock->expects($this->once())
+ ->method('get_name')
+ ->will($this->returnValue($this->author));
+ $this->expectItem('get_author', $mock);
+ $item->setAuthor($this->author);
+ }
+
+ if($enclosureType === 'audio/ogg') {
+ $mock = $this->getMock('enclosure', array('get_type', 'get_link'));
+ $mock->expects($this->any())
+ ->method('get_type')
+ ->will($this->returnValue($enclosureType));
+ $this->expectItem('get_enclosure', $this->mock);
+ $item->setEnclosureMime($enclosureType);
+ $item->setEnclosureLink($this->enclosureLink);
+ }
+ return $item;
+ }
+
+
+ private function createFeed($hasFavicon=false, $hasWebFavicon=false) {
+ $this->expectCore('get_title', $this->feedTitle);
+ $this->expectCore('get_link', $this->feedLink);
+
+ $feed = new Feed();
+ $feed->setTitle(html_entity_decode($this->feedTitle));
+ $feed->setUrl($this->url);
+ $feed->setLink($this->feedLink);
+ $feed->setUrlHash(md5($this->url));
+ $feed->setAdded($this->time);
+
+ if($hasFavicon) {
+ $this->expectCore('get_image_url', $this->feedImage);
+ $feed->setFaviconLink($this->feedImage);
+ } else {
+ $feed->setFaviconLink(null);
+ $this->expectCore('get_image_url', null);
+ }
+
+ if($hasWebFavicon) {
+ $this->faviconFetcher->expects($this->once())
+ ->method('fetch')
+ ->with($this->equalTo($this->feedLink))
+ ->will($this->returnValue($this->webFavicon));
+ $feed->setFaviconLink($this->webFavicon);
+ }
+
+ return $feed;
+ }
+
+
+ public function testFetchMapItems(){
+ $this->core->expects($this->once())
+ ->method('init')
+ ->will($this->returnValue(true));
+ $item = $this->createItem();
+ $feed = $this->createFeed();
+ $this->expectCore('get_items', array($this->item));
+ $result = $this->fetcher->fetch($this->url);
+
+ $this->assertEquals(array($feed, array($item)), $result);
+ }
+
+
+ public function testFetchMapItemsAuthorExists(){
+ $this->core->expects($this->once())
+ ->method('init')
+ ->will($this->returnValue(true));
+ $item = $this->createItem(true);
+ $feed = $this->createFeed(true);
+ $this->expectCore('get_items', array($this->item));
+ $result = $this->fetcher->fetch($this->url);
+
+ $this->assertEquals(array($feed, array($item)), $result);
+ }
+
+
+ public function testFetchMapItemsEnclosureExists(){
+ $this->core->expects($this->once())
+ ->method('init')
+ ->will($this->returnValue(true));
+ $item = $this->createItem(false, true);
+ $feed = $this->createFeed(false, true);
+ $this->expectCore('get_items', array($this->item));
+ $result = $this->fetcher->fetch($this->url);
+
+ $this->assertEquals(array($feed, array($item)), $result);
+ }
+
+}
diff --git a/utility/feedfetcher.php b/utility/feedfetcher.php
index 1113877d0..b6b0161da 100644
--- a/utility/feedfetcher.php
+++ b/utility/feedfetcher.php
@@ -27,6 +27,8 @@ namespace OCA\News\Utility;
use \OCA\AppFramework\Core\API;
use \OCA\AppFramework\Utility\FaviconFetcher;
+use \OCA\AppFramework\Utility\SimplePieAPIFactory;
+use \OCA\AppFramework\Utility\TimeFactory;
use \OCA\News\Db\Item;
use \OCA\News\Db\Feed;
@@ -38,13 +40,21 @@ class FeedFetcher implements IFeedFetcher {
private $cacheDirectory;
private $cacheDuration;
private $faviconFetcher;
-
- public function __construct(API $api, FaviconFetcher $faviconFetcher,
- $cacheDirectory, $cacheDuration){
+ private $simplePieFactory;
+ private $time;
+
+ public function __construct(API $api,
+ SimplePieAPIFactory $simplePieFactory,
+ FaviconFetcher $faviconFetcher,
+ TimeFactory $time,
+ $cacheDirectory,
+ $cacheDuration){
$this->api = $api;
$this->cacheDirectory = $cacheDirectory;
$this->cacheDuration = $cacheDuration;
$this->faviconFetcher = $faviconFetcher;
+ $this->simplePieFactory = $simplePieFactory;
+ $this->time = $time;
}
@@ -63,9 +73,8 @@ class FeedFetcher implements IFeedFetcher {
* @return array an array containing the new feed and its items
*/
public function fetch($url) {
- // TODO: write unittests!
- $simplePie = new \SimplePie_Core();
- $simplePie->set_feed_url( $url );
+ $simplePie = $this->simplePieFactory->getCore();
+ $simplePie->set_feed_url($url);
$simplePie->enable_cache(true);
$simplePie->set_cache_location($this->cacheDirectory);
$simplePie->set_cache_duration($this->cacheDuration);
@@ -74,59 +83,17 @@ class FeedFetcher implements IFeedFetcher {
throw new FetcherException('Could not initialize simple pie');
}
+
try {
+ // somehow $simplePie turns into a feed after init
$items = array();
if ($feedItems = $simplePie->get_items()) {
foreach($feedItems as $feedItem) {
- $item = new Item();
- $item->setStatus(0);
- $item->setUnread();
- $item->setUrl( $feedItem->get_permalink() );
- // unescape content because angularjs helps agains XSS
- $item->setTitle(html_entity_decode($feedItem->get_title()));
- $item->setGuid( $feedItem->get_id() );
- $item->setGuidHash( md5($feedItem->get_id()) );
- $item->setBody( $feedItem->get_content() );
- $item->setPubDate( $feedItem->get_date('U') );
- $item->setLastModified(time());
-
- $author = $feedItem->get_author();
- if ($author !== null) {
- $item->setAuthor( $author->get_name() );
- }
-
- // TODO: make it work for video files also
- $enclosure = $feedItem->get_enclosure();
- if($enclosure !== null) {
- $enclosureType = $enclosure->get_type();
- if(stripos($enclosureType, "audio/") !== false) {
- $item->setEnclosureMime($enclosureType);
- $item->setEnclosureLink($enclosure->get_link());
- }
- }
-
- array_push($items, $item);
+ array_push($items, $this->buildItem($feedItem));
}
}
- $feed = new Feed();
- // unescape content because angularjs helps agains XSS
- $feed->setTitle(html_entity_decode($simplePie->get_title()));
- $feed->setUrl($url);
- $feed->setLink($simplePie->get_link());
- $feed->setUrlHash(md5($url));
- $feed->setAdded(time());
-
- // get the favicon from the feed
- $favicon = $simplePie->get_image_url();
- if ($favicon) {
- $feed->setFaviconLink($favicon);
-
- // or the webpage
- } else {
- $webFavicon = $this->faviconFetcher->fetch($feed->getLink());
- $feed->setFaviconLink($webFavicon);
- }
+ $feed = $this->buildFeed($simplePie, $url);
return array($feed, $items);
@@ -137,4 +104,60 @@ class FeedFetcher implements IFeedFetcher {
}
+ protected function buildItem($simplePieItem) {
+ $item = new Item();
+ $item->setStatus(0);
+ $item->setUnread();
+ $item->setUrl($simplePieItem->get_permalink());
+ // unescape content because angularjs helps agains XSS
+ $item->setTitle(html_entity_decode($simplePieItem->get_title()));
+ $guid = $simplePieItem->get_id();
+ $item->setGuid($guid);
+ $item->setGuidHash(md5($guid));
+ $item->setBody($simplePieItem->get_content());
+ $item->setPubDate($simplePieItem->get_date('U'));
+ $item->setLastModified($this->time->getTime());
+
+ $author = $simplePieItem->get_author();
+ if ($author !== null) {
+ $item->setAuthor($author->get_name());
+ }
+
+ // TODO: make it work for video files also
+ $enclosure = $simplePieItem->get_enclosure();
+ if($enclosure !== null) {
+ $enclosureType = $enclosure->get_type();
+ if(stripos($enclosureType, "audio/") !== false) {
+ $item->setEnclosureMime($enclosureType);
+ $item->setEnclosureLink($enclosure->get_link());
+ }
+ }
+
+ return $item;
+ }
+
+
+ protected function buildFeed($simplePieFeed, $url) {
+ $feed = new Feed();
+
+ // unescape content because angularjs helps agains XSS
+ $feed->setTitle(html_entity_decode($simplePieFeed->get_title()));
+ $feed->setUrl($url);
+ $feed->setLink($simplePieFeed->get_link());
+ $feed->setUrlHash(md5($url));
+ $feed->setAdded($this->time->getTime());
+
+ // get the favicon from the feed or the webpage
+ $favicon = $simplePieFeed->get_image_url();
+
+ if ($favicon) {
+ $feed->setFaviconLink($favicon);
+ } else {
+ $webFavicon = $this->faviconFetcher->fetch($feed->getLink());
+ $feed->setFaviconLink($webFavicon);
+ }
+
+ return $feed;
+ }
+
} \ No newline at end of file