main

square/leakcanary

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

LeakTraceWrapperTest.kt

TLDR

The LeakTraceWrapperTest.kt file in the leakcanary.internal.activity.screen package contains a series of test methods for the LeakTraceWrapper class.

Methods

short string stays identical

This method tests that a short string remains unchanged when wrapped.

string at width stays identical

This method tests that a string at the specified width remains unchanged when wrapped.

string at width no newline stays identical

This method tests that a string without a newline at the specified width remains unchanged when wrapped.

string wrapped without newline stays has no trailing newline

This method tests that a string wrapped without a newline has no trailing newline when wrapped.

wrap line at space removing space

This method tests that a line can be wrapped at a space, with the space being removed.

wrap line at space keeps prefix

This method tests that a line can be wrapped at a space while keeping the prefix.

wrap line at space keeps non-breaking space

This method tests that a line can be wrapped at a non-breaking space.

wrap line at period keeping period

This method tests that a line can be wrapped at a period while keeping the period.

two periods wraps at last period

This method tests that two periods can be wrapped at the last period.

no space or period is wrapped at max width

This method tests that a line without a space or a period can be wrapped at the maximum width.

two consecutive periods wraps at last period

This method tests that two consecutive periods can be wrapped at the last period.

period and space wraps at last

This method tests that a line with a period and a space can be wrapped at the space.

space and period wraps at last

This method tests that a line with a space and a period can be wrapped at the period.

period and separated space wraps at last

This method tests that a line with a period and a separated space can be wrapped at the space.

several spaces are all removed

This method tests that several spaces are all removed when wrapping a line.

several periods all keeping period

This method tests that several periods are wrapped while keeping the periods.

prefix applied to all lines

This method tests that a prefix is applied to all wrapped lines.

underline is positioned under a word on a line that will not be wrapped

This method tests that an underline is correctly positioned under a word on a line that will not be wrapped.

underline is positioned under a word on last line

This method tests that an underline is correctly positioned under a word on the last line.

underline within multiline string

This method tests that an underline is correctly positioned within a multiline string when wrapping.

a real leak trace is correctly wrapped

This method tests that a real leak trace is correctly wrapped.

package leakcanary.internal.activity.screen

import org.assertj.core.api.Assertions.assertThat
import org.junit.Test

class LeakTraceWrapperTest {

  @Test fun `short string stays identical`() {
    val string = "12\n"

    val wrapped = LeakTraceWrapper.wrap(string, 4)

    assertThat(wrapped).isEqualTo(string)
  }

  @Test fun `string at width stays identical`() {
    val string = "1234\n"

    val wrapped = LeakTraceWrapper.wrap(string, 4)

    assertThat(wrapped).isEqualTo(string)
  }

  @Test fun `string at width no newline stays identical`() {
    val string = "1234"

    val wrapped = LeakTraceWrapper.wrap(string, 4)

    assertThat(wrapped).isEqualTo(string)
  }

  @Test fun `string wrapped without newline stays has no trailing newline`() {
    val string = "12 34"

    val wrapped = LeakTraceWrapper.wrap(string, 4)

    assertThat(wrapped).isEqualTo("12\n34")
  }

  @Test fun `wrap line at space removing space`() {
    val string = "12 34\n"

    val wrapped = LeakTraceWrapper.wrap(string, 4)

    assertThat(wrapped).isEqualTo("12\n34\n")
  }

  @Test fun `wrap line at space keeps prefix`() {
    val prefix = "│   "
    val string = "${prefix}12 34\n"

    val wrapped = LeakTraceWrapper.wrap(string, prefix.length + 4)

    assertThat(wrapped).isEqualTo("${prefix}12\n${prefix}34\n")
  }

  @Test fun `wrap line at space keeps non breaking space`() {
    val string = "\u200B 12 34\n"
    val wrapped = LeakTraceWrapper.wrap(string, 5)

    assertThat(wrapped).isEqualTo("\u200B 12\n\u200B 34\n")
  }

  @Test fun `wrap line at period keeping period`() {
    val string = "12.34\n"

    val wrapped = LeakTraceWrapper.wrap(string, 4)

    assertThat(wrapped).isEqualTo("12.\n34\n")
  }

  @Test fun `two periods wraps at last period`() {
    val string = "1.2.34\n"

    val wrapped = LeakTraceWrapper.wrap(string, 5)

    assertThat(wrapped).isEqualTo("1.2.\n34\n")
  }

  @Test fun `no space or period is wrapped at max width`() {
    val string = "1234\n"

    val wrapped = LeakTraceWrapper.wrap(string, 2)

    assertThat(wrapped).isEqualTo("12\n34\n")
  }

  @Test fun `two consecutive periods wraps at last period`() {
    val string = "12..34\n"

    val wrapped = LeakTraceWrapper.wrap(string, 5)

    assertThat(wrapped).isEqualTo("12..\n34\n")
  }

  @Test fun `period and space wraps at last`() {
    val string = "12. 34\n"

    val wrapped = LeakTraceWrapper.wrap(string, 5)

    assertThat(wrapped).isEqualTo("12.\n34\n")
  }

  @Test fun `space and period wraps at last`() {
    val string = "12 .34\n"

    val wrapped = LeakTraceWrapper.wrap(string, 5)

    assertThat(wrapped).isEqualTo("12 .\n34\n")
  }

  @Test fun `period and separated space wraps at last`() {
    val string = "1.2 34\n"

    val wrapped = LeakTraceWrapper.wrap(string, 5)

    assertThat(wrapped).isEqualTo("1.2\n34\n")
  }

