<lambda>null1 package leakcanary
2 
3 import android.app.Activity
4 import android.content.Intent
5 import android.net.Uri
6 import androidx.test.espresso.Espresso.onView
7 import androidx.test.espresso.action.ViewActions.click
8 import androidx.test.espresso.assertion.ViewAssertions.matches
9 import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
10 import androidx.test.espresso.matcher.ViewMatchers.withText
11 import androidx.test.rule.ActivityTestRule
12 import java.io.File
13 import java.util.concurrent.CountDownLatch
14 import java.util.concurrent.TimeUnit.SECONDS
15 import leakcanary.EventListener.Event.HeapAnalysisDone
16 import org.junit.Rule
17 import org.junit.Test
18 import org.junit.rules.TemporaryFolder
19 import shark.HprofWriterHelper
20 import shark.ValueHolder.IntHolder
21 import shark.dumpToBytes
22 
23 internal class LeakActivityTest {
24 
25   @get:Rule
26   var testFolder = TemporaryFolder()
27 
28   @Suppress("UNCHECKED_CAST")
29   // This class is internal but ActivityTestRule requires being passed in the real class.
30   private val leakActivityClass = Class.forName("leakcanary.internal.activity.LeakActivity")
31     as Class<Activity>
32 
33   @get:Rule
34   val activityTestRule = object : ActivityTestRule<Activity>(leakActivityClass, false, false) {
35     override fun getActivityIntent(): Intent {
36       return LeakCanary.newLeakDisplayActivityIntent()
37     }
38   }
39 
40   @Test
41   fun importHeapDumpFile() = tryAndRestoreConfig {
42     val latch = CountDownLatch(1)
43     LeakCanary.config = LeakCanary.config.run {
44       copy(eventListeners = eventListeners + EventListener { event ->
45         if (event is HeapAnalysisDone<*>) {
46           latch.countDown()
47         }
48       })
49     }
50     val hprof = writeHeapDump {
51       "Holder" clazz {
52         staticField["leak"] = "com.example.Leaking" watchedInstance {}
53       }
54     }
55     val intent = Intent(Intent.ACTION_VIEW, Uri.fromFile(hprof))
56     activityTestRule.launchActivity(intent)
57     require(latch.await(5, SECONDS)) {
58       "Heap analysis not done within 5 seconds of starting import"
59     }
60     onView(withText("1 Heap Dump")).check(matches(isDisplayed()))
61     onView(withText("1 Distinct Leak")).perform(click())
62     onView(withText("Holder.leak")).check(matches(isDisplayed()))
63   }
64 
65   private fun writeHeapDump(block: HprofWriterHelper.() -> Unit): File {
66     val hprofBytes = dumpToBytes {
67       "android.os.Build" clazz {
68         staticField["MANUFACTURER"] = string("Samsing")
69         staticField["ID"] = string("M4-rc20")
70       }
71       "android.os.Build\$VERSION" clazz {
72         staticField["SDK_INT"] = IntHolder(47)
73       }
74       block()
75     }
76     return testFolder.newFile("temp.hprof").apply {
77       writeBytes(hprofBytes)
78       require(exists()) {
79         "$this does not exist"
80       }
81       require(length().toInt() == hprofBytes.size) {
82         "$this has size ${length()} instead of expected ${hprofBytes.size}"
83       }
84     }
85   }
86 
87   private fun tryAndRestoreConfig(block: () -> Unit) {
88     val original = LeakCanary.config
89     try {
90       block()
91     } finally {
92       LeakCanary.config = original
93     }
94   }
95 
96 }
97