1 /* 2 * Copyright (C) 2017 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.tradefed.util; 18 19 import com.android.tradefed.build.IBuildInfo; 20 import com.android.tradefed.build.IDeviceBuildInfo; 21 import com.android.tradefed.command.CommandOptions; 22 import com.android.tradefed.config.filter.CommandOptionsGetter; 23 import com.android.tradefed.log.LogUtil.CLog; 24 import com.android.tradefed.service.TradefedFeatureClient; 25 26 import com.google.common.annotations.VisibleForTesting; 27 import com.proto.tradefed.feature.FeatureResponse; 28 29 import java.io.File; 30 import java.util.ArrayList; 31 import java.util.Arrays; 32 import java.util.HashMap; 33 import java.util.List; 34 import java.util.Map; 35 36 37 /** Utility class for making system calls. */ 38 public class SystemUtil { 39 40 @VisibleForTesting static SystemUtil singleton = new SystemUtil(); 41 42 // Environment variables for the test cases directory in target out directory and host out 43 // directory. 44 public enum EnvVariable { 45 ANDROID_TARGET_OUT_TESTCASES, 46 ANDROID_HOST_OUT_TESTCASES, 47 } 48 49 public static final String REMOTE_VM_VARIABLE = "REMOTE_VM_ENV"; 50 51 private static final String HOST_TESTCASES = "host/testcases"; 52 private static final String TARGET_TESTCASES = "target/testcases"; 53 54 private static final String LOCAL_AUTH_VARIABLE = "LOCAL_AUTH"; 55 private static final String LOCAL_MODE = "LOCAL_MODE"; 56 57 /** Keep track of the mapping of the variables to the subpath it takes in the tests dir. */ 58 public static final Map<EnvVariable, String> ENV_VARIABLE_PATHS_IN_TESTS_DIR = new HashMap<>(); 59 60 static { ENV_VARIABLE_PATHS_IN_TESTS_DIR.put( EnvVariable.ANDROID_TARGET_OUT_TESTCASES, TARGET_TESTCASES)61 ENV_VARIABLE_PATHS_IN_TESTS_DIR.put( 62 EnvVariable.ANDROID_TARGET_OUT_TESTCASES, TARGET_TESTCASES); ENV_VARIABLE_PATHS_IN_TESTS_DIR.put(EnvVariable.ANDROID_HOST_OUT_TESTCASES, HOST_TESTCASES)63 ENV_VARIABLE_PATHS_IN_TESTS_DIR.put(EnvVariable.ANDROID_HOST_OUT_TESTCASES, HOST_TESTCASES); 64 } 65 66 static final String ENV_ANDROID_PRODUCT_OUT = "ANDROID_PRODUCT_OUT"; 67 68 69 /** 70 * Get the value of an environment variable. 71 * 72 * <p>The wrapper function is created for mock in unit test. 73 * 74 * @param name the name of the environment variable. 75 * @return {@link String} value of the given environment variable. 76 */ 77 @VisibleForTesting getEnv(String name)78 String getEnv(String name) { 79 return System.getenv(name); 80 } 81 82 /** Get a list of {@link File} pointing to tests directories external to Tradefed. */ getExternalTestCasesDirs()83 public static List<File> getExternalTestCasesDirs() { 84 List<File> testCasesDirs = new ArrayList<File>(); 85 // TODO(b/36782030): Support running both HOST and TARGET tests. 86 List<String> testCasesDirNames = 87 // List order matters. ConfigurationFactory caller uses first dir with test config. 88 Arrays.asList( 89 singleton.getEnv(EnvVariable.ANDROID_TARGET_OUT_TESTCASES.name()), 90 singleton.getEnv(EnvVariable.ANDROID_HOST_OUT_TESTCASES.name())); 91 for (String testCasesDirName : testCasesDirNames) { 92 if (testCasesDirName != null) { 93 File dir = new File(testCasesDirName); 94 if (dir.exists() && dir.isDirectory()) { 95 CLog.d("Found test case dir: %s", testCasesDirName); 96 testCasesDirs.add(dir); 97 } else { 98 CLog.w( 99 "Path %s for test cases directory does not exist or it's not a " 100 + "directory.", 101 testCasesDirName); 102 } 103 } 104 } 105 return testCasesDirs; 106 } 107 108 /** 109 * Get the file associated with the env. variable. 110 * 111 * @param envVariable ANDROID_TARGET_OUT_TESTCASES or ANDROID_HOST_OUT_TESTCASES 112 * @return The directory associated. 113 */ getExternalTestCasesDir(EnvVariable envVariable)114 public static File getExternalTestCasesDir(EnvVariable envVariable) { 115 String var = System.getenv(envVariable.name()); 116 if (var == null) { 117 return null; 118 } 119 File dir = new File(var); 120 if (dir.exists() && dir.isDirectory()) { 121 CLog.d("Found test case dir: %s for %s.", dir.getAbsolutePath(), envVariable.name()); 122 return dir; 123 } 124 return null; 125 } 126 127 /** 128 * Get a list of {@link File} of the test cases directories 129 * 130 * @param buildInfo the build artifact information. Set it to null if build info is not 131 * available or there is no need to get test cases directories from build info. 132 * @return a list of {@link File} of directories of the test cases folder of build output, based 133 * on the value of environment variables and the given build info. 134 */ getTestCasesDirs(IBuildInfo buildInfo)135 public static List<File> getTestCasesDirs(IBuildInfo buildInfo) { 136 List<File> testCasesDirs = new ArrayList<File>(); 137 testCasesDirs.addAll(getExternalTestCasesDirs()); 138 139 // TODO: Remove this logic after Versioned TF V2 is implemented, in which staging build 140 // artifact will be done by the parent process, and the test cases dirs will be set by 141 // environment variables. 142 // Add tests dir from build info. 143 if (buildInfo instanceof IDeviceBuildInfo) { 144 IDeviceBuildInfo deviceBuildInfo = (IDeviceBuildInfo) buildInfo; 145 File testsDir = deviceBuildInfo.getTestsDir(); 146 // Add all possible paths to the testcases directory list. 147 if (testsDir != null) { 148 testCasesDirs.addAll( 149 Arrays.asList( 150 testsDir, 151 FileUtil.getFileForPath(testsDir, HOST_TESTCASES), 152 FileUtil.getFileForPath(testsDir, TARGET_TESTCASES))); 153 } 154 } 155 156 return testCasesDirs; 157 } 158 159 /** 160 * Gets the product specific output dir from an Android build tree. Typically this location 161 * contains images for various device partitions, bootloader, radio and so on. 162 * 163 * <p>Note: the method does not guarantee that this path exists. 164 * 165 * @return the location of the output dir or <code>null</code> if the current build is not 166 */ getProductOutputDir()167 public static File getProductOutputDir() { 168 String path = singleton.getEnv(ENV_ANDROID_PRODUCT_OUT); 169 if (path == null) { 170 return null; 171 } else { 172 return new File(path); 173 } 174 } 175 176 /** Return true if we are currently running in a remote environment. */ isRemoteEnvironment()177 public static boolean isRemoteEnvironment() { 178 if ("1".equals(System.getenv(REMOTE_VM_VARIABLE))) { 179 return true; 180 } 181 return false; 182 } 183 184 /** 185 * Returns true if Tradefed is running in local mode and should automate some actions for user. 186 */ isLocalMode()187 public static boolean isLocalMode() { 188 if (System.getenv(LOCAL_MODE) != null) { 189 return true; 190 } 191 if (System.getenv(LOCAL_AUTH_VARIABLE) != null) { 192 return true; 193 } 194 return false; 195 } 196 197 /** Returns the path to the Java binary that current test harness is running in */ getRunningJavaBinaryPath()198 public static File getRunningJavaBinaryPath() { 199 return getRunningJavaBinaryPath(false); 200 } 201 202 /** 203 * This version with explicit feature server is only for special situation such as noisy dry 204 * run. 205 */ getRunningJavaBinaryPath(boolean skipJavaCheck)206 public static File getRunningJavaBinaryPath(boolean skipJavaCheck) { 207 String javaHome = System.getProperty("java.home"); 208 if (!skipJavaCheck) { 209 try (TradefedFeatureClient client = new TradefedFeatureClient()) { 210 Map<String, String> args = new HashMap<String, String>(); 211 args.put(CommandOptionsGetter.OPTION_NAME, CommandOptions.JDK_FOLDER_OPTION_NAME); 212 FeatureResponse rep = 213 client.triggerFeature(CommandOptionsGetter.COMMAND_OPTIONS_GETTER, args); 214 if (!rep.hasErrorInfo()) { 215 javaHome = rep.getResponse(); 216 CLog.d("Using jdk: %s", javaHome); 217 } 218 } 219 } 220 if (javaHome == null) { 221 throw new RuntimeException("System property \"java.home\" is not set."); 222 } 223 // this only works on *nix systems, but it's not like we officially support any others 224 File javaBinary = new File(String.format("%s/bin/java", javaHome)); 225 if (!javaBinary.exists()) { 226 throw new RuntimeException( 227 String.format("Computed Java binary path %s does not exist", javaBinary)); 228 } 229 return javaBinary; 230 } 231 } 232