main

square/leakcanary

Last updated at: 29/12/2023 09:38

ManualInstallTest.kt

TLDR

This file contains the ManualInstallTest class, which includes several test methods related to the installation and configuration of LeakCanary, a memory leak detection library for Android.

Methods

throwOnAnyThreadPolicyViolation

This method sets a StrictMode thread policy that detects all violations and throws an exception when any violation occurs. It takes a lambda function as a parameter and executes the function with the strict thread policy applied. After the execution, the previous thread policy is restored.

runOnMainSyncRethrowing

This method executes a given lambda function on the main thread using InstrumentationRegistry. Any throwable that occurs during the execution is saved, and if a throwable is present, it is thrown after the execution is completed.

Classes

ManualInstallTest

This class includes several test methods related to the installation and configuration of LeakCanary:

  • appWatcher_is_not_installed: This test method checks if AppWatcher is not installed.
  • can_update_LeakCanary_config_without_installing: This test method tries to update the configuration of LeakCanary without installing it.
  • no_thread_policy_violations_on_install: This test method checks if there are no thread policy violations when manually installing LeakCanary.
  • no_thread_policy_violations_on_config_update: This test method checks if there are no thread policy violations when updating the configuration of LeakCanary.
  • no_thread_policy_violations_on_install_then_config_update: This test method checks if there are no thread policy violations when manually installing LeakCanary and then updating its configuration.
  • no_thread_policy_violations_on_config_update_then_install: This test method checks if there are no thread policy violations when updating the configuration of LeakCanary and then manually installing it.
package leakcanary

import android.app.Application
import android.os.StrictMode
import android.os.StrictMode.ThreadPolicy
import androidx.test.platform.app.InstrumentationRegistry
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test

class ManualInstallTest {

  private val application: Application
    get() = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as Application

  @Test fun appWatcher_is_not_installed() {
    assertThat(AppWatcher.isInstalled).isFalse()
  }

  @Test fun can_update_LeakCanary_config_without_installing() = tryAndRestoreConfig {
    LeakCanary.config = LeakCanary.config.copy(dumpHeap = LeakCanary.config.dumpHeap)
  }

  @Test fun no_thread_policy_violations_on_install() {
    runOnMainSyncRethrowing {
      throwOnAnyThreadPolicyViolation {
        AppWatcher.manualInstall(application)
      }
    }
  }

  @Test fun no_thread_policy_violations_on_config_update() {
    runOnMainSyncRethrowing {
      throwOnAnyThreadPolicyViolation {
        LeakCanary.config = LeakCanary.config.copy(dumpHeap = LeakCanary.config.dumpHeap)
      }
    }
  }

  @Test fun no_thread_policy_violations_on_install_then_config_update() {
    runOnMainSyncRethrowing {
      throwOnAnyThreadPolicyViolation {
        AppWatcher.manualInstall(application)
        LeakCanary.config = LeakCanary.config.copy(dumpHeap = LeakCanary.config.dumpHeap)
      }
    }
  }

  @Test fun no_thread_policy_violations_on_config_update_then_install() {
    runOnMainSyncRethrowing {
      throwOnAnyThreadPolicyViolation {
        LeakCanary.config = LeakCanary.config.copy(dumpHeap = LeakCanary.config.dumpHeap)
        AppWatcher.manualInstall(application)
      }
    }
  }

  private fun throwOnAnyThreadPolicyViolation(block: () -> Unit) {
    val previousThreadPolicy = StrictMode.getThreadPolicy()
    try {
      StrictMode.setThreadPolicy(
        ThreadPolicy.Builder()
          .detectAll()
          .penaltyDeath()
          .build()
      )
      block()
    } finally {
      StrictMode.setThreadPolicy(previousThreadPolicy)
    }
  }

  private fun runOnMainSyncRethrowing(block: () -> Unit) {
    var mainThreadThrowable: Throwable? = null
    val instrumentation = InstrumentationRegistry.getInstrumentation()
    instrumentation.runOnMainSync {
      try {
        block()
      } catch (throwable: Throwable) {
        mainThreadThrowable = throwable
      }
    }
    mainThreadThrowable?.let { cause ->
      throw cause
    }
  }
}