main

square/leakcanary

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

HprofHeapGraphTest.kt

TLDR

This file contains a test class HprofHeapGraphTest which tests finding classes and their properties in a heap graph.

Methods

setUp

This method is annotated with @Before and is called before each test case. It initializes the hprofFile variable by creating a new temporary file.

non system class can be found

This test case checks if a non-system class can be found in the heap graph. It uses the HprofWriter class to create a .hprof file with a class record and a corresponding string record. Then it opens the heap graph from the file and asserts that the class can be found by its name.

system class can be found

This test case checks if a system class can be found in the heap graph. Similar to the previous test case, it creates a .hprof file with a class record and a corresponding string record, but this time the class is marked as a root class. It then opens the heap graph from the file and asserts that the class can be found by its name.

system class prioritized over non system class

This test case checks if a system class is prioritized over a non-system class when both are present in the heap graph. It creates a .hprof file with multiple class records, including a root system class and non-root non-system classes. It then opens the heap graph from the file and asserts that the system class is prioritized by checking its object ID.

Classes

HprofHeapGraphTest

This class contains test cases for finding classes in a heap graph. It uses the HprofWriter class and its helper methods to create a .hprof file to test against. The test cases use TemporaryFolder rule to create a temporary directory and file for the .hprof file.

package shark

import java.io.File
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import shark.GcRoot.StickyClass
import shark.HprofHeapGraph.Companion.openHeapGraph
import shark.HprofRecord.HeapDumpRecord.GcRootRecord
import shark.HprofRecord.HeapDumpRecord.ObjectRecord.ClassDumpRecord
import shark.HprofRecord.LoadClassRecord
import shark.HprofRecord.StringRecord

class HprofHeapGraphTest {

  @get:Rule
  val testFolder = TemporaryFolder()

  private lateinit var hprofFile: File

  @Before
  fun setUp() {
    hprofFile = testFolder.newFile("temp.hprof")
  }

  @Test
  fun `non system class can be found`() {
    val className = "com.example.SimpleClass"

    HprofWriter.openWriterFor(hprofFile).use { writer ->
      val classNameRecord = StringRecord(1L, className)
      writer.write(classNameRecord)
      writer.writeClass(42, classNameRecord, rootClass = false)
    }

    hprofFile.openHeapGraph().use { graph ->
      assertThat(graph.findClassByName(className)).isNotNull
    }
  }

  @Test
  fun `system class can be found`() {
    val className = "com.example.SimpleClass"

    HprofWriter.openWriterFor(hprofFile).use { writer ->
      val classNameRecord = StringRecord(1L, className)
      writer.write(classNameRecord)
      writer.writeClass(42, classNameRecord, rootClass = true)
    }

    hprofFile.openHeapGraph().use { graph ->
      assertThat(graph.findClassByName(className)).isNotNull
    }
  }

  @Test
  fun `system class prioritized over non system class`() {
    val className = "com.example.SimpleClass"

    HprofWriter.openWriterFor(hprofFile).use { writer ->
      val classNameRecord = StringRecord(1L, className)
      writer.write(classNameRecord)
      writer.writeClass(24, classNameRecord, rootClass = false)
      writer.writeClass(25, classNameRecord, rootClass = false)
      writer.writeClass(42, classNameRecord, rootClass = true)
      writer.writeClass(43, classNameRecord, rootClass = false)
    }

    hprofFile.openHeapGraph().use { graph ->
      val heapClass = graph.findClassByName(className)!!
      assertThat(heapClass.objectId).isEqualTo(42)
    }
  }

  private fun HprofWriter.writeClass(
    classId: Long,
    classNameRecord: StringRecord,
    rootClass: Boolean
  ) {
    val loadClass = LoadClassRecord(1, classId, 1, classNameRecord.id)
    val classDump = ClassDumpRecord(
      id = loadClass.id,
      stackTraceSerialNumber = 1,
      superclassId = 0,
      classLoaderId = 0,
      signersId = 0,
      protectionDomainId = 0,
      instanceSize = 0,
      staticFields = emptyList(),
      fields = emptyList()
    )
    write(loadClass)
    if (rootClass) {
      write(GcRootRecord(gcRoot = StickyClass(classId)))
    }
    write(classDump)
  }
}