  @Test fun `several spaces are all removed`() {
    val string = "12  34\n"

    val wrapped = LeakTraceWrapper.wrap(string, 5)

    assertThat(wrapped).isEqualTo("12\n34\n")
  }

  @Test fun `several periods all keeping period`() {
    val string = "12...34\n"
    val wrapped = LeakTraceWrapper.wrap(string, 4)
    assertThat(wrapped).isEqualTo("12..\n.34\n")
  }

  @Test fun `prefix applied to all lines`() {
    val prefix = "│  "
    val part1 = "A word"
    val part2 = "and a"
    val part3 = "pk.g"
    val string = "\n${prefix}$part1 $part2 $part3"
    val wrappedString = LeakTraceWrapper.wrap(string, prefix.length + part1.length + 1)

    assertThat(wrappedString).isEqualTo(
      """
${prefix}$part1
${prefix}$part2
${prefix}$part3"""
    )
  }

  @Test fun `underline is positioned under a word on a line that will not be wrapped`() {
    val string = """
│  A word and a pk.g
│         ~~~      
        """
    val wrappedString = LeakTraceWrapper.wrap(string, 10)

    assertThat(wrappedString).isEqualTo(
      """
│  A word
│  and a
│  ~~~
│  pk.g
"""
    )
  }

  @Test fun `underline is positioned under a word on last line`() {
    val string = """
│  A word and a pk.g
│                  ~           
        """
    val wrappedString = LeakTraceWrapper.wrap(string, 10)

    assertThat(wrappedString).isEqualTo(
      """
│  A word
│  and a
│  pk.g
│     ~
"""
    )
  }

  @Test fun `underline within multiline string`() {
    val string = """
├─ com.example.FooFooFooFooFooFooFoo instance
│    Leaking: UNKNOWN
│    ↓ FooFooFooFooFooFooFoo.barbarbarbarbarbarbarbar
│                            ~~~~~~~~~~~~~~~~~~~~~~~~
"""

    val wrappedString = LeakTraceWrapper.wrap(string, 30)

    assertThat(wrappedString).isEqualTo(
      """
├─ com.example.
│  FooFooFooFooFooFooFoo
│  instance
│    Leaking: UNKNOWN
│    ↓ FooFooFooFooFooFooFoo.
│    barbarbarbarbarbarbarbar
│    ~~~~~~~~~~~~~~~~~~~~~~~~
"""
    )
  }

  @Test fun `a real leak trace is correctly wrapped`() {
    val string = """
┬───
│ GC Root: System class
│
├─ leakcanary.internal.InternalAppWatcher class
│    Leaking: NO (ExampleApplication↓ is not leaking and a class is never leaking)
│    ↓ static InternalAppWatcher.application
├─ com.example.leakcanary.ExampleApplication instance
│    Leaking: NO (Application is a singleton)
│    ExampleApplication does not wrap an activity context
│    ↓ ExampleApplication.leakedViews
│                         ~~~~~~~~~~~
├─ java.util.ArrayList instance
│    Leaking: UNKNOWN
│    ↓ ArrayList.array
│                ~~~~~
├─ java.lang.Object[] array
│    Leaking: UNKNOWN
│    ↓ Object[].[0]
│               ~~~
├─ android.widget.TextView instance
│    Leaking: YES (View.mContext references a destroyed activity)
│    mContext instance of com.example.leakcanary.MainActivity with mDestroyed = true
│    View#mParent is set
│    View#mAttachInfo is null (view detached)
│    View.mWindowAttachCount = 1
│    ↓ TextView.mContext
╰→ com.example.leakcanary.MainActivity instance
​     Leaking: YES (ObjectWatcher was watching this because com.example.leakcanary.MainActivity received Activity#onDestroy() callback and Activity#mDestroyed is true)
​     key = b3dd6589-560d-48dc-9fbb-ab8300e5752b
​     watchDurationMillis = 5117
​     retainedDurationMillis = 110
"""

    val wrappedString = LeakTraceWrapper.wrap(string, 80)

    assertThat(wrappedString).isEqualTo(
      """
┬───
│ GC Root: System class
│
├─ leakcanary.internal.InternalAppWatcher class
│    Leaking: NO (ExampleApplication↓ is not leaking and a class is never
│    leaking)
│    ↓ static InternalAppWatcher.application
├─ com.example.leakcanary.ExampleApplication instance
│    Leaking: NO (Application is a singleton)
│    ExampleApplication does not wrap an activity context
│    ↓ ExampleApplication.leakedViews
│                         ~~~~~~~~~~~
├─ java.util.ArrayList instance
│    Leaking: UNKNOWN
│    ↓ ArrayList.array
│                ~~~~~
├─ java.lang.Object[] array
│    Leaking: UNKNOWN
│    ↓ Object[].[0]
│               ~~~
├─ android.widget.TextView instance
│    Leaking: YES (View.mContext references a destroyed activity)
│    mContext instance of com.example.leakcanary.MainActivity with mDestroyed =
│    true
│    View#mParent is set
│    View#mAttachInfo is null (view detached)
│    View.mWindowAttachCount = 1
│    ↓ TextView.mContext
╰→ com.example.leakcanary.MainActivity instance
​     Leaking: YES (ObjectWatcher was watching this because com.example.
​     leakcanary.MainActivity received Activity#onDestroy() callback and
​     Activity#mDestroyed is true)
​     key = b3dd6589-560d-48dc-9fbb-ab8300e5752b
​     watchDurationMillis = 5117
​     retainedDurationMillis = 110
"""
    )
  }
}