<lambda>null1package 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