main

square/leakcanary

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

LifecycleLeaksTest.kt

TLDR

The LifecycleLeaksTest file is a Kotlin test file in the LeakCanary library that contains test cases for detecting and asserting memory leaks in Android activities, fragments, and view models.

Methods

N/A

Classes

LifecycleLeaksTest

This class contains test cases for detecting and asserting memory leaks in Android activities, fragments, and view models. It extends the HasActivityTestRule<TestActivity> class and includes the following test methods:

  • activityLeakDetected(): Tests for activity leaks by launching and finishing an activity and asserting a memory leak of the TestActivity class.
  • activityViewModelLeakDetected(): Tests for activity view model leaks by launching an activity, installing a TestViewModel class as the view model, and asserting a memory leak of the TestViewModel class.
  • fragmentViewModelLeakDetected(): Tests for fragment view model leaks by launching an activity, adding a TestFragment fragment, installing a TestViewModel class as the view model, removing the fragment, and asserting a memory leak of the TestViewModel class.
  • fragmentLeakDetected(): Tests for fragment leaks by launching an activity, adding a Fragment fragment, removing the fragment, and asserting a memory leak of the Fragment class.
  • fragmentViewLeakDetected(): Tests for fragment view leaks by launching an activity, adding a TestFragment fragment, getting the fragment's view, destroying the view by replacing it with another fragment, and asserting a memory leak of the View class.

Class TestViewModel

This class represents a test view model.

Class TestFragment

This class represents a test fragment and overrides the onCreateView() method to return a new View object.

package leakcanary

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModel
import com.squareup.leakcanary.instrumentation.test.R
import leakcanary.TestUtils.assertLeak
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test

class LifecycleLeaksTest : HasActivityTestRule<TestActivity> {

  class TestViewModel : ViewModel()

  class TestFragment : Fragment() {
    override fun onCreateView(
      inflater: LayoutInflater,
      container: ViewGroup?,
      savedInstanceState: Bundle?
    ): View {
      return View(context)
    }
  }

  @get:Rule
  override val activityRule = activityTestRule<TestActivity>(
    initialTouchMode = false,
    launchActivity = false
  )

  @Before fun setUp() {
    AppWatcher.objectWatcher
      .clearWatchedObjects()
  }

  @After fun tearDown() {
    AppWatcher.objectWatcher
      .clearWatchedObjects()
  }

  @Test fun activityLeakDetected() {
    triggersOnActivityCreated {
      activityRule.launchActivity(null)
    }

    activity retained {
      triggersOnActivityDestroyed {
        activityRule.finishActivity()
      }
      assertLeak(TestActivity::class.java)
    }
  }

  @Test fun activityViewModelLeakDetected() {
    triggersOnActivityCreated {
      activityRule.launchActivity(null)
    }

    val viewModel = getOnMainSync {
      activity.installViewModel(TestViewModel::class)
    }

    viewModel retained {
      triggersOnActivityDestroyed {
        activityRule.finishActivity()
      }
      assertLeak(TestViewModel::class.java)
    }
  }

  @Test fun fragmentViewModelLeakDetected() {
    triggersOnActivityCreated {
      activityRule.launchActivity(null)
    }

    val viewModel = getOnMainSync {
      val fragment = Fragment()
      activity.addFragmentNow(fragment)
      val viewModel = fragment.installViewModel(TestViewModel::class)
      activity.removeFragmentNow(fragment)
      viewModel
    }

    viewModel retained {
      assertLeak(TestViewModel::class.java)
    }
  }

  @Test
  fun fragmentLeakDetected() {
    triggersOnActivityCreated {
      activityRule.launchActivity(null)
    }

    val fragment = getOnMainSync {
      val fragment = Fragment()
      activity.addFragmentNow(fragment)
      activity.removeFragmentNow(fragment)
      fragment
    }

    fragment retained {
      assertLeak(Fragment::class.java)
    }
  }

  @Test
  fun fragmentViewLeakDetected() {
    triggersOnActivityCreated {
      activityRule.launchActivity(null)
    }

    val fragment = triggersOnFragmentCreated {
      getOnMainSync {
        val fragment = TestFragment()
        activity.replaceWithBackStack(fragment, R.id.fragments)
        fragment
      }
    }

    val fragmentView = getOnMainSync {
      fragment.view!!
    }

    triggersOnFragmentViewDestroyed {
      runOnMainSync {
        // Add a new fragment again, which destroys the view of the previous fragment and puts
        // the first fragment in the backstack.
        activity.replaceWithBackStack(Fragment(), R.id.fragments)
      }
    }

    fragmentView retained {
      assertLeak(View::class.java)
    }
  }
}