1 /* 2 * Copyright 2021 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 androidx.benchmark 18 19 import android.os.Build 20 import android.os.Process 21 import androidx.test.ext.junit.runners.AndroidJUnit4 22 import androidx.test.filters.MediumTest 23 import androidx.test.filters.SdkSuppress 24 import kotlin.test.assertEquals 25 import kotlin.test.assertNotNull 26 import kotlin.test.assertTrue 27 import org.junit.Test 28 import org.junit.runner.RunWith 29 30 /** 31 * This class collects tests of strange shell behavior, for the purpose of documenting and 32 * validating how general the problems are. For this reason, these tests call directly shell 33 * commands without referring to the actual implementations in [Shell]. To test the [Shell] 34 * implementations, please add to [ShellTest]. 35 */ 36 @MediumTest 37 @SdkSuppress(minSdkVersion = 21) 38 @RunWith(AndroidJUnit4::class) 39 class ShellBehaviorTest { 40 41 /** 42 * Test validates consistent behavior of pgrep, for usage in discovering processes without 43 * needing to check stderr 44 */ 45 @Test pgrepLFExecuteScriptnull46 fun pgrepLFExecuteScript() { 47 48 val apiSpecificArgs = 49 setOfNotNull( 50 // aosp/3507001 -> needed to print full command line (so full package name) 51 if (Build.VERSION.SDK_INT >= 36) "-a" else null 52 ) 53 .joinToString(" ") 54 55 // Should only be one process - this one! 56 val pgrepOutput = 57 Shell.executeScriptCaptureStdoutStderr("pgrep -l -f $apiSpecificArgs ${Packages.TEST}") 58 59 if (Build.VERSION.SDK_INT >= 23) { 60 // API 23 has trailing whitespace after the package name for some reason :shrug: 61 val regex = "^\\d+ ${Packages.TEST.replace(".", "\\.")}\\s*$".toRegex() 62 assertTrue( 63 // For some reason, `stdout.contains(regex)` doesn't work :shrug: 64 pgrepOutput.stdout.lines().any { it.matches(regex) }, 65 "expected $regex to be contained in output:\n${pgrepOutput.stdout}" 66 ) 67 } else { 68 // command doesn't exist 69 assertEquals("", pgrepOutput.stdout) 70 assertTrue(pgrepOutput.stderr.isNotBlank()) 71 } 72 } 73 74 @Test pidofnull75 fun pidof() { 76 // Should only be one process - this one! 77 val output = Shell.executeScriptCaptureStdoutStderr("pidof ${Packages.TEST}") 78 val pidofString = output.stdout.trim() 79 80 when { 81 Build.VERSION.SDK_INT < 23 -> { 82 // command doesn't exist 83 assertTrue( 84 output.stdout.isBlank() && output.stderr.isNotBlank(), 85 "saw output $output" 86 ) 87 } 88 Build.VERSION.SDK_INT == 23 -> { 89 // on API 23 specifically, pidof prints... all processes, ignoring the arg... 90 assertTrue(pidofString.contains(" ")) 91 } 92 else -> { 93 assertNotNull(pidofString.toLongOrNull(), "Error, can't parse $pidofString") 94 } 95 } 96 } 97 98 @Test psDashAnull99 fun psDashA() { 100 val output = Shell.executeScriptCaptureStdout("ps -A").trim() 101 when { 102 Build.VERSION.SDK_INT <= 23 -> { 103 // doesn't correctly handle -A, sometimes sees nothing, sometimes only this process 104 val processes = output.lines() 105 assertTrue(processes.size <= 2) 106 assertTrue(processes.first().matches(psLabelRowRegex)) 107 if (processes.size > 1) { 108 assertTrue(processes.last().endsWith(Packages.TEST)) 109 } 110 } 111 Build.VERSION.SDK_INT in 24..25 -> { 112 // still doesn't support, but useful error at least 113 assertEquals("bad pid '-A'", output) 114 } 115 else -> { 116 // ps -A should work - expect several processes including this one 117 val processes = output.lines() 118 assertTrue(processes.size > 5) 119 assertTrue(processes.first().matches(psLabelRowRegex)) 120 assertTrue(processes.any { it.endsWith(Packages.TEST) }) 121 } 122 } 123 } 124 125 /** 126 * Test validates consistent behavior of ps, for usage in checking process is alive without 127 * needing to check stderr 128 */ 129 @Test psnull130 fun ps() { 131 val output = Shell.executeScriptCaptureStdout("ps ${Process.myPid()}").trim() 132 // ps should work - expect several processes including this one 133 val lines = output.lines() 134 assertEquals(2, lines.size) 135 assertTrue(lines.first().matches(psLabelRowRegex)) 136 assertTrue(lines.last().endsWith(Packages.TEST)) 137 } 138 139 companion object { 140 /** 141 * Regex for matching ps output label row 142 * 143 * Note that `ps` output changes over time, e.g.: 144 * * API 23 - `USER\s+PID\s+PPID\s+VSIZE\s+RSS\s+WCHAN\s+PC\s+NAME` 145 * * API 33 - `USER\s+PID\s+PPID\s+VSZ\s+RSS\s+WCHAN\s+ADDR\s+S\s+NAME\s` 146 */ 147 val psLabelRowRegex = Regex("USER\\s+PID.+NAME\\s*") 148 } 149 } 150