diff --git a/basil/build.gradle.kts b/basil/build.gradle.kts index 9c9f56de..8137593a 100644 --- a/basil/build.gradle.kts +++ b/basil/build.gradle.kts @@ -39,6 +39,8 @@ dependencies { implementation(project(":feedbinclient")) implementation(project(":feedfinder")) testImplementation("junit:junit:4.13.2") + testImplementation("io.mockk:mockk-android:1.13.7") + testImplementation("io.mockk:mockk-agent:1.13.7") testImplementation(kotlin("test")) androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") diff --git a/basil/src/main/java/com/jocmp/basil/Account.kt b/basil/src/main/java/com/jocmp/basil/Account.kt index 9768106c..77cb5484 100644 --- a/basil/src/main/java/com/jocmp/basil/Account.kt +++ b/basil/src/main/java/com/jocmp/basil/Account.kt @@ -7,6 +7,7 @@ import com.jocmp.feedfinder.FeedFinder import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.net.URI +import java.net.URL import java.util.UUID data class Account( @@ -49,15 +50,17 @@ data class Account( val feed = Feed( id = UUID.randomUUID().toString(), - name = entry.name, - feedURL = found.feedURL.toString() + name = entryNameOrDefault(entry, found.name), + feedURL = found.feedURL.toString(), + siteURL = entrySiteURL(found.siteURL) ) if (entry.folderTitles.isEmpty()) { feeds.add(feed) } else { entry.folderTitles.forEach { folderTitle -> - val folder = folders.find { folder -> folder.title == folderTitle } ?: Folder(title = folderTitle) + val folder = folders.find { folder -> folder.title == folderTitle } + ?: Folder(title = folderTitle) folder.feeds.add(feed) @@ -74,6 +77,18 @@ data class Account( return feed } + private fun entrySiteURL(url: URL?): String { + return url?.toString() ?: "" + } + + private fun entryNameOrDefault(entry: FeedFormEntry, feedName: String): String { + if (entry.name.isBlank()) { + return feedName + } + + return entry.name + } + private suspend fun saveOPMLFile() = withContext(Dispatchers.IO) { opmlFile.save() } @@ -95,7 +110,7 @@ fun Account.asOPML(): String { opml += feed.asOPML(indentLevel = 2) } - folders.sortedBy { it.title } .forEach { folder -> + folders.sortedBy { it.title }.forEach { folder -> opml += folder.asOPML(indentLevel = 2) } diff --git a/basil/src/main/java/com/jocmp/basil/Feed.kt b/basil/src/main/java/com/jocmp/basil/Feed.kt index 5357b8d5..c99a74a2 100644 --- a/basil/src/main/java/com/jocmp/basil/Feed.kt +++ b/basil/src/main/java/com/jocmp/basil/Feed.kt @@ -6,7 +6,7 @@ data class Feed( val id: String, val name: String, val feedURL: String, - val homepageURL: String = "" + val siteURL: String = "" ) { override fun equals(other: Any?): Boolean { if (other is Feed) { @@ -21,6 +21,6 @@ data class Feed( } fun Feed.asOPML(indentLevel: Int): String { - val opml = "\n" + val opml = "\n" return opml.prepending(tabCount = indentLevel) } diff --git a/basil/src/test/java/com/jocmp/basil/AccountTest.kt b/basil/src/test/java/com/jocmp/basil/AccountTest.kt index 24e32ea2..0384c22f 100644 --- a/basil/src/test/java/com/jocmp/basil/AccountTest.kt +++ b/basil/src/test/java/com/jocmp/basil/AccountTest.kt @@ -1,10 +1,13 @@ package com.jocmp.basil +import com.jocmp.feedfinder.FeedFinder +import io.mockk.coEvery +import io.mockk.mockkConstructor import kotlinx.coroutines.runBlocking +import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder -import java.util.UUID import kotlin.test.assertContains import kotlin.test.assertEquals @@ -13,6 +16,15 @@ class AccountTest { @Rule val folder = TemporaryFolder() + @Before + fun setup() { + mockkConstructor(FeedFinder::class) + + coEvery { + anyConstructed().find() + } returns FeedFinder.Result.Success(listOf(FakeParserFeed())) + } + @Test fun opmlFile_endsWithSubscriptions() { val accountPath = folder.newFile().toURI() @@ -50,7 +62,7 @@ class AccountTest { val accountPath = folder.newFile().toURI() val account = Account(id = "777", path = accountPath) val entry = FeedFormEntry( - url = "https://www.theverge.com/rss/index.xml", + url = "https://theverge.com/rss/index.xml", name = "The Verge", folderTitles = listOf(), ) @@ -70,7 +82,7 @@ class AccountTest { val accountPath = folder.newFile().toURI() val account = Account(id = "777", path = accountPath) val entry = FeedFormEntry( - url = "https://www.theverge.com/rss/index.xml", + url = "https://theverge.com/rss/index.xml", name = "The Verge", folderTitles = listOf("Tech"), ) @@ -92,7 +104,7 @@ class AccountTest { runBlocking { account.addFolder("Tech") } val entry = FeedFormEntry( - url = "https://www.theverge.com/rss/index.xml", + url = "https://theverge.com/rss/index.xml", name = "The Verge", folderTitles = listOf("Tech"), ) @@ -114,7 +126,7 @@ class AccountTest { runBlocking { account.addFolder("Tech") } val entry = FeedFormEntry( - url = "https://www.theverge.com/rss/index.xml", + url = "https://theverge.com/rss/index.xml", name = "The Verge", folderTitles = listOf("Tech", "Culture"), ) diff --git a/basil/src/test/java/com/jocmp/basil/FakeParserFeed.kt b/basil/src/test/java/com/jocmp/basil/FakeParserFeed.kt new file mode 100644 index 00000000..75009ce1 --- /dev/null +++ b/basil/src/test/java/com/jocmp/basil/FakeParserFeed.kt @@ -0,0 +1,13 @@ +package com.jocmp.basil + +import com.jocmp.feedfinder.parser.Feed +import java.net.URL + +class FakeParserFeed( + override val name: String = "The Verge - All Posts", + override val feedURL: URL = URL("https://theverge.com/rss/index.xml"), + override val siteURL: URL? = null, + private val valid: Boolean = true +) : Feed { + override fun isValid() = valid +} diff --git a/feedfinder/src/main/java/com/jocmp/feedfinder/FeedFinder.kt b/feedfinder/src/main/java/com/jocmp/feedfinder/FeedFinder.kt index 0dc4abd9..7446b576 100644 --- a/feedfinder/src/main/java/com/jocmp/feedfinder/FeedFinder.kt +++ b/feedfinder/src/main/java/com/jocmp/feedfinder/FeedFinder.kt @@ -22,14 +22,10 @@ import java.net.URL // HTML takes response body class FeedFinder internal constructor( - val url: String, + private val url: String, private val request: Request = DefaultRequest() ) { - // Convert URL to HTTPS if missing - // 1. Download the request using a Java HTTP connection - // 2. If the response is an XML Feed itself, return - // 3. If the response is HTML and th - internal suspend fun find(): Result = withContext(Dispatchers.IO) { + suspend fun find(): Result = withContext(Dispatchers.IO) { try { val parsedURL = URI(url.withProtocol).toURL() val response = request.fetch(url = parsedURL) @@ -52,6 +48,7 @@ class FeedFinder internal constructor( private fun sources(response: Response): List { return listOf( + XMLSource(response), MetaLinkSource(response = response, request = request), ) } diff --git a/feedfinder/src/main/java/com/jocmp/feedfinder/Response.kt b/feedfinder/src/main/java/com/jocmp/feedfinder/Response.kt index 5e580946..5c08eb3e 100644 --- a/feedfinder/src/main/java/com/jocmp/feedfinder/Response.kt +++ b/feedfinder/src/main/java/com/jocmp/feedfinder/Response.kt @@ -1,9 +1,6 @@ package com.jocmp.feedfinder -import com.jocmp.feedfinder.parser.FakeFeed -import com.jocmp.feedfinder.parser.Feed import com.jocmp.feedfinder.parser.Parser -import com.jocmp.feedfinder.parser.XMLFeed import java.net.URL internal class Response(val url: URL, val body: String) { diff --git a/feedfinder/src/main/java/com/jocmp/feedfinder/parser/FakeFeed.kt b/feedfinder/src/main/java/com/jocmp/feedfinder/parser/FakeFeed.kt deleted file mode 100644 index f960f4c3..00000000 --- a/feedfinder/src/main/java/com/jocmp/feedfinder/parser/FakeFeed.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.jocmp.feedfinder.parser - -import java.net.URL - -class FakeFeed: Feed { - override fun isValid(): Boolean { - return false - } - - override val feedURL: URL - get() = URL("https://arstechnica.com/feed") -} diff --git a/feedfinder/src/main/java/com/jocmp/feedfinder/parser/Feed.kt b/feedfinder/src/main/java/com/jocmp/feedfinder/parser/Feed.kt index 126c6c31..cc6b09aa 100644 --- a/feedfinder/src/main/java/com/jocmp/feedfinder/parser/Feed.kt +++ b/feedfinder/src/main/java/com/jocmp/feedfinder/parser/Feed.kt @@ -5,5 +5,9 @@ import java.net.URL interface Feed { fun isValid(): Boolean + val name: String + val feedURL: URL + + val siteURL: URL? } diff --git a/feedfinder/src/main/java/com/jocmp/feedfinder/parser/XMLFeed.kt b/feedfinder/src/main/java/com/jocmp/feedfinder/parser/XMLFeed.kt index aa103067..a789305f 100644 --- a/feedfinder/src/main/java/com/jocmp/feedfinder/parser/XMLFeed.kt +++ b/feedfinder/src/main/java/com/jocmp/feedfinder/parser/XMLFeed.kt @@ -16,6 +16,14 @@ internal class XMLFeed( hasEntries() } + override val name: String + get() = channel!!.title!! + + override val siteURL: URL? + get() = channel?.link?.let { + URL(it) + } + private fun hasEntries(): Boolean { return channel != null && channel.items.isNotEmpty() diff --git a/feedfinder/src/main/java/com/jocmp/feedfinder/sources/BodyLinkSource.kt b/feedfinder/src/main/java/com/jocmp/feedfinder/sources/BodyLinkSource.kt deleted file mode 100644 index f6118548..00000000 --- a/feedfinder/src/main/java/com/jocmp/feedfinder/sources/BodyLinkSource.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.jocmp.feedfinder.sources - -import com.jocmp.feedfinder.parser.Feed - -internal class BodyLinkSource: Source { - override suspend fun find(): List { - return emptyList() - } -} diff --git a/feedfinder/src/main/java/com/jocmp/feedfinder/sources/Guess.kt b/feedfinder/src/main/java/com/jocmp/feedfinder/sources/Guess.kt deleted file mode 100644 index 90b3d53d..00000000 --- a/feedfinder/src/main/java/com/jocmp/feedfinder/sources/Guess.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.jocmp.feedfinder.sources - -// val urls = listOf(parsedURL) + variations.map { variation -> -// parsedURL.resolve(variation) -// } -// -// val documents = coroutineScope { -// urls.map { async { fetchDocument(it) } } -// .awaitAll() -// .filterNotNull() -// } - -// return find(documents = documents) - -private val variations = listOf( - "feed", - "rss" -) diff --git a/feedfinder/src/test/java/com/jocmp/feedfinder/FeedFinderTest.kt b/feedfinder/src/test/java/com/jocmp/feedfinder/FeedFinderTest.kt deleted file mode 100644 index d9ea3086..00000000 --- a/feedfinder/src/test/java/com/jocmp/feedfinder/FeedFinderTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.jocmp.feedfinder - -import kotlinx.coroutines.runBlocking -import org.junit.Rule -import org.junit.Test -import kotlin.test.assertEquals - -class FeedFinderTest { - @Test - fun find_returnsASuccess() = runBlocking { - val finder = FeedFinder("arstechnica.com") - - finder.find() - - assertEquals("", "") - } -} diff --git a/feedfinder/src/test/java/com/jocmp/feedfinder/sources/BodyLinkTest.kt b/feedfinder/src/test/java/com/jocmp/feedfinder/sources/BodyLinkTest.kt deleted file mode 100644 index 3c469cbb..00000000 --- a/feedfinder/src/test/java/com/jocmp/feedfinder/sources/BodyLinkTest.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.jocmp.feedfinder.sources - -import org.junit.Test - -class BodyLinkTest { - @Test - fun find() { - } -} diff --git a/feedfinder/src/test/java/com/jocmp/feedfinder/sources/XMLSourceTest.kt b/feedfinder/src/test/java/com/jocmp/feedfinder/sources/XMLSourceTest.kt index b428d608..a9900861 100644 --- a/feedfinder/src/test/java/com/jocmp/feedfinder/sources/XMLSourceTest.kt +++ b/feedfinder/src/test/java/com/jocmp/feedfinder/sources/XMLSourceTest.kt @@ -8,6 +8,7 @@ import java.net.URL import kotlin.math.exp import kotlin.test.assertEquals import kotlin.test.assertFalse +import kotlin.test.assertTrue class XMLSourceTest { @Test