1 package leakcanary.internal.activity.screen 2 3 import org.assertj.core.api.Assertions.assertThat 4 import org.junit.Test 5 6 class LeakTraceWrapperTest { 7 short string stays identicalnull8 @Test fun `short string stays identical`() { 9 val string = "12\n" 10 11 val wrapped = LeakTraceWrapper.wrap(string, 4) 12 13 assertThat(wrapped).isEqualTo(string) 14 } 15 string at width stays identicalnull16 @Test fun `string at width stays identical`() { 17 val string = "1234\n" 18 19 val wrapped = LeakTraceWrapper.wrap(string, 4) 20 21 assertThat(wrapped).isEqualTo(string) 22 } 23 string at width no newline stays identicalnull24 @Test fun `string at width no newline stays identical`() { 25 val string = "1234" 26 27 val wrapped = LeakTraceWrapper.wrap(string, 4) 28 29 assertThat(wrapped).isEqualTo(string) 30 } 31 string wrapped without newline stays has no trailing newlinenull32 @Test fun `string wrapped without newline stays has no trailing newline`() { 33 val string = "12 34" 34 35 val wrapped = LeakTraceWrapper.wrap(string, 4) 36 37 assertThat(wrapped).isEqualTo("12\n34") 38 } 39 wrap line at space removing spacenull40 @Test fun `wrap line at space removing space`() { 41 val string = "12 34\n" 42 43 val wrapped = LeakTraceWrapper.wrap(string, 4) 44 45 assertThat(wrapped).isEqualTo("12\n34\n") 46 } 47 wrap line at space keeps prefixnull48 @Test fun `wrap line at space keeps prefix`() { 49 val prefix = "│ " 50 val string = "${prefix}12 34\n" 51 52 val wrapped = LeakTraceWrapper.wrap(string, prefix.length + 4) 53 54 assertThat(wrapped).isEqualTo("${prefix}12\n${prefix}34\n") 55 } 56 wrap line at space keeps non breaking spacenull57 @Test fun `wrap line at space keeps non breaking space`() { 58 val string = "\u200B 12 34\n" 59 val wrapped = LeakTraceWrapper.wrap(string, 5) 60 61 assertThat(wrapped).isEqualTo("\u200B 12\n\u200B 34\n") 62 } 63 wrap line at period keeping periodnull64 @Test fun `wrap line at period keeping period`() { 65 val string = "12.34\n" 66 67 val wrapped = LeakTraceWrapper.wrap(string, 4) 68 69 assertThat(wrapped).isEqualTo("12.\n34\n") 70 } 71 two periods wraps at last periodnull72 @Test fun `two periods wraps at last period`() { 73 val string = "1.2.34\n" 74 75 val wrapped = LeakTraceWrapper.wrap(string, 5) 76 77 assertThat(wrapped).isEqualTo("1.2.\n34\n") 78 } 79 no space or period is wrapped at max widthnull80 @Test fun `no space or period is wrapped at max width`() { 81 val string = "1234\n" 82 83 val wrapped = LeakTraceWrapper.wrap(string, 2) 84 85 assertThat(wrapped).isEqualTo("12\n34\n") 86 } 87 two consecutive periods wraps at last periodnull88 @Test fun `two consecutive periods wraps at last period`() { 89 val string = "12..34\n" 90 91 val wrapped = LeakTraceWrapper.wrap(string, 5) 92 93 assertThat(wrapped).isEqualTo("12..\n34\n") 94 } 95 period and space wraps at lastnull96 @Test fun `period and space wraps at last`() { 97 val string = "12. 34\n" 98 99 val wrapped = LeakTraceWrapper.wrap(string, 5) 100 101 assertThat(wrapped).isEqualTo("12.\n34\n") 102 } 103 space and period wraps at lastnull104 @Test fun `space and period wraps at last`() { 105 val string = "12 .34\n" 106 107 val wrapped = LeakTraceWrapper.wrap(string, 5) 108 109 assertThat(wrapped).isEqualTo("12 .\n34\n") 110 } 111 period and separated space wraps at lastnull112 @Test fun `period and separated space wraps at last`() { 113 val string = "1.2 34\n" 114 115 val wrapped = LeakTraceWrapper.wrap(string, 5) 116 117 assertThat(wrapped).isEqualTo("1.2\n34\n") 118 } 119 several spaces are all removednull120 @Test fun `several spaces are all removed`() { 121 val string = "12 34\n" 122 123 val wrapped = LeakTraceWrapper.wrap(string, 5) 124 125 assertThat(wrapped).isEqualTo("12\n34\n") 126 } 127 several periods all keeping periodnull128 @Test fun `several periods all keeping period`() { 129 val string = "12...34\n" 130 val wrapped = LeakTraceWrapper.wrap(string, 4) 131 assertThat(wrapped).isEqualTo("12..\n.34\n") 132 } 133 prefix applied to all linesnull134 @Test fun `prefix applied to all lines`() { 135 val prefix = "│ " 136 val part1 = "A word" 137 val part2 = "and a" 138 val part3 = "pk.g" 139 val string = "\n${prefix}$part1 $part2 $part3" 140 val wrappedString = LeakTraceWrapper.wrap(string, prefix.length + part1.length + 1) 141 142 assertThat(wrappedString).isEqualTo( 143 """ 144 ${prefix}$part1 145 ${prefix}$part2 146 ${prefix}$part3""" 147 ) 148 } 149 underline is positioned under a word on a line that will not be wrappednull150 @Test fun `underline is positioned under a word on a line that will not be wrapped`() { 151 val string = """ 152 │ A word and a pk.g 153 │ ~~~ 154 """ 155 val wrappedString = LeakTraceWrapper.wrap(string, 10) 156 157 assertThat(wrappedString).isEqualTo( 158 """ 159 │ A word 160 │ and a 161 │ ~~~ 162 │ pk.g 163 """ 164 ) 165 } 166 underline is positioned under a word on last linenull167 @Test fun `underline is positioned under a word on last line`() { 168 val string = """ 169 │ A word and a pk.g 170 │ ~ 171 """ 172 val wrappedString = LeakTraceWrapper.wrap(string, 10) 173 174 assertThat(wrappedString).isEqualTo( 175 """ 176 │ A word 177 │ and a 178 │ pk.g 179 │ ~ 180 """ 181 ) 182 } 183 underline within multiline stringnull184 @Test fun `underline within multiline string`() { 185 val string = """ 186 ├─ com.example.FooFooFooFooFooFooFoo instance 187 │ Leaking: UNKNOWN 188 │ ↓ FooFooFooFooFooFooFoo.barbarbarbarbarbarbarbar 189 │ ~~~~~~~~~~~~~~~~~~~~~~~~ 190 """ 191 192 val wrappedString = LeakTraceWrapper.wrap(string, 30) 193 194 assertThat(wrappedString).isEqualTo( 195 """ 196 ├─ com.example. 197 │ FooFooFooFooFooFooFoo 198 │ instance 199 │ Leaking: UNKNOWN 200 │ ↓ FooFooFooFooFooFooFoo. 201 │ barbarbarbarbarbarbarbar 202 │ ~~~~~~~~~~~~~~~~~~~~~~~~ 203 """ 204 ) 205 } 206 a real leak trace is correctly wrappednull207 @Test fun `a real leak trace is correctly wrapped`() { 208 val string = """ 209 ┬─── 210 │ GC Root: System class 211 │ 212 ├─ leakcanary.internal.InternalAppWatcher class 213 │ Leaking: NO (ExampleApplication↓ is not leaking and a class is never leaking) 214 │ ↓ static InternalAppWatcher.application 215 ├─ com.example.leakcanary.ExampleApplication instance 216 │ Leaking: NO (Application is a singleton) 217 │ ExampleApplication does not wrap an activity context 218 │ ↓ ExampleApplication.leakedViews 219 │ ~~~~~~~~~~~ 220 ├─ java.util.ArrayList instance 221 │ Leaking: UNKNOWN 222 │ ↓ ArrayList.array 223 │ ~~~~~ 224 ├─ java.lang.Object[] array 225 │ Leaking: UNKNOWN 226 │ ↓ Object[].[0] 227 │ ~~~ 228 ├─ android.widget.TextView instance 229 │ Leaking: YES (View.mContext references a destroyed activity) 230 │ mContext instance of com.example.leakcanary.MainActivity with mDestroyed = true 231 │ View#mParent is set 232 │ View#mAttachInfo is null (view detached) 233 │ View.mWindowAttachCount = 1 234 │ ↓ TextView.mContext 235 ╰→ com.example.leakcanary.MainActivity instance 236 Leaking: YES (ObjectWatcher was watching this because com.example.leakcanary.MainActivity received Activity#onDestroy() callback and Activity#mDestroyed is true) 237 key = b3dd6589-560d-48dc-9fbb-ab8300e5752b 238 watchDurationMillis = 5117 239 retainedDurationMillis = 110 240 """ 241 242 val wrappedString = LeakTraceWrapper.wrap(string, 80) 243 244 assertThat(wrappedString).isEqualTo( 245 """ 246 ┬─── 247 │ GC Root: System class 248 │ 249 ├─ leakcanary.internal.InternalAppWatcher class 250 │ Leaking: NO (ExampleApplication↓ is not leaking and a class is never 251 │ leaking) 252 │ ↓ static InternalAppWatcher.application 253 ├─ com.example.leakcanary.ExampleApplication instance 254 │ Leaking: NO (Application is a singleton) 255 │ ExampleApplication does not wrap an activity context 256 │ ↓ ExampleApplication.leakedViews 257 │ ~~~~~~~~~~~ 258 ├─ java.util.ArrayList instance 259 │ Leaking: UNKNOWN 260 │ ↓ ArrayList.array 261 │ ~~~~~ 262 ├─ java.lang.Object[] array 263 │ Leaking: UNKNOWN 264 │ ↓ Object[].[0] 265 │ ~~~ 266 ├─ android.widget.TextView instance 267 │ Leaking: YES (View.mContext references a destroyed activity) 268 │ mContext instance of com.example.leakcanary.MainActivity with mDestroyed = 269 │ true 270 │ View#mParent is set 271 │ View#mAttachInfo is null (view detached) 272 │ View.mWindowAttachCount = 1 273 │ ↓ TextView.mContext 274 ╰→ com.example.leakcanary.MainActivity instance 275 Leaking: YES (ObjectWatcher was watching this because com.example. 276 leakcanary.MainActivity received Activity#onDestroy() callback and 277 Activity#mDestroyed is true) 278 key = b3dd6589-560d-48dc-9fbb-ab8300e5752b 279 watchDurationMillis = 5117 280 retainedDurationMillis = 110 281 """ 282 ) 283 } 284 }