• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download

<lambda>null1 package leakcanary
2 
3 import androidx.test.espresso.Espresso.onData
4 import androidx.test.espresso.Espresso.onView
5 import androidx.test.espresso.action.ViewActions.click
6 import androidx.test.espresso.assertion.ViewAssertions.matches
7 import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
8 import androidx.test.espresso.matcher.ViewMatchers.withId
9 import androidx.test.espresso.matcher.ViewMatchers.withText
10 import androidx.test.platform.app.InstrumentationRegistry
11 import androidx.test.rule.ActivityTestRule
12 import com.squareup.leakcanary.core.R
13 import java.io.File
14 import leakcanary.internal.activity.LeakActivity
15 import leakcanary.internal.activity.db.HeapAnalysisTable
16 import leakcanary.internal.activity.db.LeakTable.AllLeaksProjection
17 import leakcanary.internal.activity.db.ScopedLeaksDb
18 import org.hamcrest.Description
19 import org.hamcrest.Matcher
20 import org.hamcrest.TypeSafeMatcher
21 import org.junit.Rule
22 import org.junit.Test
23 import org.junit.rules.RuleChain
24 import org.junit.rules.TemporaryFolder
25 import shark.GcRoot.JniGlobal
26 import shark.HeapAnalyzer
27 import shark.HprofWriterHelper
28 import shark.LeakTraceObject
29 import shark.OnAnalysisProgressListener
30 import shark.ValueHolder.IntHolder
31 import shark.dump
32 
33 internal class LeakActivityTest {
34 
35   private val activityTestRule = ActivityTestRule(LeakActivity::class.java, false, false)
36 
37   @get:Rule
38   var testFolder = TemporaryFolder()
39 
40   @get:Rule
41   var rules: RuleChain = RuleChain.outerRule(DatabaseRule())
42     .around(activityTestRule)
43 
44   @Test
45   fun noLeakOnHome() {
46     activityTestRule.launchActivity(null)
47     onView(withText("0 Distinct Leaks")).check(matches(isDisplayed()))
48   }
49 
50   @Test
51   fun oneLeakOnHome() {
52     insertHeapDump {
53       "Holder" clazz {
54         staticField["leak"] = "com.example.Leaking" watchedInstance {}
55       }
56     }
57     activityTestRule.launchActivity(null)
58     onView(withText("1 Distinct Leak")).check(matches(isDisplayed()))
59   }
60 
61   @Test
62   fun seeLeakOnLeakScreen() {
63     insertHeapDump {
64       "Holder" clazz {
65         staticField["leak"] = "com.example.Leaking" watchedInstance {}
66       }
67     }
68     activityTestRule.launchActivity(null)
69 
70     onData(withItem<AllLeaksProjection> { it.shortDescription == "Holder.leak" })
71       .perform(click())
72     onData(withItem<LeakTraceObject> { it.className == "com.example.Leaking" })
73       .inAdapterView(withId(R.id.leak_canary_list))
74       .check(matches(isDisplayed()))
75   }
76 
77   @Test
78   fun leakWithEmptyReferencePath() {
79     insertHeapDump {
80       val leakingInstance = "com.example.Leaking" watchedInstance {}
81       gcRoot(JniGlobal(id = leakingInstance.value, jniGlobalRefId = 42))
82     }
83     activityTestRule.launchActivity(null)
84 
85     onData(withItem<AllLeaksProjection> { it.shortDescription == "com.example.Leaking" })
86       .perform(click())
87     onData(withItem<LeakTraceObject> { it.className == "com.example.Leaking" })
88       .inAdapterView(withId(R.id.leak_canary_list))
89       .check(matches(isDisplayed()))
90   }
91 
92   private fun writeHeapDump(block: HprofWriterHelper.() -> Unit): File {
93     val hprofFile = testFolder.newFile("temp.hprof")
94     hprofFile.dump {
95       "android.os.Build" clazz {
96         staticField["MANUFACTURER"] = string("Samsing")
97         staticField["ID"] = string("M4-rc20")
98       }
99       "android.os.Build\$VERSION" clazz {
100         staticField["SDK_INT"] = IntHolder(47)
101       }
102       block()
103     }
104     return hprofFile
105   }
106 
107   private fun insertHeapDump(block: HprofWriterHelper.() -> Unit) {
108     val hprofFile = writeHeapDump(block)
109     val heapAnalyzer = HeapAnalyzer(OnAnalysisProgressListener.NO_OP)
110     val result = heapAnalyzer.analyze(
111       heapDumpFile = hprofFile,
112       leakingObjectFinder = LeakCanary.config.leakingObjectFinder,
113       referenceMatchers = LeakCanary.config.referenceMatchers,
114       computeRetainedHeapSize = LeakCanary.config.computeRetainedHeapSize,
115       objectInspectors = LeakCanary.config.objectInspectors,
116       metadataExtractor = LeakCanary.config.metadataExtractor,
117       proguardMapping = null
118     )
119     val instrumentation = InstrumentationRegistry.getInstrumentation()
120     val context = instrumentation.targetContext
121     ScopedLeaksDb.writableDatabase(context) { db ->
122       HeapAnalysisTable.insert(db, result)
123     }
124   }
125 
126   inline fun <reified T : Any> withItem(
127     filterDescription: String? = null,
128     crossinline filter: (T) -> Boolean
129   ): Matcher<T> {
130     return object : TypeSafeMatcher<T>(T::class.java) {
131       override fun describeTo(description: Description) {
132         if (filterDescription != null) {
133           description.appendText("is $filterDescription")
134         }
135       }
136 
137       override fun matchesSafely(item: T): Boolean {
138         return filter(item)
139       }
140     }
141   }
142 }
143 
tryAndRestoreConfignull144 fun tryAndRestoreConfig(block: () -> Unit) {
145   val original = LeakCanary.config
146   try {
147     block()
148   } finally {
149     LeakCanary.config = original
150   }
151 }
152