LeakStatusTest.kt
TLDR
The LeakStatusTest
class is a test file that contains various test methods for checking the leak status of objects in a heap dump.
Methods
setUp()
This method is a setup method annotated with @Before
. It initializes the hprofFile
field with a temporary file.
gcRootClassNotLeaking()
This method is a test method annotated with @Test
. It checks if a specific object in the heap dump is not leaking.
leakingInstanceLeaking()
This method is a test method annotated with @Test
. It checks if a specific object in the heap dump is leaking.
unreachableInstanceLeaking()
This method is a test method annotated with @Test
. It checks if a specific unreachable object in the heap dump is leaking.
defaultsToUnknown()
This method is a test method annotated with @Test
. It checks if a specific object in the heap dump has an unknown leaking status.
inspectorNotLeaking()
This method is a test method annotated with @Test
. It checks if a specific object in the heap dump is not leaking according to a custom inspector.
inspectorLeaking()
This method is a test method annotated with @Test
. It checks if a specific object in the heap dump is leaking according to a custom inspector.
leakingWinsUnknown()
This method is a test method annotated with @Test
. It checks if a specific object in the heap dump is leaking when there is a conflict with an unknown leaking status.
notLeakingWhenNextIsNotLeaking()
This method is a test method annotated with @Test
. It checks if a specific object in the heap dump is not leaking when the next object is also not leaking.
leakingWhenPreviousIsLeaking()
This method is a test method annotated with @Test
. It checks if a specific object in the heap dump is leaking when the previous object is leaking.
middleUnknown()
This method is a test method annotated with @Test
. It checks if a specific object in the heap dump has an unknown leaking status when there is a conflicting leaking status.
gcRootClassNotLeakingConflictingWithInspector()
This method is a test method annotated with @Test
. It checks if a specific object in the heap dump is not leaking when there is a conflict with a custom inspector.
gcRootClassNotLeakingAgreesWithInspector()
This method is a test method annotated with @Test
. It checks if a specific object in the heap dump is not leaking when it agrees with a custom inspector.
leakingInstanceLeakingConflictingWithInspector()
This method is a test method annotated with @Test
. It checks if a specific object in the heap dump is leaking when there is a conflict with a custom inspector.
leakingInstanceLeakingAgreesWithInspector()
This method is a test method annotated with @Test
. It checks if a specific object in the heap dump is leaking when it agrees with a custom inspector.
conflictNotLeakingWins()
This method is a test method annotated with @Test
. It checks if a specific object in the heap dump is not leaking when there is a conflict between two custom inspectors.
twoInspectorsAgreeNotLeaking()
This method is a test method annotated with @Test
. It checks if a specific object in the heap dump is not leaking when two custom inspectors agree.
twoInspectorsAgreeLeaking()
This method is a test method annotated with @Test
. It checks if a specific object in the heap dump is leaking when two custom inspectors agree.
notLeakingWhenFurtherDownIsNotLeaking()
This method is a test method annotated with @Test
. It checks if a specific object in the heap dump is not leaking when the further down object is not leaking.
leakingWhenFurtherUpIsLeaking()
This method is a test method annotated with @Test
. It checks if a specific object in the heap dump is leaking when the further up object is leaking.
leakCausesAreLastNotLeakingAndUnknown()
This method is a test method annotated with @Test
. It checks if the leak causes for a specific object in the heap dump are the last not leaking and unknown.
sameLeakTraceSameSignature()
This method is a test method annotated with @Test
. It checks if two heap dumps with the same leak trace have the same signature.
differentLeakTraceDifferentSignature()
This method is a test method annotated with @Test
. It checks if two heap dumps with different leak traces have different signatures.
sameCausesSameSignature()
This method is a test method annotated with @Test
. It checks if two heap dumps with the same causes have the same signature.
sameCausesSameApplicationLeak()
This method is a test method annotated with @Test
. It checks if a heap dump with the same causes produces the same application leak.
notLeakingInstance(className: String) -> ObjectInspector
This method returns an ObjectInspector
that checks if an object is not leaking based on the given class name.
leakingInstance(className: String) -> ObjectInspector
This method returns an ObjectInspector
that checks if an object is leaking based on the given class name.
notLeakingClass(className: String) -> ObjectInspector
This method returns an ObjectInspector
that checks if a class is not leaking based on the given class name.
leakingClass(className: String) -> ObjectInspector
This method returns an ObjectInspector
that checks if a class is leaking based on the given class name.
computeSignature(notLeaking: String, leaking: String) -> String
This method computes the signature for a given pair of not leaking and leaking class names based on the object inspectors.
Classes
LeakStatusTest
This class contains various test methods for checking the leak status of objects in a heap dump.
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.HeapObject.HeapClass
import shark.HeapObject.HeapInstance
import shark.LeakTraceObject.LeakingStatus.LEAKING
import shark.LeakTraceObject.LeakingStatus.NOT_LEAKING
import shark.LeakTraceObject.LeakingStatus.UNKNOWN
import java.io.File
class LeakStatusTest {
@get:Rule
var testFolder = TemporaryFolder()
private lateinit var hprofFile: File
@Before
fun setUp() {
hprofFile = testFolder.newFile("temp.hprof")
}
@Test fun gcRootClassNotLeaking() {
hprofFile.writeSinglePathToInstance()
val analysis = hprofFile.checkForLeaks<HeapAnalysisSuccess>(
objectInspectors = listOf(ObjectInspectors.CLASS)
)
val leak = analysis.applicationLeaks[0]
assertThat(leak.leakTraces.first().referencePath.first().originObject.leakingStatus).isEqualTo(
NOT_LEAKING
)
}
@Test fun leakingInstanceLeaking() {
hprofFile.writeSinglePathToInstance()
val analysis = hprofFile.checkForLeaks<HeapAnalysisSuccess>()
val leakTrace = analysis.applicationLeaks[0].leakTraces.first()
assertThat(leakTrace.leakingObject.leakingStatus).isEqualTo(LEAKING)
}
@Test fun unreachableInstanceLeaking() {
val heapDump = dump {
"SomeClass" watchedInstance {
}
}
val analysis = heapDump.checkForLeaks<HeapAnalysisSuccess>()
assertThat(analysis.unreachableObjects[0].leakingStatus).isEqualTo(LEAKING)
}
@Test fun defaultsToUnknown() {
hprofFile.dump {
"GcRoot" clazz {
staticField["staticField1"] = "Class1" instance {
field["field1"] = "Leaking" watchedInstance {}
}
}
}
val analysis = hprofFile.checkForLeaks<HeapAnalysisSuccess>()
val leakTrace = analysis.applicationLeaks[0].leakTraces.first()
assertThat(leakTrace.referencePath[1].originObject.leakingStatus).isEqualTo(UNKNOWN)
}
@Test fun inspectorNotLeaking() {
hprofFile.dump {
"GcRoot" clazz {
staticField["staticField1"] = "Class1" instance {
field["field1"] = "Leaking" watchedInstance {}
}
}
}
val analysis = hprofFile.checkForLeaks<HeapAnalysisSuccess>(
objectInspectors = listOf(notLeakingInstance("Class1"))
)
val leakTrace = analysis.applicationLeaks[0].leakTraces.first()
assertThat(leakTrace.referencePath[1].originObject.leakingStatus).isEqualTo(NOT_LEAKING)
}
@Test fun inspectorLeaking() {
hprofFile.dump {
"GcRoot" clazz {
staticField["staticField1"] = "Class1" instance {
field["field1"] = "Leaking" watchedInstance {}
}
}
}
val analysis =
hprofFile.checkForLeaks<HeapAnalysisSuccess>(
objectInspectors = listOf(leakingInstance("Class1"))
)
val leakTrace = analysis.applicationLeaks[0].leakTraces.first()
assertThat(leakTrace.referencePath[1].originObject.leakingStatusReason).isEqualTo(
"Class1 is leaking"
)
}
@Test fun leakingWinsUnknown() {
hprofFile.dump {
"GcRoot" clazz {
staticField["staticField1"] = "Class1" instance {
field["field1"] = "Leaking" watchedInstance {}
}
}
}
val analysis =
hprofFile.checkForLeaks<HeapAnalysisSuccess>(
objectInspectors = listOf(leakingInstance("Class1"))
)
val leakTrace = analysis.applicationLeaks[0].leakTraces.first()
assertThat(leakTrace.referencePath[1].originObject.leakingStatus).isEqualTo(LEAKING)
}
@Test fun notLeakingWhenNextIsNotLeaking() {
hprofFile.dump {
"GcRoot" clazz {
staticField["staticField1"] = "Class1" instance {
field["field1"] = "Class2" instance {
field["field2"] = "Class3" instance {
field["field3"] = "Leaking" watchedInstance {}
}
}
}
}
}
val analysis =
hprofFile.checkForLeaks<HeapAnalysisSuccess>(
objectInspectors = listOf(notLeakingInstance("Class3"))
)
val leakTrace = analysis.applicationLeaks[0].leakTraces.first()
assertThat(leakTrace.referencePath[1].originObject.leakingStatus).isEqualTo(NOT_LEAKING)
}
@Test fun leakingWhenPreviousIsLeaking() {
hprofFile.dump {
"GcRoot" clazz {
staticField["staticField1"] = "Class1" instance {
field["field1"] = "Class2" instance {
field["field2"] = "Class3" instance {
field["field3"] = "Leaking" watchedInstance {}
}
}
}
}
}
val analysis =
hprofFile.checkForLeaks<HeapAnalysisSuccess>(
objectInspectors = listOf(leakingInstance("Class1"))
)
val leakTrace = analysis.applicationLeaks[0].leakTraces.first()
assertThat(leakTrace.referencePath).hasSize(4)
assertThat(leakTrace.referencePath[2].originObject.leakingStatus).isEqualTo(LEAKING)
assertThat(leakTrace.referencePath[2].originObject.leakingStatusReason).isEqualTo(
"Class1↑ is leaking"
)
}
@Test fun middleUnknown() {
hprofFile.dump {
"GcRoot" clazz {
staticField["staticField1"] = "Class1" instance {
field["field1"] = "Class2" instance {
field["field2"] = "Class3" instance {
field["field3"] = "Leaking" watchedInstance {}
}
}
}
}
}
val analysis =
hprofFile.checkForLeaks<HeapAnalysisSuccess>(
objectInspectors = listOf(
notLeakingInstance("Class1"), leakingInstance("Class3")
)
)
val leakTrace = analysis.applicationLeaks[0].leakTraces.first()
assertThat(leakTrace.referencePath[2].originObject.leakingStatus).isEqualTo(UNKNOWN)
}
@Test fun gcRootClassNotLeakingConflictingWithInspector() {
hprofFile.writeSinglePathToInstance()
val analysis =
hprofFile.checkForLeaks<HeapAnalysisSuccess>(
objectInspectors = listOf(leakingClass("GcRoot"), ObjectInspectors.CLASS)
)
val leakTrace = analysis.applicationLeaks[0].leakTraces.first()
assertThat(leakTrace.referencePath.first().originObject.leakingStatus).isEqualTo(NOT_LEAKING)
assertThat(leakTrace.referencePath.first().originObject.leakingStatusReason).isEqualTo(
"a class is never leaking. Conflicts with GcRoot is leaking"
)
}
@Test fun gcRootClassNotLeakingAgreesWithInspector() {
hprofFile.writeSinglePathToInstance()
val analysis =
hprofFile.checkForLeaks<HeapAnalysisSuccess>(
objectInspectors = listOf(notLeakingClass("GcRoot"), ObjectInspectors.CLASS)
)
println(analysis)
val leakTrace = analysis.applicationLeaks[0].leakTraces.first()
assertThat(leakTrace.referencePath.first().originObject.leakingStatus).isEqualTo(NOT_LEAKING)
assertThat(leakTrace.referencePath.first().originObject.leakingStatusReason).isEqualTo(
"GcRoot is not leaking and a class is never leaking"
)
}
@Test fun leakingInstanceLeakingConflictingWithInspector() {
hprofFile.writeSinglePathToInstance()
val analysis =
hprofFile.checkForLeaks<HeapAnalysisSuccess>(
objectInspectors = listOf(notLeakingInstance("Leaking"))
)
val leakTrace = analysis.applicationLeaks[0].leakTraces.first()
assertThat(leakTrace.leakingObject.leakingStatus).isEqualTo(LEAKING)
assertThat(leakTrace.leakingObject.leakingStatusReason).isEqualTo(
"ObjectWatcher was watching this because its lifecycle has ended. " +
"Conflicts with Leaking is not leaking"
)
}
@Test fun leakingInstanceLeakingAgreesWithInspector() {
hprofFile.writeSinglePathToInstance()
val analysis =
hprofFile.checkForLeaks<HeapAnalysisSuccess>(
objectInspectors = listOf(leakingInstance("Leaking"))
)
val leakTrace = analysis.applicationLeaks[0].leakTraces.first()
assertThat(leakTrace.leakingObject.leakingStatus).isEqualTo(LEAKING)
assertThat(leakTrace.leakingObject.leakingStatusReason).isEqualTo(
"Leaking is leaking and ObjectWatcher was watching this because its lifecycle has ended"
)
}
@Test fun conflictNotLeakingWins() {
hprofFile.dump {
"GcRoot" clazz {
staticField["staticField1"] = "Class1" instance {
field["field1"] = "Leaking" watchedInstance {}
}
}
}
val analysis =
hprofFile.checkForLeaks<HeapAnalysisSuccess>(
objectInspectors = listOf(
notLeakingInstance("Class1"), leakingInstance("Class1")
)
)
val leakTrace = analysis.applicationLeaks[0].leakTraces.first()
println(leakTrace)
assertThat(leakTrace.referencePath[1].originObject.leakingStatus).isEqualTo(NOT_LEAKING)
assertThat(leakTrace.referencePath[1].originObject.leakingStatusReason).isEqualTo(
"Class1 is not leaking. Conflicts with Class1 is leaking"
)
}
@Test fun twoInspectorsAgreeNotLeaking() {
hprofFile.dump {
"GcRoot" clazz {
staticField["staticField1"] = "Class1" instance {
field["field1"] = "Leaking" watchedInstance {}
}
}
}
val analysis =
hprofFile.checkForLeaks<HeapAnalysisSuccess>(
objectInspectors = listOf(
notLeakingInstance("Class1"), notLeakingInstance("Class1")
)
)
val leakTrace = analysis.applicationLeaks[0].leakTraces.first()
assertThat(leakTrace.referencePath[1].originObject.leakingStatus).isEqualTo(NOT_LEAKING)
assertThat(leakTrace.referencePath[1].originObject.leakingStatusReason).isEqualTo(
"Class1 is not leaking"
)
}
@Test fun twoInspectorsAgreeLeaking() {
hprofFile.dump {
"GcRoot" clazz {
staticField["staticField1"] = "Class1" instance {
field["field1"] = "Leaking" watchedInstance {}
}
}
}
val analysis =
hprofFile.checkForLeaks<HeapAnalysisSuccess>(
objectInspectors = listOf(leakingInstance("Class1"), leakingInstance("Class1"))
)
val leakTrace = analysis.applicationLeaks[0].leakTraces.first()
assertThat(leakTrace.referencePath[1].originObject.leakingStatus).isEqualTo(LEAKING)
assertThat(leakTrace.referencePath[1].originObject.leakingStatusReason).isEqualTo(
"Class1 is leaking"
)
}
@Test fun notLeakingWhenFurtherDownIsNotLeaking() {
hprofFile.dump {
"GcRoot" clazz {
staticField["staticField1"] = "Class1" instance {
field["field1"] = "Class2" instance {
field["field2"] = "Class3" instance {
field["field3"] = "Leaking" watchedInstance {}
}
}
}
}
}
val analysis =
hprofFile.checkForLeaks<HeapAnalysisSuccess>(
objectInspectors = listOf(notLeakingInstance("Class3"))
)
val leakTrace = analysis.applicationLeaks[0].leakTraces.first()
assertThat(leakTrace.referencePath[1].originObject.className).isEqualTo("Class1")
assertThat(leakTrace.referencePath[1].originObject.leakingStatus).isEqualTo(NOT_LEAKING)
assertThat(leakTrace.referencePath[1].originObject.leakingStatusReason).isEqualTo(
"Class3↓ is not leaking"
)
}
@Test fun leakingWhenFurtherUpIsleaking() {
hprofFile.dump {
"GcRoot" clazz {
staticField["staticField1"] = "Class1" instance {
field["field1"] = "Class2" instance {
field["field2"] = "Class3" instance {
field["field3"] = "Leaking" watchedInstance {}
}
}
}
}
}
val analysis =
hprofFile.checkForLeaks<HeapAnalysisSuccess>(
objectInspectors = listOf(leakingInstance("Class1"))
)
val leakTrace = analysis.applicationLeaks[0].leakTraces.first()
assertThat(leakTrace.referencePath[3].originObject.className).isEqualTo("Class3")
assertThat(leakTrace.referencePath[3].originObject.leakingStatus).isEqualTo(LEAKING)
assertThat(leakTrace.referencePath[3].originObject.leakingStatusReason).isEqualTo(
"Class1↑ is leaking"
)
}
@Test fun leakCausesAreLastNotLeakingAndUnknown() {
hprofFile.dump {
"GcRoot" clazz {
staticField["staticField1"] = "Class1" instance {
field["field1"] = "Class2" instance {
field["field2"] = "Class3" instance {
field["field3"] = "Leaking" watchedInstance {}
}
}
}
}
}
val analysis =
hprofFile.checkForLeaks<HeapAnalysisSuccess>(
objectInspectors = listOf(
notLeakingInstance("Class1"), leakingInstance("Class3")
)
)
val leakTrace = analysis.applicationLeaks[0].leakTraces.first()
assertThat(leakTrace.referencePathElementIsSuspect(0)).isFalse()
assertThat(leakTrace.referencePathElementIsSuspect(1)).isTrue()
assertThat(leakTrace.referencePathElementIsSuspect(2)).isTrue()
}
@Test fun sameLeakTraceSameSignature() {
hprofFile.dump {
"GcRoot" clazz {
staticField["staticField1"] = "Class1" instance {
field["field1"] = "Class2" instance {
field["field2"] = "Class3" instance {
field["field3"] = "Leaking" watchedInstance {}
}
}
}
}
}
val hash1 = computeSignature(notLeaking = "Class1", leaking = "Class3")
hprofFile.dump {
"GcRoot" clazz {
staticField["staticField1"] = "Class1" instance {
field["field1"] = "Class2" instance {
field["field2"] = "Class3" instance {
field["field3"] = "Leaking" watchedInstance {}
}
}
}
}
}
val hash2 = computeSignature(notLeaking = "Class1", leaking = "Class3")
assertThat(hash1).isEqualTo(hash2)
}
@Test fun differentLeakTraceDifferentSignature() {
hprofFile.dump {
"GcRoot" clazz {
staticField["staticField1"] = "Class1" instance {
field["field1a"] = "Class2" instance {
field["field2"] = "Class3" instance {
field["field3"] = "Leaking" watchedInstance {}
}
}
}
}
}
val hash1 = computeSignature(notLeaking = "Class1", leaking = "Class3")
hprofFile.dump {
"GcRoot" clazz {
staticField["staticField1"] = "Class1" instance {
field["field1b"] = "Class2" instance {
field["field2"] = "Class3" instance {
field["field3"] = "Leaking" watchedInstance {}
}
}
}
}
}
val hash2 = computeSignature(notLeaking = "Class1", leaking = "Class3")
assertThat(hash1).isNotEqualTo(hash2)
}
@Test fun sameCausesSameSignature() {
hprofFile.dump {
"GcRoot" clazz {
staticField["staticField1"] = "Class1" instance {
field["field1"] = "Class2" instance {
field["field2"] = "Class3" instance {
field["field3a"] = "Leaking" watchedInstance {}
}
}
}
}
}
val hash1 = computeSignature(notLeaking = "Class1", leaking = "Class3")
hprofFile.dump {
"GcRoot" clazz {
staticField["staticField1"] = "Class1" instance {
field["field1"] = "Class2" instance {
field["field2"] = "Class3" instance {
field["field3b"] = "Leaking" watchedInstance {}
}
}
}
}
}
val hash2 = computeSignature(notLeaking = "Class1", leaking = "Class3")
assertThat(hash1).isEqualTo(hash2)
}
@Test fun sameCausesSameApplicationLeak() {
hprofFile.dump {
"GcRoot" clazz {
staticField["staticField1"] = "Class1" instance {
field["field1"] = "Class2" instance {
field["field2"] = "Class3" instance {
field["field3a"] = "Leaking" watchedInstance {}
field["field3b"] = "Leaking" watchedInstance {}
}
}
}
}
}
val analysis =
hprofFile.checkForLeaks<HeapAnalysisSuccess>(
objectInspectors = listOf(notLeakingInstance("Class1"), leakingInstance("Class3"))
)
assertThat(analysis.applicationLeaks).hasSize(1)
assertThat(analysis.applicationLeaks.first().leakTraces).hasSize(2)
}
private fun notLeakingInstance(className: String): ObjectInspector {
return ObjectInspector { reporter ->
val record = reporter.heapObject
if (record is HeapInstance && record.instanceClassName == className) {
reporter.notLeakingReasons += "$className is not leaking"
}
}
}
private fun leakingInstance(className: String): ObjectInspector {
return ObjectInspector { reporter ->
val record = reporter.heapObject
if (record is HeapInstance && record.instanceClassName == className) {
reporter.leakingReasons += "$className is leaking"
}
}
}
private fun notLeakingClass(className: String): ObjectInspector {
return ObjectInspector { reporter ->
val record = reporter.heapObject
if (record is HeapClass && record.name == className) {
reporter.notLeakingReasons += "$className is not leaking"
}
}
}
private fun leakingClass(className: String): ObjectInspector {
return ObjectInspector { reporter ->
val record = reporter.heapObject
if (record is HeapClass && record.name == className) {
reporter.leakingReasons += "$className is leaking"
}
}
}
private fun computeSignature(
notLeaking: String,
leaking: String
): String {
val analysis =
hprofFile.checkForLeaks<HeapAnalysisSuccess>(
objectInspectors = listOf(notLeakingInstance(notLeaking), leakingInstance(leaking))
)
require(analysis.applicationLeaks.size == 1) {
"Expecting 1 retained instance in ${analysis.applicationLeaks}"
}
val leak = analysis.applicationLeaks[0]
return leak.signature
}
}