Skip to content

Commit

Permalink
feat(android): add a heap growth detection test
Browse files Browse the repository at this point in the history
  • Loading branch information
msfjarvis committed May 12, 2024
1 parent 59acbe2 commit 83eb3c1
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 0 deletions.
9 changes: 9 additions & 0 deletions android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
*/
@file:Suppress("UnstableApiUsage")

import dev.msfjarvis.claw.gradle.addTestDependencies

plugins {
id("dev.msfjarvis.claw.android-application")
id("dev.msfjarvis.claw.rename-artifacts")
Expand All @@ -14,6 +16,7 @@ plugins {
id("dev.msfjarvis.claw.sentry")
id("dev.msfjarvis.claw.versioning-plugin")
alias(libs.plugins.aboutlibraries)
alias(libs.plugins.android.junit5)
alias(libs.plugins.anvil)
alias(libs.plugins.modulegraphassert)
alias(libs.plugins.whetstone)
Expand All @@ -25,6 +28,7 @@ plugins {
android {
namespace = "dev.msfjarvis.claw.android"
defaultConfig.applicationId = "dev.msfjarvis.claw.android"
defaultConfig.testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
buildFeatures.compose = true
composeOptions {
useLiveLiterals = false
Expand Down Expand Up @@ -113,4 +117,9 @@ dependencies {
implementation(projects.web)

kapt(libs.dagger.compiler)

addTestDependencies(project)
androidTestImplementation(libs.androidx.test.espresso.core)
androidTestImplementation(libs.androidx.test.uiautomator)
androidTestImplementation(libs.leakcanary.android.test)
}
13 changes: 13 additions & 0 deletions android/src/androidTest/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!--

Check warning

Code scanning / mobsfscan

The flag [android:allowBackup] should be set to false. By default it is set to true and allows anyone to backup your application data via adb. It allows users who have enabled USB debugging to copy application data off of the device. Warning

The flag [android:allowBackup] should be set to false. By default it is set to true and allows anyone to backup your application data via adb. It allows users who have enabled USB debugging to copy application data off of the device.
~ Copyright © 2021-2024 Harsh Shandilya.
~ Use of this source code is governed by an MIT-style
~ license that can be found in the LICENSE file or at
~ https://opensource.org/licenses/MIT.
-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android">

<!-- Performing the heap growth analysis in process requires more heap. -->
<application
android:largeHeap="true"/>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright © 2024 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.claw.android

import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By
import androidx.test.uiautomator.BySelector
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.Until
import com.google.common.truth.Truth.assertThat
import de.mannodermaus.junit5.ActivityScenarioExtension
import leakcanary.repeatingAndroidInProcessScenario
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.RegisterExtension
import shark.ObjectGrowthDetector
import shark.forAndroidHeap

class HeapGrowthCheck {
@JvmField
@RegisterExtension
val scenarioExtension = ActivityScenarioExtension.launch<MainActivity>()
private val detector = ObjectGrowthDetector.forAndroidHeap().repeatingAndroidInProcessScenario()
private lateinit var device: UiDevice

@BeforeEach
fun setUp() {
val instrumentation = InstrumentationRegistry.getInstrumentation()
device = UiDevice.getInstance(instrumentation)
}

@Test
fun verify_heap_growth() {
val heapGrowth = detector.findRepeatedlyGrowingObjects { device.exploreScreens() }
assertThat(heapGrowth.growingObjects).isEmpty()
}

private companion object {
fun UiDevice.exploreScreens() {
listOf("HOTTEST", "NEWEST", "SAVED").forEach { tag ->
waitForObject(By.res(tag)).click()
waitForIdle()
}
}

private fun UiDevice.waitForObject(selector: BySelector, timeout: Long = 10_000L): UiObject2 {
if (wait(Until.hasObject(selector), timeout)) {
return findObject(selector)
}

error("Object with selector [$selector] not found")
}
}
}
4 changes: 4 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[versions]
aboutLibraries = "11.1.4"
agp = "8.5.0-beta01"
android-junit5 = "1.10.0.0"
benchmark = "1.3.0-alpha04"
coil = "2.6.0"
# @keep used for kotlinCompilerExtensionVersion
Expand All @@ -12,6 +13,7 @@ junit = "5.10.2"
konvert = "3.2.0"
kotlin = "1.9.24"
kotlinResult = "2.0.0"
leakcanary = "3.0-alpha-4"
lifecycle = "2.8.0-rc01"
retrofit = "2.11.0"
richtext = "1.0.0-alpha01"
Expand Down Expand Up @@ -89,6 +91,7 @@ kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-c
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" }
kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "serialization" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization" }
leakcanary-android-test = { module = "com.squareup.leakcanary:leakcanary-android-test", version.ref = "leakcanary" }
material3-pulltorefresh = "eu.bambooapps:compose-material3-pullrefresh:1.1.1"
napier = "io.github.aakira:napier:2.7.1"
okhttp-bom = "com.squareup.okhttp3:okhttp-bom:4.12.0"
Expand All @@ -114,6 +117,7 @@ unfurl = "me.saket.unfurl:unfurl:1.7.0"
[plugins]
aboutlibraries = { id = "com.mikepenz.aboutlibraries.plugin", version.ref = "aboutLibraries" }
android-lint = { id = "com.android.lint", version.ref = "agp" }
android-junit5 = { id = "de.mannodermaus.android-junit5", version.ref = "android-junit5" }
android-test = { id = "com.android.test", version.ref = "agp" }
anvil = "com.squareup.anvil:2.5.0-beta09"
baselineprofile = { id = "androidx.baselineprofile", version.ref = "benchmark" }
Expand Down

0 comments on commit 83eb3c1

Please sign in to comment.