<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