diff options
Diffstat (limited to 'core')
7 files changed, 648 insertions, 3 deletions
diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java index 08ea27434..95b828e28 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java +++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java @@ -100,7 +100,7 @@ public class PlaybackPreferences implements SharedPreferences.OnSharedPreference } public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - if (key.equals(PREF_CURRENT_PLAYER_STATUS)) { + if (PREF_CURRENT_PLAYER_STATUS.equals(key)) { EventBus.getDefault().post(new PlayerStatusEvent()); } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/FileNameGenerator.java b/core/src/main/java/de/danoeh/antennapod/core/util/FileNameGenerator.java index 2a387b7b0..69c23efc2 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/FileNameGenerator.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/FileNameGenerator.java @@ -13,7 +13,7 @@ import java.security.NoSuchAlgorithmException; /** Generates valid filenames for a given string. */ public class FileNameGenerator { @VisibleForTesting - public static final int MAX_FILENAME_LENGTH = 255; // Limited by ext4 + public static final int MAX_FILENAME_LENGTH = 242; // limited by CircleCI private static final int MD5_HEX_LENGTH = 32; private static final char[] validChars = diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/AudioPlayer.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/AudioPlayer.java index 0467c0a78..031bf9a24 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/AudioPlayer.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/AudioPlayer.java @@ -20,7 +20,7 @@ public class AudioPlayer extends MediaPlayer implements IPlayer { super(context, true, ClientConfig.USER_AGENT); PreferenceManager.getDefaultSharedPreferences(context) .registerOnSharedPreferenceChangeListener((sharedPreferences, key) -> { - if (key.equals(UserPreferences.PREF_MEDIA_PLAYER)) { + if (UserPreferences.PREF_MEDIA_PLAYER.equals(key)) { checkMpi(); } }); diff --git a/core/src/test/java/de/danoeh/antennapod/core/util/FilenameGeneratorTest.java b/core/src/test/java/de/danoeh/antennapod/core/util/FilenameGeneratorTest.java new file mode 100644 index 000000000..af22a4b9d --- /dev/null +++ b/core/src/test/java/de/danoeh/antennapod/core/util/FilenameGeneratorTest.java @@ -0,0 +1,98 @@ +package de.danoeh.antennapod.core.util; + +import androidx.test.platform.app.InstrumentationRegistry; +import android.text.TextUtils; + +import java.io.File; + +import org.apache.commons.lang3.StringUtils; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(RobolectricTestRunner.class) +public class FilenameGeneratorTest { + + public FilenameGeneratorTest() { + super(); + } + + @Test + public void testGenerateFileName() throws Exception { + String result = FileNameGenerator.generateFileName("abc abc"); + assertEquals(result, "abc abc"); + createFiles(result); + } + + @Test + public void testGenerateFileName1() throws Exception { + String result = FileNameGenerator.generateFileName("ab/c: <abc"); + assertEquals(result, "abc abc"); + createFiles(result); + } + + @Test + public void testGenerateFileName2() throws Exception { + String result = FileNameGenerator.generateFileName("abc abc "); + assertEquals(result, "abc abc"); + createFiles(result); + } + + @Test + public void testFeedTitleContainsApostrophe() { + String result = FileNameGenerator.generateFileName("Feed's Title ..."); + assertEquals("Feeds Title", result); + } + + @Test + public void testFeedTitleContainsDash() { + String result = FileNameGenerator.generateFileName("Left - Right"); + assertEquals("Left - Right", result); + } + + @Test + public void testFeedTitleContainsAccents() { + String result = FileNameGenerator.generateFileName("Äàáâãå"); + assertEquals("Aaaaaa", result); + } + + @Test + public void testInvalidInput() { + String result = FileNameGenerator.generateFileName("???"); + assertFalse(TextUtils.isEmpty(result)); + } + + @Test + public void testLongFilename() throws Exception { + String longName = StringUtils.repeat("x", 20 + FileNameGenerator.MAX_FILENAME_LENGTH); + String result = FileNameGenerator.generateFileName(longName); + assertTrue(result.length() <= FileNameGenerator.MAX_FILENAME_LENGTH); + createFiles(result); + } + + @Test + public void testLongFilenameNotEquals() { + // Verify that the name is not just trimmed and different suffixes end up with the same name + String longName = StringUtils.repeat("x", 20 + FileNameGenerator.MAX_FILENAME_LENGTH); + String result1 = FileNameGenerator.generateFileName(longName + "a"); + String result2 = FileNameGenerator.generateFileName(longName + "b"); + assertNotEquals(result1, result2); + } + + /** + * Tests if files can be created. + */ + private void createFiles(String name) throws Exception { + File cache = InstrumentationRegistry.getInstrumentation().getTargetContext().getExternalCacheDir(); + File testFile = new File(cache, name); + assertTrue(testFile.mkdir()); + assertTrue(testFile.exists()); + assertTrue(testFile.delete()); + assertTrue(testFile.createNewFile()); + } +} diff --git a/core/src/test/java/de/danoeh/antennapod/core/util/URLCheckerTest.java b/core/src/test/java/de/danoeh/antennapod/core/util/URLCheckerTest.java new file mode 100644 index 000000000..a4b3dee06 --- /dev/null +++ b/core/src/test/java/de/danoeh/antennapod/core/util/URLCheckerTest.java @@ -0,0 +1,157 @@ +package de.danoeh.antennapod.core.util; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Test class for {@link URLChecker} + */ +@RunWith(RobolectricTestRunner.class) +public class URLCheckerTest { + + @Test + public void testCorrectURLHttp() { + final String in = "http://example.com"; + final String out = URLChecker.prepareURL(in); + assertEquals(in, out); + } + + @Test + public void testCorrectURLHttps() { + final String in = "https://example.com"; + final String out = URLChecker.prepareURL(in); + assertEquals(in, out); + } + + @Test + public void testMissingProtocol() { + final String in = "example.com"; + final String out = URLChecker.prepareURL(in); + assertEquals("http://example.com", out); + } + + @Test + public void testFeedProtocol() { + final String in = "feed://example.com"; + final String out = URLChecker.prepareURL(in); + assertEquals("http://example.com", out); + } + + @Test + public void testPcastProtocolNoScheme() { + final String in = "pcast://example.com"; + final String out = URLChecker.prepareURL(in); + assertEquals("http://example.com", out); + } + + @Test + public void testItpcProtocol() { + final String in = "itpc://example.com"; + final String out = URLChecker.prepareURL(in); + assertEquals("http://example.com", out); + } + + @Test + public void testItpcProtocolWithScheme() { + final String in = "itpc://https://example.com"; + final String out = URLChecker.prepareURL(in); + assertEquals("https://example.com", out); + } + + @Test + public void testWhiteSpaceUrlShouldNotAppend() { + final String in = "\n http://example.com \t"; + final String out = URLChecker.prepareURL(in); + assertEquals("http://example.com", out); + } + + @Test + public void testWhiteSpaceShouldAppend() { + final String in = "\n example.com \t"; + final String out = URLChecker.prepareURL(in); + assertEquals("http://example.com", out); + } + + @Test + public void testAntennaPodSubscribeProtocolNoScheme() { + final String in = "antennapod-subscribe://example.com"; + final String out = URLChecker.prepareURL(in); + assertEquals("http://example.com", out); + } + + @Test + public void testPcastProtocolWithScheme() { + final String in = "pcast://https://example.com"; + final String out = URLChecker.prepareURL(in); + assertEquals("https://example.com", out); + } + + @Test + public void testAntennaPodSubscribeProtocolWithScheme() { + final String in = "antennapod-subscribe://https://example.com"; + final String out = URLChecker.prepareURL(in); + assertEquals("https://example.com", out); + } + + @Test + public void testProtocolRelativeUrlIsAbsolute() { + final String in = "https://example.com"; + final String inBase = "http://examplebase.com"; + final String out = URLChecker.prepareURL(in, inBase); + assertEquals(in, out); + } + + @Test + public void testProtocolRelativeUrlIsRelativeHttps() { + final String in = "//example.com"; + final String inBase = "https://examplebase.com"; + final String out = URLChecker.prepareURL(in, inBase); + assertEquals("https://example.com", out); + } + + @Test + public void testProtocolRelativeUrlIsHttpsWithApSubscribeProtocol() { + final String in = "//example.com"; + final String inBase = "antennapod-subscribe://https://examplebase.com"; + final String out = URLChecker.prepareURL(in, inBase); + assertEquals("https://example.com", out); + } + + @Test + public void testProtocolRelativeUrlBaseUrlNull() { + final String in = "example.com"; + final String out = URLChecker.prepareURL(in, null); + assertEquals("http://example.com", out); + } + + @Test + public void testUrlEqualsSame() { + assertTrue(URLChecker.urlEquals("https://www.example.com/test", "https://www.example.com/test")); + assertTrue(URLChecker.urlEquals("https://www.example.com/test", "https://www.example.com/test/")); + assertTrue(URLChecker.urlEquals("https://www.example.com/test", "https://www.example.com//test")); + assertTrue(URLChecker.urlEquals("https://www.example.com", "https://www.example.com/")); + assertTrue(URLChecker.urlEquals("https://www.example.com", "http://www.example.com")); + assertTrue(URLChecker.urlEquals("http://www.example.com/", "https://www.example.com/")); + assertTrue(URLChecker.urlEquals("https://www.example.com/?id=42", "https://www.example.com/?id=42")); + assertTrue(URLChecker.urlEquals("https://example.com/podcast%20test", "https://example.com/podcast test")); + assertTrue(URLChecker.urlEquals("https://example.com/?a=podcast%20test", "https://example.com/?a=podcast test")); + assertTrue(URLChecker.urlEquals("https://example.com/?", "https://example.com/")); + assertTrue(URLChecker.urlEquals("https://example.com/?", "https://example.com")); + assertTrue(URLChecker.urlEquals("https://Example.com", "https://example.com")); + assertTrue(URLChecker.urlEquals("https://example.com/test", "https://example.com/Test")); + } + + @Test + public void testUrlEqualsDifferent() { + assertFalse(URLChecker.urlEquals("https://www.example.com/test", "https://www.example2.com/test")); + assertFalse(URLChecker.urlEquals("https://www.example.com/test", "https://www.example.de/test")); + assertFalse(URLChecker.urlEquals("https://example.com/", "https://otherpodcast.example.com/")); + assertFalse(URLChecker.urlEquals("https://www.example.com/?id=42&a=b", "https://www.example.com/?id=43&a=b")); + assertFalse(URLChecker.urlEquals("https://example.com/podcast%25test", "https://example.com/podcast test")); + } +} diff --git a/core/src/test/java/de/danoeh/antennapod/core/util/playback/TimelineTest.java b/core/src/test/java/de/danoeh/antennapod/core/util/playback/TimelineTest.java new file mode 100644 index 000000000..8927a41de --- /dev/null +++ b/core/src/test/java/de/danoeh/antennapod/core/util/playback/TimelineTest.java @@ -0,0 +1,262 @@ +package de.danoeh.antennapod.core.util.playback; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.util.Date; +import java.util.List; + +import de.danoeh.antennapod.core.feed.Chapter; +import de.danoeh.antennapod.core.feed.FeedItem; +import de.danoeh.antennapod.core.feed.FeedMedia; +import de.danoeh.antennapod.core.storage.DBReader; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.robolectric.RobolectricTestRunner; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * Test class for {@link Timeline}. + */ +@RunWith(RobolectricTestRunner.class) +public class TimelineTest { + + private Context context; + MockedStatic<DBReader> dbReaderMock; + + @Before + public void setUp() { + context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + // mock DBReader, because Timeline.processShownotes() calls FeedItem.loadShownotes() + // which calls DBReader.loadDescriptionOfFeedItem(), but we don't need the database here + dbReaderMock = Mockito.mockStatic(DBReader.class); + } + + @After + public void tearDown() { + dbReaderMock.close(); + } + + @SuppressWarnings("SameParameterValue") + private Playable newTestPlayable(List<Chapter> chapters, String shownotes, int duration) { + FeedItem item = new FeedItem(0, "Item", "item-id", "http://example.com/item", new Date(), FeedItem.PLAYED, null); + item.setChapters(chapters); + item.setContentEncoded(shownotes); + FeedMedia media = new FeedMedia(item, "http://example.com/episode", 100, "audio/mp3"); + media.setDuration(duration); + item.setMedia(media); + return media; + } + + @Test + public void testProcessShownotesAddTimecodeHhmmssNoChapters() { + final String timeStr = "10:11:12"; + final long time = 3600 * 1000 * 10 + 60 * 1000 * 11 + 12 * 1000; + + Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + + timeStr + " here.</p>", Integer.MAX_VALUE); + Timeline t = new Timeline(context, p); + String res = t.processShownotes(); + checkLinkCorrect(res, new long[]{time}, new String[]{timeStr}); + } + + @Test + public void testProcessShownotesAddTimecodeHhmmssMoreThen24HoursNoChapters() { + final String timeStr = "25:00:00"; + final long time = 25 * 60 * 60 * 1000; + + Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + + timeStr + " here.</p>", Integer.MAX_VALUE); + Timeline t = new Timeline(context, p); + String res = t.processShownotes(); + checkLinkCorrect(res, new long[]{time}, new String[]{timeStr}); + } + + @Test + public void testProcessShownotesAddTimecodeHhmmNoChapters() { + final String timeStr = "10:11"; + final long time = 3600 * 1000 * 10 + 60 * 1000 * 11; + + Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + + timeStr + " here.</p>", Integer.MAX_VALUE); + Timeline t = new Timeline(context, p); + String res = t.processShownotes(); + checkLinkCorrect(res, new long[]{time}, new String[]{timeStr}); + } + + @Test + public void testProcessShownotesAddTimecodeMmssNoChapters() { + final String timeStr = "10:11"; + final long time = 10 * 60 * 1000 + 11 * 1000; + + Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + + timeStr + " here.</p>", 11 * 60 * 1000); + Timeline t = new Timeline(context, p); + String res = t.processShownotes(); + checkLinkCorrect(res, new long[]{time}, new String[]{timeStr}); + } + + @Test + public void testProcessShownotesAddTimecodeHmmssNoChapters() { + final String timeStr = "2:11:12"; + final long time = 2 * 60 * 60 * 1000 + 11 * 60 * 1000 + 12 * 1000; + Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + + timeStr + " here.</p>", Integer.MAX_VALUE); + Timeline t = new Timeline(context, p); + String res = t.processShownotes(); + checkLinkCorrect(res, new long[]{time}, new String[]{timeStr}); + } + + @Test + public void testProcessShownotesAddTimecodeMssNoChapters() { + final String timeStr = "1:12"; + final long time = 60 * 1000 + 12 * 1000; + + Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + + timeStr + " here.</p>", 2 * 60 * 1000); + Timeline t = new Timeline(context, p); + String res = t.processShownotes(); + checkLinkCorrect(res, new long[]{time}, new String[]{timeStr}); + } + + @Test + public void testProcessShownotesAddNoTimecodeDuration() { + final String timeStr = "2:11:12"; + final int time = 2 * 60 * 60 * 1000 + 11 * 60 * 1000 + 12 * 1000; + String originalText = "<p> Some test text with a timecode " + timeStr + " here.</p>"; + Playable p = newTestPlayable(null, originalText, time); + Timeline t = new Timeline(context, p); + String res = t.processShownotes(); + Document d = Jsoup.parse(res); + assertEquals("Should not parse time codes that equal duration", 0, d.body().getElementsByTag("a").size()); + } + + @Test + public void testProcessShownotesAddTimecodeMultipleFormatsNoChapters() { + final String[] timeStrings = new String[]{ "10:12", "1:10:12" }; + + Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + + timeStrings[0] + " here. Hey look another one " + timeStrings[1] + " here!</p>", 2 * 60 * 60 * 1000); + Timeline t = new Timeline(context, p); + String res = t.processShownotes(); + checkLinkCorrect(res, new long[]{10 * 60 * 1000 + 12 * 1000, + 60 * 60 * 1000 + 10 * 60 * 1000 + 12 * 1000}, timeStrings); + } + + @Test + public void testProcessShownotesAddTimecodeMultipleShortFormatNoChapters() { + + // One of these timecodes fits as HH:MM and one does not so both should be parsed as MM:SS. + final String[] timeStrings = new String[]{ "10:12", "2:12" }; + + Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + + timeStrings[0] + " here. Hey look another one " + timeStrings[1] + " here!</p>", 3 * 60 * 60 * 1000); + Timeline t = new Timeline(context, p); + String res = t.processShownotes(); + checkLinkCorrect(res, new long[]{10 * 60 * 1000 + 12 * 1000, 2 * 60 * 1000 + 12 * 1000}, timeStrings); + } + + @Test + public void testProcessShownotesAddTimecodeParentheses() { + final String timeStr = "10:11"; + final long time = 3600 * 1000 * 10 + 60 * 1000 * 11; + + Playable p = newTestPlayable(null, "<p> Some test text with a timecode (" + + timeStr + ") here.</p>", Integer.MAX_VALUE); + Timeline t = new Timeline(context, p); + String res = t.processShownotes(); + checkLinkCorrect(res, new long[]{time}, new String[]{timeStr}); + } + + @Test + public void testProcessShownotesAddTimecodeBrackets() { + final String timeStr = "10:11"; + final long time = 3600 * 1000 * 10 + 60 * 1000 * 11; + + Playable p = newTestPlayable(null, "<p> Some test text with a timecode [" + + timeStr + "] here.</p>", Integer.MAX_VALUE); + Timeline t = new Timeline(context, p); + String res = t.processShownotes(); + checkLinkCorrect(res, new long[]{time}, new String[]{timeStr}); + } + + @Test + public void testProcessShownotesAddTimecodeAngleBrackets() { + final String timeStr = "10:11"; + final long time = 3600 * 1000 * 10 + 60 * 1000 * 11; + + Playable p = newTestPlayable(null, "<p> Some test text with a timecode <" + + timeStr + "> here.</p>", Integer.MAX_VALUE); + Timeline t = new Timeline(context, p); + String res = t.processShownotes(); + checkLinkCorrect(res, new long[]{time}, new String[]{timeStr}); + } + + @Test + public void testProcessShownotesAndInvalidTimecode() { + final String[] timeStrs = new String[] {"2:1", "0:0", "000", "00", "00:000"}; + + StringBuilder shownotes = new StringBuilder("<p> Some test text with timecodes "); + for (String timeStr : timeStrs) { + shownotes.append(timeStr).append(" "); + } + shownotes.append("here.</p>"); + + Playable p = newTestPlayable(null, shownotes.toString(), Integer.MAX_VALUE); + Timeline t = new Timeline(context, p); + String res = t.processShownotes(); + checkLinkCorrect(res, new long[0], new String[0]); + } + + private void checkLinkCorrect(String res, long[] timecodes, String[] timecodeStr) { + assertNotNull(res); + Document d = Jsoup.parse(res); + Elements links = d.body().getElementsByTag("a"); + int countedLinks = 0; + for (Element link : links) { + String href = link.attributes().get("href"); + String text = link.text(); + if (href.startsWith("antennapod://")) { + assertTrue(href.endsWith(String.valueOf(timecodes[countedLinks]))); + assertEquals(timecodeStr[countedLinks], text); + countedLinks++; + assertTrue("Contains too many links: " + countedLinks + " > " + + timecodes.length, countedLinks <= timecodes.length); + } + } + assertEquals(timecodes.length, countedLinks); + } + + @Test + public void testIsTimecodeLink() { + assertFalse(Timeline.isTimecodeLink(null)); + assertFalse(Timeline.isTimecodeLink("http://antennapod/timecode/123123")); + assertFalse(Timeline.isTimecodeLink("antennapod://timecode/")); + assertFalse(Timeline.isTimecodeLink("antennapod://123123")); + assertFalse(Timeline.isTimecodeLink("antennapod://timecode/123123a")); + assertTrue(Timeline.isTimecodeLink("antennapod://timecode/123")); + assertTrue(Timeline.isTimecodeLink("antennapod://timecode/1")); + } + + @Test + public void testGetTimecodeLinkTime() { + assertEquals(-1, Timeline.getTimecodeLinkTime(null)); + assertEquals(-1, Timeline.getTimecodeLinkTime("http://timecode/123")); + assertEquals(123, Timeline.getTimecodeLinkTime("antennapod://timecode/123")); + + } +} diff --git a/core/src/test/java/de/danoeh/antennapod/core/util/syndication/FeedDiscovererTest.java b/core/src/test/java/de/danoeh/antennapod/core/util/syndication/FeedDiscovererTest.java new file mode 100644 index 000000000..3df5230cc --- /dev/null +++ b/core/src/test/java/de/danoeh/antennapod/core/util/syndication/FeedDiscovererTest.java @@ -0,0 +1,128 @@ +package de.danoeh.antennapod.core.util.syndication; + +import androidx.test.platform.app.InstrumentationRegistry; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +import java.io.File; +import java.io.FileOutputStream; +import java.nio.charset.StandardCharsets; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * Test class for {@link FeedDiscoverer} + */ +@RunWith(RobolectricTestRunner.class) +public class FeedDiscovererTest { + + private FeedDiscoverer fd; + + private File testDir; + + @Before + public void setUp() { + fd = new FeedDiscoverer(); + testDir = new File(InstrumentationRegistry + .getInstrumentation().getTargetContext().getFilesDir(), "FeedDiscovererTest"); + //noinspection ResultOfMethodCallIgnored + testDir.mkdir(); + assertTrue(testDir.exists()); + } + + @After + public void tearDown() throws Exception { + FileUtils.deleteDirectory(testDir); + } + + @SuppressWarnings("SameParameterValue") + private String createTestHtmlString(String rel, String type, String href, String title) { + return String.format("<html><head><title>Test</title><link rel=\"%s\" type=\"%s\" href=\"%s\" title=\"%s\"></head><body></body></html>", + rel, type, href, title); + } + + private String createTestHtmlString(String rel, String type, String href) { + return String.format("<html><head><title>Test</title><link rel=\"%s\" type=\"%s\" href=\"%s\"></head><body></body></html>", + rel, type, href); + } + + private void checkFindUrls(boolean isAlternate, boolean isRss, boolean withTitle, boolean isAbsolute, boolean fromString) throws Exception { + final String title = "Test title"; + final String hrefAbs = "http://example.com/feed"; + final String hrefRel = "/feed"; + final String base = "http://example.com"; + + final String rel = (isAlternate) ? "alternate" : "feed"; + final String type = (isRss) ? "application/rss+xml" : "application/atom+xml"; + final String href = (isAbsolute) ? hrefAbs : hrefRel; + + Map<String, String> res; + String html = (withTitle) ? createTestHtmlString(rel, type, href, title) + : createTestHtmlString(rel, type, href); + if (fromString) { + res = fd.findLinks(html, base); + } else { + File testFile = new File(testDir, "feed"); + FileOutputStream out = new FileOutputStream(testFile); + IOUtils.write(html, out, StandardCharsets.UTF_8); + out.close(); + res = fd.findLinks(testFile, base); + } + + assertNotNull(res); + assertEquals(1, res.size()); + for (String key : res.keySet()) { + assertEquals(hrefAbs, key); + } + assertTrue(res.containsKey(hrefAbs)); + if (withTitle) { + assertEquals(title, res.get(hrefAbs)); + } else { + assertEquals(href, res.get(hrefAbs)); + } + } + + @Test + public void testAlternateRSSWithTitleAbsolute() throws Exception { + checkFindUrls(true, true, true, true, true); + } + + @Test + public void testAlternateRSSWithTitleRelative() throws Exception { + checkFindUrls(true, true, true, false, true); + } + + @Test + public void testAlternateRSSNoTitleAbsolute() throws Exception { + checkFindUrls(true, true, false, true, true); + } + + @Test + public void testAlternateRSSNoTitleRelative() throws Exception { + checkFindUrls(true, true, false, false, true); + } + + @Test + public void testAlternateAtomWithTitleAbsolute() throws Exception { + checkFindUrls(true, false, true, true, true); + } + + @Test + public void testFeedAtomWithTitleAbsolute() throws Exception { + checkFindUrls(false, false, true, true, true); + } + + @Test + public void testAlternateRSSWithTitleAbsoluteFromFile() throws Exception { + checkFindUrls(true, true, true, true, false); + } +} |