1 /* 2 * Copyright (C) 2019 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 package com.android.tradefed.testtype.binary; 17 18 import com.android.annotations.VisibleForTesting; 19 import com.android.tradefed.build.BuildInfoKey.BuildInfoFileKey; 20 import com.android.tradefed.build.IBuildInfo; 21 import com.android.tradefed.build.IDeviceBuildInfo; 22 import com.android.tradefed.config.Option; 23 import com.android.tradefed.config.OptionClass; 24 import com.android.tradefed.device.DeviceNotAvailableException; 25 import com.android.tradefed.device.ITestDevice; 26 import com.android.tradefed.device.StubDevice; 27 import com.android.tradefed.log.LogUtil.CLog; 28 import com.android.tradefed.result.ITestInvocationListener; 29 import com.android.tradefed.result.TestDescription; 30 import com.android.tradefed.testtype.IBuildReceiver; 31 import com.android.tradefed.testtype.IDeviceTest; 32 import com.android.tradefed.util.CommandResult; 33 import com.android.tradefed.util.CommandStatus; 34 import com.android.tradefed.util.FileUtil; 35 import com.android.tradefed.util.IRunUtil; 36 import com.android.tradefed.util.RunUtil; 37 38 import java.io.File; 39 import java.io.IOException; 40 import java.util.ArrayList; 41 import java.util.List; 42 43 /** 44 * Test runner for executable running on the host. The runner implements {@link IDeviceTest} since 45 * the host binary might communicate to a device. If the received device is not a {@link StubDevice} 46 * the serial will be passed to the binary to be used. 47 */ 48 @OptionClass(alias = "executable-host-test") 49 public class ExecutableHostTest extends ExecutableBaseTest implements IDeviceTest, IBuildReceiver { 50 51 private static final String ANDROID_SERIAL = "ANDROID_SERIAL"; 52 53 @Option( 54 name = "per-binary-timeout", 55 isTimeVal = true, 56 description = "Timeout applied to each binary for their execution." 57 ) 58 private long mTimeoutPerBinaryMs = 5 * 60 * 1000L; 59 60 private ITestDevice mDevice; 61 private IBuildInfo mBuild; 62 63 @Override findBinary(String binary)64 public String findBinary(String binary) { 65 File bin = new File(binary); 66 // If it's a local path or absolute path 67 if (bin.exists()) { 68 return bin.getAbsolutePath(); 69 } 70 if (mBuild instanceof IDeviceBuildInfo) { 71 IDeviceBuildInfo deviceBuild = (IDeviceBuildInfo) mBuild; 72 File testsDir = deviceBuild.getTestsDir(); 73 74 List<File> scanDirs = new ArrayList<>(); 75 // If it exists, always look first in the ANDROID_HOST_OUT_TESTCASES 76 File targetTestCases = deviceBuild.getFile(BuildInfoFileKey.HOST_LINKED_DIR); 77 if (targetTestCases != null) { 78 scanDirs.add(targetTestCases); 79 } 80 if (testsDir != null) { 81 scanDirs.add(testsDir); 82 } 83 84 try { 85 // Search the full tests dir if no target dir is available. 86 File src = FileUtil.findFile(binary, getAbi(), scanDirs.toArray(new File[] {})); 87 if (src != null) { 88 return src.getAbsolutePath(); 89 } 90 } catch (IOException e) { 91 CLog.e("Failed to find test files from directory."); 92 } 93 } 94 return null; 95 } 96 97 @Override runBinary( String binaryPath, ITestInvocationListener listener, TestDescription description)98 public void runBinary( 99 String binaryPath, ITestInvocationListener listener, TestDescription description) 100 throws DeviceNotAvailableException { 101 IRunUtil runUtil = createRunUtil(); 102 // Output everything in stdout 103 runUtil.setRedirectStderrToStdout(true); 104 // If we are running against a real device, set ANDROID_SERIAL to the proper serial. 105 if (!(mDevice.getIDevice() instanceof StubDevice)) { 106 runUtil.setEnvVariable(ANDROID_SERIAL, mDevice.getSerialNumber()); 107 } 108 // Ensure its executable 109 FileUtil.chmodRWXRecursively(new File(binaryPath)); 110 111 List<String> command = new ArrayList<>(); 112 command.add(binaryPath); 113 CommandResult res = 114 runUtil.runTimedCmd(mTimeoutPerBinaryMs, command.toArray(new String[0])); 115 if (!CommandStatus.SUCCESS.equals(res.getStatus())) { 116 // Everything should be outputted in stdout with our redirect above. 117 String errorMessage = res.getStdout(); 118 if (CommandStatus.TIMED_OUT.equals(res.getStatus())) { 119 errorMessage += "\nTimeout."; 120 } 121 if (res.getExitCode() != null) { 122 errorMessage += String.format("\nExit Code: %s", res.getExitCode()); 123 } 124 listener.testFailed(description, errorMessage); 125 } 126 if (!(mDevice.getIDevice() instanceof StubDevice)) { 127 // Ensure that the binary did not leave the device offline. 128 CLog.d("Checking whether device is still online after %s", binaryPath); 129 try { 130 mDevice.waitForDeviceAvailable(); 131 } catch (DeviceNotAvailableException e) { 132 listener.testRunFailed( 133 String.format("Device became unavailable after %s.", binaryPath)); 134 throw e; 135 } 136 } 137 } 138 139 @Override setDevice(ITestDevice device)140 public final void setDevice(ITestDevice device) { 141 mDevice = device; 142 } 143 144 @Override getDevice()145 public final ITestDevice getDevice() { 146 return mDevice; 147 } 148 149 @Override setBuild(IBuildInfo buildInfo)150 public final void setBuild(IBuildInfo buildInfo) { 151 mBuild = buildInfo; 152 } 153 154 @VisibleForTesting createRunUtil()155 IRunUtil createRunUtil() { 156 return new RunUtil(); 157 } 158 } 159