Files
RSSuper/native-route/android/src/test/java/com/rssuper/parsing/AtomParserTest.kt
Michael Freno d84b8ff4e8 Implement Android RSS/Atom feed parser
- Add FeedParser.kt with automatic feed type detection
- Add RSSParser.kt for RSS 2.0 feeds
- Add AtomParser.kt for Atom 1.0 feeds
- Add comprehensive unit tests for both parsers
- Support iTunes namespace and enclosures
- Fix pre-existing compilation issues in the codebase
- Update build.gradle.kts with proper dependencies and AGP 8.5.0
2026-03-30 09:01:49 -04:00

246 lines
8.3 KiB
Kotlin

package com.rssuper.parsing
import com.rssuper.models.Enclosure
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [24])
class AtomParserTest {
@Test
fun testParseBasicAtom() {
val xml = """
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Atom Feed</title>
<subtitle>Feed subtitle</subtitle>
<link href="https://example.com" rel="alternate"/>
<id>urn:uuid:feed-id-123</id>
<updated>2024-01-01T12:00:00Z</updated>
<generator>Atom Generator</generator>
<entry>
<title>Entry 1</title>
<link href="https://example.com/entry1" rel="alternate"/>
<id>urn:uuid:entry-1</id>
<updated>2024-01-01T10:00:00Z</updated>
<summary>Summary of entry 1</summary>
</entry>
<entry>
<title>Entry 2</title>
<link href="https://example.com/entry2" rel="alternate"/>
<id>urn:uuid:entry-2</id>
<updated>2023-12-31T10:00:00Z</updated>
<content>Full content of entry 2</content>
</entry>
</feed>
""".trimIndent()
val feed = AtomParser.parse(xml, "https://example.com/feed.atom")
assertNotNull(feed)
assertEquals("Atom Feed", feed.title)
assertEquals("https://example.com", feed.link)
assertEquals("Feed subtitle", feed.subtitle)
assertEquals(2, feed.items.size)
val entry1 = feed.items[0]
assertEquals("Entry 1", entry1.title)
assertEquals("https://example.com/entry1", entry1.link)
assertEquals("Summary of entry 1", entry1.description)
assertNotNull(entry1.published)
val entry2 = feed.items[1]
assertEquals("Entry 2", entry2.title)
assertEquals("Full content of entry 2", entry2.content)
}
@Test
fun testParseAtomWithAuthor() {
val xml = """
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Author Feed</title>
<id>urn:uuid:feed-id</id>
<entry>
<title>Entry with Author</title>
<id>urn:uuid:entry</id>
<author>
<name>John Doe</name>
<email>john@example.com</email>
</author>
</entry>
</feed>
""".trimIndent()
val feed = AtomParser.parse(xml, "https://example.com/feed.atom")
assertNotNull(feed)
val entry = feed.items[0]
assertEquals("John Doe", entry.author)
}
@Test
fun testParseAtomWithCategories() {
val xml = """
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Categorized Feed</title>
<id>urn:uuid:feed-id</id>
<entry>
<title>Categorized Entry</title>
<id>urn:uuid:entry</id>
<category term="technology"/>
<category term="programming"/>
</entry>
</feed>
""".trimIndent()
val feed = AtomParser.parse(xml, "https://example.com/feed.atom")
assertNotNull(feed)
val entry = feed.items[0]
assertEquals(2, entry.categories?.size)
assertEquals("technology", entry.categories?.get(0))
assertEquals("programming", entry.categories?.get(1))
}
@Test
fun testParseAtomWithEnclosure() {
val xml = """
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Enclosure Feed</title>
<id>urn:uuid:feed-id</id>
<entry>
<title>Episode</title>
<id>urn:uuid:entry</id>
<link href="https://example.com/ep.mp3" rel="enclosure" type="audio/mpeg" length="12345678"/>
</entry>
</feed>
""".trimIndent()
val feed = AtomParser.parse(xml, "https://example.com/feed.atom")
assertNotNull(feed)
val entry = feed.items[0]
assertNotNull(entry.enclosure)
assertEquals("https://example.com/ep.mp3", entry.enclosure?.url)
assertEquals("audio/mpeg", entry.enclosure?.type)
assertEquals(12345678L, entry.enclosure?.length)
}
@Test
fun testParseAtomWithContent() {
val xml = """
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Content Feed</title>
<id>urn:uuid:feed-id</id>
<entry>
<title>Entry</title>
<id>urn:uuid:entry</id>
<summary>Short summary</summary>
<content>Full HTML content</content>
</entry>
</feed>
""".trimIndent()
val feed = AtomParser.parse(xml, "https://example.com/feed.atom")
assertNotNull(feed)
val entry = feed.items[0]
assertEquals("Full HTML content", entry.content)
assertEquals("Short summary", entry.description)
}
@Test
fun testParseAtomWithiTunesExtension() {
val xml = """
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd">
<title>Podcast</title>
<id>urn:uuid:feed-id</id>
<entry>
<title>Episode</title>
<id>urn:uuid:entry</id>
<itunes:duration>3600</itunes:duration>
<itunes:summary>Episode summary</itunes:summary>
</entry>
</feed>
""".trimIndent()
val feed = AtomParser.parse(xml, "https://example.com/feed.atom")
assertNotNull(feed)
val entry = feed.items[0]
assertEquals("Episode summary", entry.description)
}
@Test
fun testParseAtomWithPublished() {
val xml = """
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Date Feed</title>
<id>urn:uuid:feed-id</id>
<updated>2024-06-15T12:00:00Z</updated>
<entry>
<title>Entry</title>
<id>urn:uuid:entry</id>
<published>2024-01-01T08:00:00Z</published>
<updated>2024-01-02T10:00:00Z</updated>
</entry>
</feed>
""".trimIndent()
val feed = AtomParser.parse(xml, "https://example.com/feed.atom")
assertNotNull(feed)
val entry = feed.items[0]
assertNotNull(entry.published)
}
@Test
fun testParseAtomWithEmptyFeed() {
val xml = """
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Empty Feed</title>
<id>urn:uuid:feed-id</id>
</feed>
""".trimIndent()
val feed = AtomParser.parse(xml, "https://example.com/feed.atom")
assertNotNull(feed)
assertEquals("Empty Feed", feed.title)
assertEquals(0, feed.items.size)
}
@Test
fun testParseAtomWithMissingFields() {
val xml = """
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<entry>
<title>Minimal Entry</title>
</entry>
</feed>
""".trimIndent()
val feed = AtomParser.parse(xml, "https://example.com/feed.atom")
assertNotNull(feed)
assertEquals("Untitled Feed", feed.title)
assertEquals(1, feed.items.size)
assertEquals("Minimal Entry", feed.items[0].title)
assertNull(feed.items[0].link)
}
}