1 package leakcanary
2 
3 import org.junit.rules.RuleChain
4 import org.junit.rules.TestRule
5 import org.junit.runner.Description
6 import org.junit.runners.model.Statement
7 
8 /**
9  * [TestRule] that invokes [LeakAssertions.assertNoLeaks] after the test
10  * successfully evaluates. Pay attention to where you set up this rule in the
11  * rule chain as you might detect different leaks (e.g. around vs wrapped by the
12  * activity rule). It's also possible to use this rule several times in a rule
13  * chain.
14  *
15  * This rule automatically applies the [TestDescriptionHolder] rule.
16  */
17 class DetectLeaksAfterTestSuccess(
18   private val tag: String = DetectLeaksAfterTestSuccess::class.java.simpleName
19 ) : TestRule {
applynull20   override fun apply(base: Statement, description: Description): Statement {
21     return TestDescriptionHolder.wrap(object : Statement() {
22       override fun evaluate() {
23         try {
24           base.evaluate()
25           // If the test fails, evaluate() will throw and we won't run the analysis (which is good).
26           LeakAssertions.assertNoLeaks(tag)
27         } finally {
28           // Otherwise upstream test failures will be reported as leaks.
29           // https://github.com/square/leakcanary/issues/2297
30           AppWatcher.objectWatcher.clearWatchedObjects()
31         }
32       }
33     }, description)
34   }
35 
36   companion object {
37     /**
38      * A helper function to trigger leak detection twice during test tear down, before and after
39      * the tear down of a set of wrapped rule chains. For example, this can be useful to detect
40      * leaks both right before and right after the activity under test is destroyed. Before means
41      * we can detect detached fragment leaks that go away when the activity is destroyed. After
42      * means we can detect activity leaks.
43      *
44      * ```kotlin
45      * RuleChain.outerRule(LoginRule())
46      *   .detectLeaksAfterTestSuccessWrapping("ActivitiesDestroyed") {
47      *     around(ActivityScenarioRule(MyActivity::class.java))
48      *   }
49      *   .around(LoadingScreenRule())
50      * ```
51      */
detectLeaksAfterTestSuccessWrappingnull52     fun RuleChain.detectLeaksAfterTestSuccessWrapping(
53       tag: String,
54       wrapped: RuleChain.() -> RuleChain
55     ): RuleChain {
56       return around(DetectLeaksAfterTestSuccess("After$tag")).wrapped()
57         .around(DetectLeaksAfterTestSuccess("Before$tag"))
58     }
59   }
60 }
61 
62 
63