main

square/leakcanary

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

HprofDeobfuscatorTest.kt

TLDR

The HprofDeobfuscatorTest.kt file contains test cases for the HprofDeobfuscator class in the shark package. It tests various scenarios where the HprofDeobfuscator is used to deobfuscate class and field names in a Hprof format file.

Classes

HprofDeobfuscatorTest

This class contains test methods to validate the functionality of the HprofDeobfuscator class. It creates mock Hprof files with obfuscated class and field names and asserts that the deobfuscated files contain the expected class and field names.

Methods

setUp()

This method is executed before each test method and is responsible for setting up the necessary resources for the test cases. It creates a temporary file named "temp.hprof" using the TemporaryFolder rule.

deobfuscateHprofClassName()

This test method validates the deobfuscation of class names in a Hprof file. It creates a mock Hprof file with a class "a" and uses the HprofDeobfuscator to deobfuscate the class name "Foo" mapped to "a". It asserts that the deobfuscated file contains the expected deobfuscated class name.

deobfuscateHprofStaticFieldName()

This test method validates the deobfuscation of static field names in a Hprof file. It creates a mock Hprof file with a class "a" containing a static field "b", and uses the HprofDeobfuscator to deobfuscate the class name "Foo" mapped to "a". It asserts that the deobfuscated file contains the expected deobfuscated static field name.

deobfuscateHprofMemberFieldName()

This test method validates the deobfuscation of member field names in a Hprof file. It creates a mock Hprof file with a class "a" containing an instance field "b", and uses the HprofDeobfuscator to deobfuscate the class name "Foo" mapped to "a". It asserts that the deobfuscated file contains the expected deobfuscated member field name.

deobfuscateHprofClassNameUsedAsFieldName()

This test method validates the deobfuscation of a class name used as a field name in a Hprof file. It creates a mock Hprof file with a class "a" containing an instance field with the same name "a", and uses the HprofDeobfuscator to deobfuscate the class name "Foo" mapped to "a". It asserts that the deobfuscated file contains the expected deobfuscated field name.

deobfuscateHprofClassNameUsedAsStaticFieldName()

This test method validates the deobfuscation of a class name used as a static field name in a Hprof file. It creates a mock Hprof file with a class containing a static field with the same name as the class, and uses the HprofDeobfuscator to deobfuscate the class name "Foo" mapped to "a". It asserts that the deobfuscated file contains the expected deobfuscated static field name.

deobfuscateHprofTwoFieldsWithSameName()

This test method validates the deobfuscation of two fields with the same name in different classes in a Hprof file. It creates a mock Hprof file with classes "Foo" and "Bar" containing fields with the same name "c" mapped to different obfuscated names, and uses the HprofDeobfuscator to deobfuscate the class names and field names. It asserts that the deobfuscated file contains the expected deobfuscated field names for both classes.

File.readHprof(block: (HeapGraph) -> Unit)

This private extension method reads a Hprof file using the openHeapGraph method of HprofHeapGraph and executes a block of code with the resulting HeapGraph object.

package shark

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.ValueHolder.IntHolder
import java.io.File
import shark.HprofHeapGraph.Companion.openHeapGraph

class HprofDeobfuscatorTest {

  @get:Rule
  var testFolder = TemporaryFolder()
  private lateinit var hprofFile: File

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

  @Test
  fun deobfuscateHprofClassName() {
    val proguardMapping = ProguardMapping().create {
      clazz("Foo" to "a")
    }

    hprofFile.dump {
      "a" clazz {}
    }

    val deobfuscator = HprofDeobfuscator()
    val deobfuscatedFile = deobfuscator.deobfuscate(proguardMapping, hprofFile)

    deobfuscatedFile.readHprof { graph ->
      val fooClass = graph.findClassByName("Foo")
      assertThat(fooClass).isNotNull
    }
  }

  @Test
  fun deobfuscateHprofStaticFieldName() {
    val proguardMapping = ProguardMapping().create {
      clazz("Foo" to "a") {
        field { "staticField" to "b" }
      }
    }

    hprofFile.dump {
      "a" clazz {
        staticField["b"] = IntHolder(42)
      }
    }

    val deobfuscator = HprofDeobfuscator()
    val deobfuscatedFile = deobfuscator.deobfuscate(proguardMapping, hprofFile)

    deobfuscatedFile.readHprof { graph ->
      val fooClass = graph.findClassByName("Foo")!!

      assertThat(
        fooClass.readStaticFields()
          .map { it.name }
          .toList()
      ).contains("staticField")
    }
  }

