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 = """ Test RSS Feed https://example.com Test feed for performance benchmarks en-us Mon, 31 Mar 2026 12:00:00 GMT """.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 = """ Test Feed https://example.com Test feed Article 1 https://example.com/1 Content 1 Mon, 31 Mar 2026 10:00:00 GMT Article 2 https://example.com/2 Content 2 Mon, 31 Mar 2026 11:00:00 GMT Article 3 https://example.com/3 Content 3 Mon, 31 Mar 2026 12:00:00 GMT """.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 ) } }