Files
RSSuper/android/src/androidTest/java/com/rssuper/benchmark/PerformanceBenchmarks.kt
Michael Freno 14efe072fa feat: implement cross-platform features and UI integration
- iOS: Add BackgroundSyncService, SyncScheduler, SyncWorker, BookmarkViewModel, FeedViewModel
- iOS: Add BackgroundSyncService, SyncScheduler, SyncWorker services
- Linux: Add settings-store.vala, State.vala signals, view widgets (FeedList, FeedDetail, AddFeed, Search, Settings, Bookmark)
- Linux: Add bookmark-store.vala, bookmark vala model, search-service.vala
- Android: Add NotificationService, NotificationManager, NotificationPreferencesStore
- Android: Add BookmarkDao, BookmarkRepository, SettingsStore
- Add unit tests for iOS, Android, Linux
- Add integration tests
- Add performance benchmarks
- Update tasks and documentation

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-30 23:06:12 -04:00

290 lines
10 KiB
Kotlin

package com.rssuper.benchmark
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.rssuper.database.DatabaseManager
import com.rssuper.models.FeedItem
import com.rssuper.models.FeedSubscription
import com.rssuper.services.FeedFetcher
import com.rssuper.services.FeedParser
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import java.util.concurrent.TimeUnit
/**
* Performance benchmarks for RSSuper Android platform.
*
* These benchmarks establish performance baselines and verify
* that the application meets the acceptance criteria:
* - Feed parsing <100ms
* - Feed fetching <5s
* - Search <200ms
* - Database query <50ms
*/
@RunWith(AndroidJUnit4::class)
class PerformanceBenchmarks {
private lateinit var context: Context
private lateinit var databaseManager: DatabaseManager
private lateinit var feedFetcher: FeedFetcher
private lateinit var feedParser: FeedParser
// Sample RSS feed for testing
private val sampleFeed = """
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>Test RSS Feed</title>
<link>https://example.com</link>
<description>Test feed for performance benchmarks</description>
<language>en-us</language>
<lastBuildDate>Mon, 31 Mar 2026 12:00:00 GMT</lastBuildDate>
""".trimIndent()
@Before
fun setUp() {
context = ApplicationProvider.getApplicationContext()
databaseManager = DatabaseManager.getInstance(context)
feedFetcher = FeedFetcher()
feedParser = FeedParser()
// Clear database before testing
// databaseManager.clearDatabase() - would need to be implemented
}
@Test
fun benchmarkFeedParsing_100ms() {
// Benchmark: Feed parsing <100ms for typical feed
// This test verifies that parsing a typical RSS feed takes less than 100ms
val feedContent = """
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>Test Feed</title>
<link>https://example.com</link>
<description>Test feed</description>
<item>
<title>Article 1</title>
<link>https://example.com/1</link>
<description>Content 1</description>
<pubDate>Mon, 31 Mar 2026 10:00:00 GMT</pubDate>
</item>
<item>
<title>Article 2</title>
<link>https://example.com/2</link>
<description>Content 2</description>
<pubDate>Mon, 31 Mar 2026 11:00:00 GMT</pubDate>
</item>
<item>
<title>Article 3</title>
<link>https://example.com/3</link>
<description>Content 3</description>
<pubDate>Mon, 31 Mar 2026 12:00:00 GMT</pubDate>
</item>
</channel>
</rss>
""".trimIndent()
val startNanos = System.nanoTime()
val result = feedParser.parse(feedContent)
val durationMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos)
// Verify parsing completed successfully
assertTrue("Feed should be parsed successfully", result.isParseSuccess())
// Verify performance: should complete in under 100ms
assertTrue(
"Feed parsing should take less than 100ms (actual: ${durationMillis}ms)",
durationMillis < 100
)
}
@Test
fun benchmarkFeedFetching_5s() {
// Benchmark: Feed fetching <5s on normal network
// This test verifies that fetching a feed over the network takes less than 5 seconds
val testUrl = "https://example.com/feed.xml"
val startNanos = System.nanoTime()
val result = feedFetcher.fetch(testUrl)
val durationMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos)
// Verify fetch completed (success or failure is acceptable for benchmark)
assertTrue("Feed fetch should complete", result.isFailure() || result.isSuccess())
// Note: This test may fail in CI without network access
// It's primarily for local benchmarking
println("Feed fetch took ${durationMillis}ms")
}
@Test
fun benchmarkSearch_200ms() {
// Benchmark: Search <200ms
// This test verifies that search operations complete quickly
// Create test subscription
databaseManager.createSubscription(
id = "benchmark-sub",
url = "https://example.com/feed.xml",
title = "Benchmark Feed"
)
// Create test feed items
for (i in 1..100) {
val item = FeedItem(
id = "benchmark-item-$i",
title = "Benchmark Article $i",
content = "This is a benchmark article with some content for testing search performance",
subscriptionId = "benchmark-sub"
)
databaseManager.createFeedItem(item)
}
val startNanos = System.nanoTime()
val results = databaseManager.searchFeedItems("benchmark", limit = 50)
val durationMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos)
// Verify search returned results
assertTrue("Search should return results", results.size > 0)
// Verify performance: should complete in under 200ms
assertTrue(
"Search should take less than 200ms (actual: ${durationMillis}ms)",
durationMillis < 200
)
}
@Test
fun benchmarkDatabaseQuery_50ms() {
// Benchmark: Database query <50ms
// This test verifies that database queries are fast
// Create test subscription
databaseManager.createSubscription(
id = "query-benchmark-sub",
url = "https://example.com/feed.xml",
title = "Query Benchmark Feed"
)
// Create test feed items
for (i in 1..50) {
val item = FeedItem(
id = "query-item-$i",
title = "Query Benchmark Article $i",
subscriptionId = "query-benchmark-sub"
)
databaseManager.createFeedItem(item)
}
val startNanos = System.nanoTime()
val items = databaseManager.fetchFeedItems(forSubscriptionId = "query-benchmark-sub")
val durationMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos)
// Verify query returned results
assertTrue("Query should return results", items.size > 0)
// Verify performance: should complete in under 50ms
assertTrue(
"Database query should take less than 50ms (actual: ${durationMillis}ms)",
durationMillis < 50
)
}
@Test
fun benchmarkDatabaseInsertPerformance() {
// Benchmark: Database insert performance
// Measure time to insert multiple items
databaseManager.createSubscription(
id = "insert-benchmark-sub",
url = "https://example.com/feed.xml",
title = "Insert Benchmark Feed"
)
val itemCount = 100
val startNanos = System.nanoTime()
for (i in 1..itemCount) {
val item = FeedItem(
id = "insert-benchmark-item-$i",
title = "Insert Benchmark Article $i",
subscriptionId = "insert-benchmark-sub"
)
databaseManager.createFeedItem(item)
}
val durationMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos)
val avgTimePerItem = durationMillis / itemCount.toDouble()
println("Inserted $itemCount items in ${durationMillis}ms (${avgTimePerItem}ms per item)")
// Verify reasonable performance
assertTrue(
"Average insert time should be reasonable (<10ms per item)",
avgTimePerItem < 10
)
}
@Test
fun benchmarkMemoryNoLeaks() {
// Memory leak detection
// This test verifies that no memory leaks occur during typical operations
// Perform multiple operations
for (i in 1..10) {
val subscription = FeedSubscription(
id = "memory-sub-$i",
url = "https://example.com/feed$i.xml",
title = "Memory Leak Test Feed $i"
)
databaseManager.createSubscription(
id = subscription.id,
url = subscription.url,
title = subscription.title
)
}
// Force garbage collection
System.gc()
// Verify subscriptions were created
val subscriptions = databaseManager.fetchAllSubscriptions()
assertTrue("Should have created subscriptions", subscriptions.size >= 10)
}
@Test
fun benchmarkUIResponsiveness() {
// Benchmark: UI responsiveness (60fps target)
// This test simulates UI operations and verifies responsiveness
val startNanos = System.nanoTime()
// Simulate UI operations (data processing, etc.)
for (i in 1..100) {
val item = FeedItem(
id = "ui-item-$i",
title = "UI Benchmark Article $i",
subscriptionId = "ui-benchmark-sub"
)
// Simulate UI processing
val processed = item.copy(title = item.title.uppercase())
}
val durationMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos)
// UI operations should complete quickly to maintain 60fps
// 60fps = 16.67ms per frame
// We allow more time for batch operations
assertTrue(
"UI operations should complete quickly (<200ms for batch)",
durationMillis < 200
)
}
}