Files
LibreMail/app/src/test/kotlin/org/libremail/push/BatteryOptimizationManagerTest.kt
T
JMR-devandClaude Opus 4.8 01841e9faf feat(onboarding): deep-link battery step nearer the per-app background-activity screen (best-effort)
Spiked #150 against the AOSP Settings source (not just the reference docs): no
public, non-hidden Settings action opens the "Unrestricted/Optimized/Restricted"
screen directly for a specific package. ACTION_VIEW_ADVANCED_POWER_USAGE_DETAIL
would, but it's @hide/non-SDK; the other public battery action,
ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS, isn't package-scoped and is a worse
landing for one known app. So ACTION_APPLICATION_DETAILS_SETTINGS (one tap from
the target via "Battery" on stock/Pixel/AOSP) stays the primary target.

BatteryOptimizationManager.settingsIntent() is restructured into a verified,
never-dead-end fallback chain: try app-details, and if it doesn't resolve on
some device, fall back to the battery-optimization list rather than nothing.
The ordering/selection logic is extracted into a small Android-free helper so
it's directly unit-testable; a new instrumented test checks the real candidate
intents/order against a real PackageManager.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 17:19:42 -05:00

50 lines
1.9 KiB
Kotlin

// SPDX-License-Identifier: GPL-3.0-or-later
package org.libremail.push
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
/**
* Unit tests for the `firstResolvableOrElseLast` fallback-selection helper that backs
* [BatteryOptimizationManager.settingsIntent] (#150). Deliberately exercised with plain `String`
* candidates rather than real `Intent`s: `Intent`/`Uri`/`PackageManager` are unmocked Android SDK stubs
* in a JVM unit test and throw on use, so the selection/ordering logic is kept generic and Android-free
* specifically so it's testable here (see its KDoc). The real candidate intents and end-to-end
* resolution are covered by the instrumented `BatteryOptimizationStepTest`.
*/
class BatteryOptimizationManagerTest {
@Test
fun `returns the first candidate that resolves`() {
assertEquals("b", firstResolvableOrElseLast(listOf("a", "b", "c")) { it == "b" })
}
@Test
fun `returns the only candidate when it is the first checked, even if it would not resolve`() {
assertEquals("only", firstResolvableOrElseLast(listOf("only")) { it == "only" })
}
@Test
fun `falls back to the last candidate when none resolve, rather than dead-ending`() {
assertEquals("c", firstResolvableOrElseLast(listOf("a", "b", "c")) { false })
}
@Test
fun `falls back to the last candidate even when it is the only one and it does not resolve`() {
assertEquals("only", firstResolvableOrElseLast(listOf("only")) { false })
}
@Test
fun `prefers an earlier resolving candidate over a later one, even if both resolve`() {
assertEquals("first", firstResolvableOrElseLast(listOf("first", "second")) { true })
}
@Test
fun `rejects an empty candidate list rather than silently returning nothing`() {
assertFailsWith<IllegalArgumentException> {
firstResolvableOrElseLast(emptyList<String>()) { true }
}
}
}