mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-15 10:46:12 -04:00
fix: Stop suppressing foreground service timing exceptions
Suppressing ForegroundServiceDidNotStartInTimeException and re-entering Looper.loop() left zombie processes behind that kept collecting ANRs. Always delegate to the previous handler instead.
This commit is contained in:
@@ -7,67 +7,17 @@ import testhelpers.BaseTest
|
||||
class CapodUncaughtExceptionHandlerTest : BaseTest() {
|
||||
|
||||
@Test
|
||||
fun `suppresses first main thread foreground service timing exception`() {
|
||||
fun `delegates foreground service timing exception`() {
|
||||
val mainThread = Thread.currentThread()
|
||||
val previousHandler = RecordingHandler()
|
||||
val reports = mutableListOf<Throwable>()
|
||||
var loopCalls = 0
|
||||
val handler = CapodUncaughtExceptionHandler(
|
||||
previousHandler = previousHandler,
|
||||
mainThreadProvider = { mainThread },
|
||||
loopMainThread = { loopCalls++ },
|
||||
reportForegroundServiceTimingException = { reports += it },
|
||||
exit = { throw AssertionError("exitProcess($it)") },
|
||||
)
|
||||
val throwable = ForegroundServiceDidNotStartInTimeException()
|
||||
|
||||
handler.uncaughtException(mainThread, throwable)
|
||||
|
||||
loopCalls shouldBe 1
|
||||
reports shouldBe listOf(throwable)
|
||||
previousHandler.throwables shouldBe emptyList()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `delegates repeated main thread foreground service timing exception`() {
|
||||
val mainThread = Thread.currentThread()
|
||||
val previousHandler = RecordingHandler()
|
||||
val reports = mutableListOf<Throwable>()
|
||||
var loopCalls = 0
|
||||
val handler = CapodUncaughtExceptionHandler(
|
||||
previousHandler = previousHandler,
|
||||
mainThreadProvider = { mainThread },
|
||||
loopMainThread = { loopCalls++ },
|
||||
reportForegroundServiceTimingException = { reports += it },
|
||||
exit = { throw AssertionError("exitProcess($it)") },
|
||||
)
|
||||
val first = ForegroundServiceDidNotStartInTimeException()
|
||||
val second = ForegroundServiceDidNotStartInTimeException()
|
||||
|
||||
handler.uncaughtException(mainThread, first)
|
||||
handler.uncaughtException(mainThread, second)
|
||||
|
||||
loopCalls shouldBe 1
|
||||
reports shouldBe listOf(first)
|
||||
previousHandler.throwables shouldBe listOf(second)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `delegates foreground service timing exception from non-main thread`() {
|
||||
val mainThread = Thread.currentThread()
|
||||
val workerThread = Thread()
|
||||
val previousHandler = RecordingHandler()
|
||||
val handler = CapodUncaughtExceptionHandler(
|
||||
previousHandler = previousHandler,
|
||||
mainThreadProvider = { mainThread },
|
||||
loopMainThread = { throw AssertionError("loopMainThread should not run") },
|
||||
reportForegroundServiceTimingException = { throw AssertionError("report should not run") },
|
||||
exit = { throw AssertionError("exitProcess($it)") },
|
||||
)
|
||||
val throwable = ForegroundServiceDidNotStartInTimeException()
|
||||
|
||||
handler.uncaughtException(workerThread, throwable)
|
||||
|
||||
previousHandler.throwables shouldBe listOf(throwable)
|
||||
}
|
||||
|
||||
@@ -77,9 +27,6 @@ class CapodUncaughtExceptionHandlerTest : BaseTest() {
|
||||
val previousHandler = RecordingHandler()
|
||||
val handler = CapodUncaughtExceptionHandler(
|
||||
previousHandler = previousHandler,
|
||||
mainThreadProvider = { mainThread },
|
||||
loopMainThread = { throw AssertionError("loopMainThread should not run") },
|
||||
reportForegroundServiceTimingException = { throw AssertionError("report should not run") },
|
||||
exit = { throw AssertionError("exitProcess($it)") },
|
||||
)
|
||||
val throwable = IllegalStateException("boom")
|
||||
@@ -101,9 +48,6 @@ class CapodUncaughtExceptionHandlerTest : BaseTest() {
|
||||
val throwable = IllegalStateException("boom")
|
||||
val handler = CapodUncaughtExceptionHandler(
|
||||
previousHandler = previousHandler,
|
||||
mainThreadProvider = { mainThread },
|
||||
loopMainThread = { throw AssertionError("loopMainThread should not run") },
|
||||
reportForegroundServiceTimingException = { throw AssertionError("report should not run") },
|
||||
cancelBeforeDelegate = { events += "cancel" },
|
||||
exit = { throw AssertionError("exitProcess($it)") },
|
||||
)
|
||||
@@ -114,21 +58,19 @@ class CapodUncaughtExceptionHandlerTest : BaseTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `delegates loop failure after suppression`() {
|
||||
fun `delegates even when cancelBeforeDelegate throws`() {
|
||||
val mainThread = Thread.currentThread()
|
||||
val previousHandler = RecordingHandler()
|
||||
val loopFailure = IllegalStateException("loop failed")
|
||||
val throwable = IllegalStateException("boom")
|
||||
val handler = CapodUncaughtExceptionHandler(
|
||||
previousHandler = previousHandler,
|
||||
mainThreadProvider = { mainThread },
|
||||
loopMainThread = { throw loopFailure },
|
||||
reportForegroundServiceTimingException = {},
|
||||
cancelBeforeDelegate = { throw IllegalStateException("shutdown failed") },
|
||||
exit = { throw AssertionError("exitProcess($it)") },
|
||||
)
|
||||
|
||||
handler.uncaughtException(mainThread, ForegroundServiceDidNotStartInTimeException())
|
||||
handler.uncaughtException(mainThread, throwable)
|
||||
|
||||
previousHandler.throwables shouldBe listOf(loopFailure)
|
||||
previousHandler.throwables shouldBe listOf(throwable)
|
||||
}
|
||||
|
||||
private class RecordingHandler : Thread.UncaughtExceptionHandler {
|
||||
|
||||
Reference in New Issue
Block a user