-
Notifications
You must be signed in to change notification settings - Fork 4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support for Android Test Orchestrator
With this change, FailTestOnLeakRunListener now hooks into AndroidJUnitRunner when a test run starts to detect if the tests are run by Android Test Orchestrator, in which case we take over the binder callback to send optionally send an extra failure when a leak is detected in test. This works whether newRunListenerMode is false or true (ie whether OrchestratedInstrumentationListener runs before or after FailTestOnLeakRunListener). When a test finishes without any failure, we intercept any "test finished" message, check for memory leaks, optionally send a "test failure" message then send the "test finished message". This intercepting is done assuming a single thread calls both listeners (see `org.junit.runner.notification.RunNotifier#wrapIfNotThreadSafe`). As before with `Instrumentation.sendStatus()`, the current approach is tied to the test runner implementation details and might have to change to adapt to new `androidx.test:runner` versions. Other change: FailTestOnLeakRunListener will wait up to 2 seconds for all activities to be destroyed before running leak detection, and otherwise proceed. Fixes #1046
- Loading branch information
Showing
8 changed files
with
255 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 20 additions & 0 deletions
20
...roidx/test/orchestrator/instrumentationlistener/OrchestratedInstrumentationListenerSpy.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package androidx.test.orchestrator.instrumentationlistener | ||
|
||
import android.os.Bundle | ||
import androidx.test.orchestrator.callback.OrchestratorCallback | ||
|
||
internal fun OrchestratedInstrumentationListener.delegateSendTestNotification( | ||
onSendTestNotification: (Bundle, (Bundle) -> Unit) -> Unit | ||
) { | ||
val realCallback = odoCallback | ||
|
||
val sendTestNotificationCallback: (Bundle) -> Unit = { bundle -> | ||
realCallback.sendTestNotification(bundle) | ||
} | ||
|
||
odoCallback = object : OrchestratorCallback by realCallback { | ||
override fun sendTestNotification(bundle: Bundle) { | ||
onSendTestNotification(bundle, sendTestNotificationCallback) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 30 additions & 0 deletions
30
...d-instrumentation/src/main/java/leakcanary/internal/InstrumentationTestResultPublisher.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package leakcanary.internal | ||
|
||
import android.app.Instrumentation.REPORT_KEY_IDENTIFIER | ||
import android.os.Bundle | ||
import androidx.test.internal.runner.listener.InstrumentationResultPrinter.REPORT_KEY_NAME_CLASS | ||
import androidx.test.internal.runner.listener.InstrumentationResultPrinter.REPORT_KEY_NAME_TEST | ||
import androidx.test.internal.runner.listener.InstrumentationResultPrinter.REPORT_KEY_STACK | ||
import androidx.test.internal.runner.listener.InstrumentationResultPrinter.REPORT_VALUE_RESULT_FAILURE | ||
import androidx.test.platform.app.InstrumentationRegistry | ||
import leakcanary.FailTestOnLeakRunListener | ||
import org.junit.runner.Description | ||
|
||
internal class InstrumentationTestResultPublisher : TestResultPublisher { | ||
override fun publishTestFinished() { | ||
} | ||
|
||
override fun publishTestFailure( | ||
description: Description, | ||
trace: String | ||
) { | ||
val bundle = Bundle() | ||
bundle.putString(REPORT_KEY_IDENTIFIER, FailTestOnLeakRunListener::class.java.name) | ||
bundle.putString(REPORT_KEY_NAME_CLASS, description.className) | ||
bundle.putString(REPORT_KEY_NAME_TEST, description.methodName) | ||
bundle.putString(REPORT_KEY_STACK, trace) | ||
|
||
val instrumentation = InstrumentationRegistry.getInstrumentation() | ||
instrumentation.sendStatus(REPORT_VALUE_RESULT_FAILURE, bundle) | ||
} | ||
} |
75 changes: 75 additions & 0 deletions
75
...roid-instrumentation/src/main/java/leakcanary/internal/OrchestratorTestResultPublisher.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package leakcanary.internal | ||
|
||
import android.os.Bundle | ||
import androidx.test.orchestrator.instrumentationlistener.OrchestratedInstrumentationListener | ||
import androidx.test.orchestrator.instrumentationlistener.delegateSendTestNotification | ||
import androidx.test.orchestrator.junit.ParcelableDescription | ||
import androidx.test.orchestrator.junit.ParcelableFailure | ||
import androidx.test.orchestrator.junit.ParcelableResult | ||
import androidx.test.orchestrator.listeners.OrchestrationListenerManager.KEY_TEST_EVENT | ||
import androidx.test.orchestrator.listeners.OrchestrationListenerManager.TestEvent.TEST_FAILURE | ||
import androidx.test.orchestrator.listeners.OrchestrationListenerManager.TestEvent.TEST_FINISHED | ||
import androidx.test.orchestrator.listeners.OrchestrationListenerManager.TestEvent.TEST_RUN_FINISHED | ||
import org.junit.runner.Description | ||
|
||
internal class OrchestratorTestResultPublisher(listener: OrchestratedInstrumentationListener) : | ||
TestResultPublisher { | ||
|
||
private var sendTestFinished: (() -> Unit)? = null | ||
|
||
private var failureBundle: Bundle? = null | ||
|
||
private var receivedTestFinished: Boolean = false | ||
|
||
init { | ||
val failures = mutableListOf<ParcelableFailure>() | ||
listener.delegateSendTestNotification { testEventBundle, sendTestNotification -> | ||
|
||
when (testEventBundle.getString(KEY_TEST_EVENT)) { | ||
TEST_FINISHED.toString() -> { | ||
sendTestFinished = { | ||
failureBundle?.let { failureBundle -> | ||
failures += failureBundle.get("failure") as ParcelableFailure | ||
sendTestNotification(failureBundle) | ||
} | ||
sendTestNotification(testEventBundle) | ||
// reset for next test if any. | ||
sendTestFinished = null | ||
failureBundle = null | ||
receivedTestFinished = false | ||
} | ||
if (receivedTestFinished) { | ||
sendTestFinished!!.invoke() | ||
} | ||
} | ||
TEST_RUN_FINISHED.toString() -> { | ||
if (failures.isNotEmpty()) { | ||
val result = testEventBundle.get("result") as ParcelableResult | ||
result.failures += failures | ||
} | ||
sendTestNotification(testEventBundle) | ||
} | ||
else -> sendTestNotification(testEventBundle) | ||
} | ||
} | ||
} | ||
|
||
override fun publishTestFinished() { | ||
receivedTestFinished = true | ||
sendTestFinished?.invoke() | ||
} | ||
|
||
override fun publishTestFailure( | ||
description: Description, | ||
trace: String | ||
) { | ||
val result = Bundle() | ||
val failure = ParcelableFailure( | ||
ParcelableDescription(description), | ||
RuntimeException(trace) | ||
) | ||
result.putParcelable("failure", failure) | ||
result.putString(KEY_TEST_EVENT, TEST_FAILURE.toString()) | ||
this.failureBundle = result | ||
} | ||
} |
37 changes: 37 additions & 0 deletions
37
leakcanary-android-instrumentation/src/main/java/leakcanary/internal/TestResultPublisher.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package leakcanary.internal | ||
|
||
import androidx.test.orchestrator.instrumentationlistener.OrchestratedInstrumentationListener | ||
import androidx.test.platform.app.InstrumentationRegistry | ||
import androidx.test.runner.AndroidJUnitRunner | ||
import org.junit.runner.Description | ||
import shark.SharkLog | ||
|
||
internal interface TestResultPublisher { | ||
|
||
fun publishTestFinished() | ||
|
||
fun publishTestFailure( | ||
description: Description, | ||
trace: String | ||
) | ||
|
||
companion object { | ||
fun install(): TestResultPublisher { | ||
val instrumentation = InstrumentationRegistry.getInstrumentation() | ||
val orchestratorListener = if (instrumentation is AndroidJUnitRunner) { | ||
AndroidJUnitRunner::class.java.getDeclaredField("orchestratorListener") | ||
.run { | ||
isAccessible = true | ||
get(instrumentation) as OrchestratedInstrumentationListener? | ||
} | ||
} else null | ||
return if (orchestratorListener != null) { | ||
SharkLog.d { "Android Test Orchestrator detected, failures will be sent via binder callback" } | ||
OrchestratorTestResultPublisher(orchestratorListener) | ||
} else { | ||
SharkLog.d { "Failures will be sent via Instrumentation.sendStatus()" } | ||
InstrumentationTestResultPublisher() | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters