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