Merge "Only report tryTest errors when not caught" into main

This commit is contained in:
Treehugger Robot 2024-12-02 01:48:40 +00:00 • committed by Gerrit Code Review
commit 739a6c4320
3 changed files with 119 additions and 19 deletions

View file

@ -17,14 +17,19 @@
package com.android.net.module.util
import android.util.Log
import com.android.testutils.TryTestConfig
import com.android.testutils.tryTest
import java.util.function.Consumer
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNull
import kotlin.test.assertTrue
import kotlin.test.fail
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue
import kotlin.test.fail
private val TAG = CleanupTest::class.simpleName
@ -34,6 +39,18 @@ class CleanupTest {
class TestException2 : Exception()
class TestException3 : Exception()
private var originalDiagnosticsCollector: Consumer<Throwable>? = null
@Before
fun setUp() {
originalDiagnosticsCollector = TryTestConfig.swapDiagnosticsCollector(null)
}
@After
fun tearDown() {
TryTestConfig.swapDiagnosticsCollector(originalDiagnosticsCollector)
}
@Test
fun testNotThrow() {
var x = 1
@ -220,4 +237,74 @@ class CleanupTest {
assertTrue(thrown.suppressedExceptions[1] is TestException3)
assert(x == 7)
}
@Test
fun testNoErrorReportingWhenCaught() {
var error: Throwable? = null
TryTestConfig.swapDiagnosticsCollector {
error = it
}
var x = 1
tryTest {
x = 2
throw TestException1()
x = 3
}.catch<TestException1> {
x = 4
} cleanup {
x = 5
}
assertEquals(5, x)
assertNull(error)
}
@Test
fun testErrorReportingInTry() {
var error: Throwable? = null
TryTestConfig.swapDiagnosticsCollector {
assertNull(error)
error = it
}
var x = 1
assertFailsWith<TestException1> {
tryTest {
x = 2
throw TestException1()
x = 3
} cleanupStep {
throw TestException2()
x = 4
} cleanup {
x = 5
}
}
assertEquals(5, x)
assertTrue(error is TestException1)
}
@Test
fun testErrorReportingInCatch() {
var error: Throwable? = null
TryTestConfig.swapDiagnosticsCollector {
assertNull(error)
error = it
}
var x = 1
assertFailsWith<TestException2> {
tryTest {
throw TestException1()
x = 2
}.catch<TestException1> {
throw TestException2()
x = 3
} cleanup {
x = 4
}
}
assertEquals(4, x)
assertTrue(error is TestException2)
}
}

View file

@ -114,7 +114,7 @@ class ConnectivityDiagnosticsCollector : BaseMetricListener() {
override fun onSetUp() {
assertNull(instance, "ConnectivityDiagnosticsCollectors were set up multiple times")
instance = this
TryTestConfig.setDiagnosticsCollector { throwable ->
TryTestConfig.swapDiagnosticsCollector { throwable ->
if (runOnFailure(throwable)) {
collectTestFailureDiagnostics(throwable)
}

View file

@ -75,13 +75,21 @@ import javax.annotation.CheckReturnValue
*/
object TryTestConfig {
internal var diagnosticsCollector: Consumer<Throwable>? = null
private var diagnosticsCollector: Consumer<Throwable>? = null
/**
* Set the diagnostics collector to be used in case of failure in [tryTest].
*
* @return The previous collector.
*/
fun setDiagnosticsCollector(collector: Consumer<Throwable>) {
fun swapDiagnosticsCollector(collector: Consumer<Throwable>?): Consumer<Throwable>? {
val oldCollector = diagnosticsCollector
diagnosticsCollector = collector
return oldCollector
}
fun reportError(e: Throwable) {
diagnosticsCollector?.accept(e)
}
}
@ -90,14 +98,10 @@ fun <T> tryTest(block: () -> T) = TryExpr(
try {
Result.success(block())
} catch (e: Throwable) {
TryTestConfig.diagnosticsCollector?.accept(e)
Result.failure(e)
})
}, skipErrorReporting = false)
// Some downstream branches have an older kotlin that doesn't know about value classes.
// TODO : Change this to "value class" when aosp no longer merges into such branches.
@Suppress("INLINE_CLASS_DEPRECATED")
inline class TryExpr<T>(val result: Result<T>) {
class TryExpr<T>(val result: Result<T>, val skipErrorReporting: Boolean) {
inline infix fun <reified E : Throwable> catch(block: (E) -> T): TryExpr<T> {
val originalException = result.exceptionOrNull()
if (originalException !is E) return this
@ -105,23 +109,32 @@ inline class TryExpr<T>(val result: Result<T>) {
Result.success(block(originalException))
} catch (e: Throwable) {
Result.failure(e)
})
}, this.skipErrorReporting)
}
@CheckReturnValue
inline infix fun cleanupStep(block: () -> Unit): TryExpr<T> {
// Report errors before the cleanup step, but after catch blocks that may suppress it
val originalException = result.exceptionOrNull()
var nextSkipErrorReporting = skipErrorReporting
if (!skipErrorReporting && originalException != null) {
TryTestConfig.reportError(originalException)
nextSkipErrorReporting = true
}
try {
block()
} catch (e: Throwable) {
val originalException = result.exceptionOrNull()
return TryExpr(if (null == originalException) {
Result.failure(e)
return if (null == originalException) {
if (!skipErrorReporting) {
TryTestConfig.reportError(e)
}
TryExpr(Result.failure(e), skipErrorReporting = true)
} else {
originalException.addSuppressed(e)
Result.failure(originalException)
})
TryExpr(Result.failure(originalException), true)
}
}
return this
return TryExpr(result, nextSkipErrorReporting)
}
inline infix fun cleanup(block: () -> Unit): T = cleanupStep(block).result.getOrThrow()