1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.dump 18 19 import androidx.test.filters.SmallTest 20 import com.android.systemui.CoreStartable 21 import com.android.systemui.Dumpable 22 import com.android.systemui.ProtoDumpable 23 import com.android.systemui.SysuiTestCase 24 import com.android.systemui.log.LogBuffer 25 import com.android.systemui.log.table.TableLogBuffer 26 import com.android.systemui.util.mockito.any 27 import com.android.systemui.util.mockito.eq 28 import com.google.common.truth.Truth.assertThat 29 import org.junit.Before 30 import org.junit.Test 31 import org.mockito.Mock 32 import org.mockito.Mockito.anyInt 33 import org.mockito.Mockito.never 34 import org.mockito.Mockito.verify 35 import org.mockito.MockitoAnnotations 36 import java.io.FileDescriptor 37 import java.io.PrintWriter 38 import java.io.StringWriter 39 import javax.inject.Provider 40 41 @SmallTest 42 class DumpHandlerTest : SysuiTestCase() { 43 44 private lateinit var dumpHandler: DumpHandler 45 46 @Mock 47 private lateinit var logBufferEulogizer: LogBufferEulogizer 48 49 @Mock 50 private lateinit var pw: PrintWriter 51 @Mock 52 private lateinit var fd: FileDescriptor 53 54 @Mock 55 private lateinit var dumpable1: Dumpable 56 @Mock 57 private lateinit var dumpable2: Dumpable 58 @Mock 59 private lateinit var dumpable3: Dumpable 60 61 @Mock 62 private lateinit var protoDumpable1: ProtoDumpable 63 @Mock 64 private lateinit var protoDumpable2: ProtoDumpable 65 66 @Mock 67 private lateinit var buffer1: LogBuffer 68 @Mock 69 private lateinit var buffer2: LogBuffer 70 71 @Mock 72 private lateinit var table1: TableLogBuffer 73 @Mock 74 private lateinit var table2: TableLogBuffer 75 76 private val dumpManager = DumpManager() 77 78 @Before 79 fun setUp() { 80 MockitoAnnotations.initMocks(this) 81 82 val config = SystemUIConfigDumpable( 83 dumpManager, 84 mContext, 85 mutableMapOf(EmptyCoreStartable::class.java to Provider { EmptyCoreStartable() }), 86 ) 87 dumpHandler = DumpHandler(dumpManager, logBufferEulogizer, config) 88 } 89 90 @Test 91 fun testDumpablesCanBeDumpedSelectively() { 92 // GIVEN a variety of registered dumpables and buffers 93 dumpManager.registerCriticalDumpable("dumpable1", dumpable1) 94 dumpManager.registerCriticalDumpable("dumpable2", dumpable2) 95 dumpManager.registerCriticalDumpable("dumpable3", dumpable3) 96 dumpManager.registerBuffer("buffer1", buffer1) 97 dumpManager.registerBuffer("buffer2", buffer2) 98 dumpManager.registerTableLogBuffer("table1", table1) 99 dumpManager.registerTableLogBuffer("table2", table2) 100 101 // WHEN some of them are dumped explicitly 102 val args = arrayOf("dumpable1", "dumpable3", "buffer2", "table2") 103 dumpHandler.dump(fd, pw, args) 104 105 // THEN only the requested ones have their dump() method called 106 verify(dumpable1).dump(pw, args) 107 verify(dumpable2, never()).dump( 108 any(PrintWriter::class.java), 109 any(Array<String>::class.java)) 110 verify(dumpable3).dump(pw, args) 111 verify(buffer1, never()).dump(any(PrintWriter::class.java), anyInt()) 112 verify(buffer2).dump(pw, 0) 113 verify(table1, never()).dump(any(), any()) 114 verify(table2).dump(pw, args) 115 } 116 117 @Test 118 fun testDumpableMatchingIsBasedOnEndOfTag() { 119 // GIVEN a dumpable registered to the manager 120 dumpManager.registerCriticalDumpable("com.android.foo.bar.dumpable1", dumpable1) 121 122 // WHEN that module is dumped 123 val args = arrayOf("dumpable1") 124 dumpHandler.dump(fd, pw, args) 125 126 // THEN its dump() method is called 127 verify(dumpable1).dump(pw, args) 128 } 129 130 @Test 131 fun testCriticalDump() { 132 // GIVEN a variety of registered dumpables and buffers 133 dumpManager.registerCriticalDumpable("dumpable1", dumpable1) 134 dumpManager.registerCriticalDumpable("dumpable2", dumpable2) 135 dumpManager.registerNormalDumpable("dumpable3", dumpable3) 136 dumpManager.registerBuffer("buffer1", buffer1) 137 dumpManager.registerBuffer("buffer2", buffer2) 138 dumpManager.registerTableLogBuffer("table1", table1) 139 dumpManager.registerTableLogBuffer("table2", table2) 140 141 // WHEN a critical dump is requested 142 val args = arrayOf("--dump-priority", "CRITICAL") 143 dumpHandler.dump(fd, pw, args) 144 145 // THEN only critical modules are dumped (and no buffers) 146 verify(dumpable1).dump(pw, args) 147 verify(dumpable2).dump(pw, args) 148 verify(dumpable3, never()).dump( 149 any(PrintWriter::class.java), 150 any(Array<String>::class.java)) 151 verify(buffer1, never()).dump(any(PrintWriter::class.java), anyInt()) 152 verify(buffer2, never()).dump(any(PrintWriter::class.java), anyInt()) 153 verify(table1, never()).dump(any(), any()) 154 verify(table2, never()).dump(any(), any()) 155 } 156 157 @Test 158 fun testNormalDump() { 159 // GIVEN a variety of registered dumpables and buffers 160 dumpManager.registerCriticalDumpable("dumpable1", dumpable1) 161 dumpManager.registerCriticalDumpable("dumpable2", dumpable2) 162 dumpManager.registerNormalDumpable("dumpable3", dumpable3) 163 dumpManager.registerBuffer("buffer1", buffer1) 164 dumpManager.registerBuffer("buffer2", buffer2) 165 dumpManager.registerTableLogBuffer("table1", table1) 166 dumpManager.registerTableLogBuffer("table2", table2) 167 168 // WHEN a normal dump is requested 169 val args = arrayOf("--dump-priority", "NORMAL") 170 dumpHandler.dump(fd, pw, args) 171 172 // THEN the normal module and all buffers are dumped 173 verify(dumpable1, never()).dump( 174 any(PrintWriter::class.java), 175 any(Array<String>::class.java)) 176 verify(dumpable2, never()).dump( 177 any(PrintWriter::class.java), 178 any(Array<String>::class.java)) 179 verify(dumpable3).dump(pw, args) 180 verify(buffer1).dump(pw, 0) 181 verify(buffer2).dump(pw, 0) 182 verify(table1).dump(pw, args) 183 verify(table2).dump(pw, args) 184 } 185 186 @Test 187 fun testConfigDump() { 188 // GIVEN a StringPrintWriter 189 val stringWriter = StringWriter() 190 val spw = PrintWriter(stringWriter) 191 192 // When a config dump is requested 193 dumpHandler.dump(fd, spw, arrayOf("config")) 194 195 assertThat(stringWriter.toString()).contains(EmptyCoreStartable::class.java.simpleName) 196 } 197 198 @Test 199 fun testDumpBuffers() { 200 // GIVEN a variety of registered dumpables and buffers and tables 201 dumpManager.registerCriticalDumpable("dumpable1", dumpable1) 202 dumpManager.registerCriticalDumpable("dumpable2", dumpable2) 203 dumpManager.registerNormalDumpable("dumpable3", dumpable3) 204 dumpManager.registerBuffer("buffer1", buffer1) 205 dumpManager.registerBuffer("buffer2", buffer2) 206 dumpManager.registerTableLogBuffer("table1", table1) 207 dumpManager.registerTableLogBuffer("table2", table2) 208 209 // WHEN a buffer dump is requested 210 val args = arrayOf("buffers", "--tail", "1") 211 dumpHandler.dump(fd, pw, args) 212 213 // THEN all buffers are dumped (and no dumpables or tables) 214 verify(dumpable1, never()).dump(any(), any()) 215 verify(dumpable2, never()).dump(any(), any()) 216 verify(dumpable3, never()).dump(any(), any()) 217 verify(buffer1).dump(pw, tailLength = 1) 218 verify(buffer2).dump(pw, tailLength = 1) 219 verify(table1, never()).dump(any(), any()) 220 verify(table2, never()).dump(any(), any()) 221 } 222 223 @Test 224 fun testDumpDumpables() { 225 // GIVEN a variety of registered dumpables and buffers and tables 226 dumpManager.registerCriticalDumpable("dumpable1", dumpable1) 227 dumpManager.registerCriticalDumpable("dumpable2", dumpable2) 228 dumpManager.registerNormalDumpable("dumpable3", dumpable3) 229 dumpManager.registerBuffer("buffer1", buffer1) 230 dumpManager.registerBuffer("buffer2", buffer2) 231 dumpManager.registerTableLogBuffer("table1", table1) 232 dumpManager.registerTableLogBuffer("table2", table2) 233 234 // WHEN a dumpable dump is requested 235 val args = arrayOf("dumpables") 236 dumpHandler.dump(fd, pw, args) 237 238 // THEN all dumpables are dumped (both critical and normal) (and no dumpables) 239 verify(dumpable1).dump(pw, args) 240 verify(dumpable2).dump(pw, args) 241 verify(dumpable3).dump(pw, args) 242 verify(buffer1, never()).dump(any(), anyInt()) 243 verify(buffer2, never()).dump(any(), anyInt()) 244 verify(table1, never()).dump(any(), any()) 245 verify(table2, never()).dump(any(), any()) 246 } 247 248 @Test 249 fun testDumpTables() { 250 // GIVEN a variety of registered dumpables and buffers and tables 251 dumpManager.registerCriticalDumpable("dumpable1", dumpable1) 252 dumpManager.registerCriticalDumpable("dumpable2", dumpable2) 253 dumpManager.registerNormalDumpable("dumpable3", dumpable3) 254 dumpManager.registerBuffer("buffer1", buffer1) 255 dumpManager.registerBuffer("buffer2", buffer2) 256 dumpManager.registerTableLogBuffer("table1", table1) 257 dumpManager.registerTableLogBuffer("table2", table2) 258 259 // WHEN a dumpable dump is requested 260 val args = arrayOf("tables") 261 dumpHandler.dump(fd, pw, args) 262 263 // THEN all dumpables are dumped (both critical and normal) (and no dumpables) 264 verify(dumpable1, never()).dump(any(), any()) 265 verify(dumpable2, never()).dump(any(), any()) 266 verify(dumpable3, never()).dump(any(), any()) 267 verify(buffer1, never()).dump(any(), anyInt()) 268 verify(buffer2, never()).dump(any(), anyInt()) 269 verify(table1).dump(pw, args) 270 verify(table2).dump(pw, args) 271 } 272 273 @Test 274 fun testDumpAllProtoDumpables() { 275 dumpManager.registerDumpable("protoDumpable1", protoDumpable1) 276 dumpManager.registerDumpable("protoDumpable2", protoDumpable2) 277 278 val args = arrayOf(DumpHandler.PROTO) 279 dumpHandler.dump(fd, pw, args) 280 281 verify(protoDumpable1).dumpProto(any(), eq(args)) 282 verify(protoDumpable2).dumpProto(any(), eq(args)) 283 } 284 285 @Test 286 fun testDumpSingleProtoDumpable() { 287 dumpManager.registerDumpable("protoDumpable1", protoDumpable1) 288 dumpManager.registerDumpable("protoDumpable2", protoDumpable2) 289 290 val args = arrayOf(DumpHandler.PROTO, "protoDumpable1") 291 dumpHandler.dump(fd, pw, args) 292 293 verify(protoDumpable1).dumpProto(any(), eq(args)) 294 verify(protoDumpable2, never()).dumpProto(any(), any()) 295 } 296 297 @Test 298 fun testDumpTarget_selectsShortestNamedDumpable() { 299 // GIVEN a variety of registered dumpables and buffers 300 dumpManager.registerCriticalDumpable("first-dumpable", dumpable1) 301 dumpManager.registerCriticalDumpable("scnd-dumpable", dumpable2) 302 dumpManager.registerCriticalDumpable("third-dumpable", dumpable3) 303 304 // WHEN a dumpable is dumped by a suffix that matches multiple options 305 val args = arrayOf("dumpable") 306 dumpHandler.dump(fd, pw, args) 307 308 // THEN the matching dumpable with the shorter name is dumped 309 verify(dumpable1, never()).dump(any(), any()) 310 verify(dumpable2).dump(pw, args) 311 verify(dumpable3, never()).dump(any(), any()) 312 } 313 314 @Test 315 fun testDumpTarget_selectsShortestNamedBuffer() { 316 // GIVEN a variety of registered dumpables and buffers 317 dumpManager.registerBuffer("first-buffer", buffer1) 318 dumpManager.registerBuffer("scnd-buffer", buffer2) 319 320 // WHEN a dumpable is dumped by a suffix that matches multiple options 321 val args = arrayOf("buffer", "--tail", "14") 322 dumpHandler.dump(fd, pw, args) 323 324 // THEN the matching buffer with the shorter name is dumped 325 verify(buffer1, never()).dump(any(), anyInt()) 326 verify(buffer2).dump(pw, tailLength = 14) 327 } 328 329 @Test 330 fun testDumpTarget_selectsShortestNamedMatch_dumpable() { 331 // GIVEN a variety of registered dumpables and buffers 332 dumpManager.registerCriticalDumpable("dumpable1", dumpable1) 333 dumpManager.registerCriticalDumpable("dumpable2", dumpable2) 334 dumpManager.registerCriticalDumpable("dumpable3", dumpable3) 335 dumpManager.registerBuffer("big-buffer1", buffer1) 336 dumpManager.registerBuffer("big-buffer2", buffer2) 337 338 // WHEN a dumpable is dumped by a suffix that matches multiple options 339 val args = arrayOf("2") 340 dumpHandler.dump(fd, pw, args) 341 342 // THEN the matching dumpable with the shorter name is dumped 343 verify(dumpable1, never()).dump(any(), any()) 344 verify(dumpable2).dump(pw, args) 345 verify(dumpable3, never()).dump(any(), any()) 346 verify(buffer1, never()).dump(any(), anyInt()) 347 verify(buffer2, never()).dump(any(), anyInt()) 348 } 349 350 @Test 351 fun testDumpTarget_selectsShortestNamedMatch_buffer() { 352 // GIVEN a variety of registered dumpables and buffers 353 dumpManager.registerCriticalDumpable("dumpable1", dumpable1) 354 dumpManager.registerCriticalDumpable("dumpable2", dumpable2) 355 dumpManager.registerCriticalDumpable("dumpable3", dumpable3) 356 dumpManager.registerBuffer("buffer1", buffer1) 357 dumpManager.registerBuffer("buffer2", buffer2) 358 359 // WHEN a dumpable is dumped by a suffix that matches multiple options 360 val args = arrayOf("2", "--tail", "14") 361 dumpHandler.dump(fd, pw, args) 362 363 // THEN the matching buffer with the shorter name is dumped 364 verify(dumpable1, never()).dump(any(), any()) 365 verify(dumpable2, never()).dump(any(), any()) 366 verify(dumpable3, never()).dump(any(), any()) 367 verify(buffer1, never()).dump(any(), anyInt()) 368 verify(buffer2).dump(pw, tailLength = 14) 369 } 370 371 @Test 372 fun testDumpTarget_selectsTheAlphabeticallyFirstShortestMatch_dumpable() { 373 // GIVEN a variety of registered dumpables and buffers 374 dumpManager.registerCriticalDumpable("d1x", dumpable1) 375 dumpManager.registerCriticalDumpable("d2x", dumpable2) 376 dumpManager.registerCriticalDumpable("a3x", dumpable3) 377 dumpManager.registerBuffer("ab1x", buffer1) 378 dumpManager.registerBuffer("b2x", buffer2) 379 380 // WHEN a dumpable is dumped by a suffix that matches multiple options 381 val args = arrayOf("x") 382 dumpHandler.dump(fd, pw, args) 383 384 // THEN the alphabetically first dumpable/buffer (of the 3 letter names) is dumped 385 verify(dumpable1, never()).dump(any(), any()) 386 verify(dumpable2, never()).dump(any(), any()) 387 verify(dumpable3).dump(pw, args) 388 verify(buffer1, never()).dump(any(), anyInt()) 389 verify(buffer2, never()).dump(any(), anyInt()) 390 } 391 392 @Test 393 fun testDumpTarget_selectsTheAlphabeticallyFirstShortestMatch_buffer() { 394 // GIVEN a variety of registered dumpables and buffers 395 dumpManager.registerCriticalDumpable("d1x", dumpable1) 396 dumpManager.registerCriticalDumpable("d2x", dumpable2) 397 dumpManager.registerCriticalDumpable("az1x", dumpable3) 398 dumpManager.registerBuffer("b1x", buffer1) 399 dumpManager.registerBuffer("b2x", buffer2) 400 401 // WHEN a dumpable is dumped by a suffix that matches multiple options 402 val args = arrayOf("x", "--tail", "14") 403 dumpHandler.dump(fd, pw, args) 404 405 // THEN the alphabetically first dumpable/buffer (of the 3 letter names) is dumped 406 verify(dumpable1, never()).dump(any(), any()) 407 verify(dumpable2, never()).dump(any(), any()) 408 verify(dumpable3, never()).dump(any(), any()) 409 verify(buffer1).dump(pw, tailLength = 14) 410 verify(buffer2, never()).dump(any(), anyInt()) 411 } 412 413 414 private class EmptyCoreStartable : CoreStartable { 415 override fun start() {} 416 } 417 } 418