  @Test
  fun deobfuscateHprofMemberFieldName() {
    val proguardMapping = ProguardMapping().create {
      clazz("Foo" to "a") {
        field { "instanceField" to "b" }
      }
    }

    hprofFile.dump {
      val classId = clazz(
        className = "a",
        fields = listOf("b" to IntHolder::class)
      )
      instance(classId, listOf(IntHolder(0)))
    }

    val deobfuscator = HprofDeobfuscator()
    val deobfuscatedFile = deobfuscator.deobfuscate(proguardMapping, hprofFile)

    deobfuscatedFile.readHprof { graph ->
      val instance = graph.instances.find { heapInstance ->
        heapInstance.instanceClassName == "Foo"
      }!!

      assertThat(
        instance.readFields()
          .map { it.name }
          .toList()
      ).contains("instanceField")
    }
  }

  @Test
  fun deobfuscateHprofClassNameUsedAsFieldName() {
    val proguardMapping = ProguardMapping().create {
      clazz("Foo" to "a") {
        field { "instanceField" to "a" }
      }
    }

    hprofFile.dump {
      val classNameRecord = stringRecord("a")

      val classId = clazz(
        classNameRecord = classNameRecord,
        fields = listOf(classNameRecord.id to IntHolder::class)
      )
      instance(classId, listOf(IntHolder(0)))
    }

    val deobfuscator = HprofDeobfuscator()
    val deobfuscatedFile = deobfuscator.deobfuscate(proguardMapping, hprofFile)

    deobfuscatedFile.readHprof { graph ->
      val instance = graph.instances.find { heapInstance ->
        heapInstance.instanceClassName == "Foo"
      }!!

      assertThat(
        instance.readFields()
          .map { it.name }
          .toList()
      ).contains("instanceField")
    }
  }

  @Test
  fun deobfuscateHprofClassNameUsedAsStaticFieldName() {
    val proguardMapping = ProguardMapping().create {
      clazz("Foo" to "a") {
        field { "staticField" to "a" }
      }
    }

    hprofFile.dump {
      val classNameRecord = stringRecord("a")

      clazz(
        classNameRecord = classNameRecord,
        staticFields = listOf(classNameRecord.id to IntHolder(42))
      )
    }

    val deobfuscator = HprofDeobfuscator()
    val deobfuscatedFile = deobfuscator.deobfuscate(proguardMapping, hprofFile)

    deobfuscatedFile.readHprof { graph ->
      val fooClass = graph.findClassByName("Foo")!!

      assertThat(
        fooClass.readStaticFields()
          .map { it.name }
          .toList()
      ).contains("staticField")
    }
  }

  @Test
  fun deobfuscateHprofTwoFieldsWithSameName() {
    val proguardMapping = ProguardMapping().create {
      clazz("Foo" to "a") {
        field { "instanceField1" to "c" }
      }
      clazz("Bar" to "b") {
        field { "instanceField2" to "c" }
      }
    }

    hprofFile.dump {
      val fooClassNameRecord = stringRecord("a")
      val barClassNameRecord = stringRecord("b")
      val fieldNameRecord = stringRecord("c")

      val fooClassId = clazz(
        classNameRecord = fooClassNameRecord,
        fields = listOf(fieldNameRecord.id to IntHolder::class)
      )
      instance(fooClassId, listOf(IntHolder(0)))

      val barClassId = clazz(
        classNameRecord = barClassNameRecord,
        fields = listOf(fieldNameRecord.id to IntHolder::class)
      )
      instance(barClassId, listOf(IntHolder(0)))
    }

    val deobfuscator = HprofDeobfuscator()
    val deobfuscatedFile = deobfuscator.deobfuscate(proguardMapping, hprofFile)

    deobfuscatedFile.readHprof { graph ->
      val fooInstance = graph.instances.find { heapInstance ->
        heapInstance.instanceClassName == "Foo"
      }!!

      assertThat(
        fooInstance.readFields()
          .map { it.name }
          .toList()
      ).contains("instanceField1")

      val barInstance = graph.instances.find { heapInstance ->
        heapInstance.instanceClassName == "Bar"
      }!!

      assertThat(
        barInstance.readFields()
          .map { it.name }
          .toList()
      ).contains("instanceField2")
    }
  }

  private fun File.readHprof(block: (HeapGraph) -> Unit) {
    openHeapGraph().use { block(it) }
  }
}