1 /*
2  * Copyright (C) 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 com.android.csuite.core;
18 
19 import com.android.tradefed.config.Option;
20 import com.android.tradefed.invoker.TestInformation;
21 import com.android.tradefed.log.LogUtil.CLog;
22 import com.android.tradefed.targetprep.ITargetPreparer;
23 import com.android.tradefed.targetprep.TargetSetupError;
24 import com.android.tradefed.util.CommandResult;
25 import com.android.tradefed.util.CommandStatus;
26 import com.android.tradefed.util.IRunUtil;
27 import com.android.tradefed.util.RunUtil;
28 
29 import com.google.common.annotations.VisibleForTesting;
30 import com.google.common.io.MoreFiles;
31 
32 import java.io.File;
33 import java.io.IOException;
34 import java.nio.file.FileSystem;
35 import java.nio.file.FileSystems;
36 import java.nio.file.Files;
37 import java.nio.file.Path;
38 
39 /** A Tradefed preparer that preparers an app crawler on the host before testing. */
40 public final class AppCrawlTesterHostPreparer implements ITargetPreparer {
41     private static final long COMMAND_TIMEOUT_MILLIS = 4 * 60 * 1000;
42     private static final String SDK_PATH_KEY = "SDK_PATH_KEY";
43     private static final String CRAWLER_BIN_PATH_KEY = "CSUITE_INTERNAL_CRAWLER_BIN_PATH";
44     private static final String CREDENTIAL_PATH_KEY = "CSUITE_INTERNAL_CREDENTIAL_PATH";
45     private static final String IS_READY_KEY = "CSUITE_INTERNAL_IS_READY";
46     @VisibleForTesting static final String SDK_TAR_OPTION = "sdk-tar";
47     @VisibleForTesting static final String CRAWLER_BIN_OPTION = "crawler-bin";
48     @VisibleForTesting static final String CREDENTIAL_JSON_OPTION = "credential-json";
49     private final FileSystem mFileSystem;
50 
51     @Option(
52             name = SDK_TAR_OPTION,
53             mandatory = true,
54             description = "The path to a tar file that contains the Android SDK.")
55     private File mSdkTar;
56 
57     @Option(
58             name = CRAWLER_BIN_OPTION,
59             mandatory = true,
60             description = "Path to the directory containing the required crawler binary files.")
61     private File mCrawlerBin;
62 
63     @Option(
64             name = CREDENTIAL_JSON_OPTION,
65             mandatory = true,
66             description = "The credential json file to access the crawler server.")
67     private File mCredential;
68 
69     private RunUtilProvider mRunUtilProvider;
70 
AppCrawlTesterHostPreparer()71     public AppCrawlTesterHostPreparer() {
72         this(() -> new RunUtil(), FileSystems.getDefault());
73     }
74 
75     @VisibleForTesting
AppCrawlTesterHostPreparer(RunUtilProvider runUtilProvider, FileSystem fileSystem)76     AppCrawlTesterHostPreparer(RunUtilProvider runUtilProvider, FileSystem fileSystem) {
77         mRunUtilProvider = runUtilProvider;
78         mFileSystem = fileSystem;
79     }
80 
81     /**
82      * Returns a path that contains Android SDK.
83      *
84      * @param testInfo The test info where the path is stored in.
85      * @return The path to Android SDK; Null if not set.
86      */
getSdkPath(TestInformation testInfo)87     public static String getSdkPath(TestInformation testInfo) {
88         return getPathFromBuildInfo(testInfo, SDK_PATH_KEY);
89     }
90 
91     /**
92      * Returns a path that contains the crawler binaries.
93      *
94      * @param testInfo The test info where the path is stored in.
95      * @return The path to the crawler binaries folder; Null if not set.
96      */
getCrawlerBinPath(TestInformation testInfo)97     public static String getCrawlerBinPath(TestInformation testInfo) {
98         return getPathFromBuildInfo(testInfo, CRAWLER_BIN_PATH_KEY);
99     }
100 
101     /**
102      * Returns a path to the credential json file for accessing the Robo crawler server.
103      *
104      * @param testInfo The test info where the path is stored in.
105      * @return The path to the crawler credential json file.
106      */
getCredentialPath(TestInformation testInfo)107     public static String getCredentialPath(TestInformation testInfo) {
108         return getPathFromBuildInfo(testInfo, CREDENTIAL_PATH_KEY);
109     }
110 
111     /**
112      * Checks whether the preparer has successfully executed.
113      *
114      * @param testInfo The test info .
115      * @return True if the preparer was executed successfully; False otherwise.
116      */
isReady(TestInformation testInfo)117     public static boolean isReady(TestInformation testInfo) {
118         return testInfo.getBuildInfo().getBuildAttributes().get(IS_READY_KEY) != null;
119     }
120 
getPathFromBuildInfo(TestInformation testInfo, String key)121     private static String getPathFromBuildInfo(TestInformation testInfo, String key) {
122         return testInfo.getBuildInfo().getBuildAttributes().get(key);
123     }
124 
125     @VisibleForTesting
setSdkPath(TestInformation testInfo, Path path)126     static void setSdkPath(TestInformation testInfo, Path path) {
127         testInfo.getBuildInfo().addBuildAttribute(SDK_PATH_KEY, path.toString());
128     }
129 
130     @VisibleForTesting
setCrawlerBinPath(TestInformation testInfo, Path path)131     static void setCrawlerBinPath(TestInformation testInfo, Path path) {
132         testInfo.getBuildInfo().addBuildAttribute(CRAWLER_BIN_PATH_KEY, path.toString());
133     }
134 
135     @VisibleForTesting
setCredentialPath(TestInformation testInfo, Path path)136     static void setCredentialPath(TestInformation testInfo, Path path) {
137         testInfo.getBuildInfo().addBuildAttribute(CREDENTIAL_PATH_KEY, path.toString());
138     }
139 
140     @Override
setUp(TestInformation testInfo)141     public void setUp(TestInformation testInfo) throws TargetSetupError {
142         IRunUtil runUtil = mRunUtilProvider.get();
143 
144         Path sdkPath;
145         try {
146             sdkPath = Files.createTempDirectory("android-sdk");
147         } catch (IOException e) {
148             throw new TargetSetupError("Failed to create the output path for android sdk.", e);
149         }
150 
151         String cmd = "tar -xvzf " + mSdkTar.getPath() + " -C " + sdkPath.toString();
152         CLog.i("Decompressing Android SDK to " + sdkPath.toString());
153         CommandResult res = runUtil.runTimedCmd(COMMAND_TIMEOUT_MILLIS, cmd.split(" "));
154         if (!res.getStatus().equals(CommandStatus.SUCCESS)) {
155             throw new TargetSetupError(String.format("Failed to untar android sdk: %s", res));
156         }
157 
158         setSdkPath(testInfo, sdkPath);
159 
160         Path jar = mCrawlerBin.toPath().resolve("crawl_launcher_deploy.jar");
161         if (!Files.exists(jar)) {
162             jar = mCrawlerBin.toPath().resolve("utp-cli-android_deploy.jar");
163         }
164 
165         // Make the crawler binary executable.
166         String chmodCmd = "chmod 555 " + jar.toString();
167         CommandResult chmodRes = runUtil.runTimedCmd(COMMAND_TIMEOUT_MILLIS, chmodCmd.split(" "));
168         if (!chmodRes.getStatus().equals(CommandStatus.SUCCESS)) {
169             throw new TargetSetupError(
170                     String.format("Failed to make crawler binary executable: %s", chmodRes));
171         }
172 
173         setCrawlerBinPath(testInfo, mCrawlerBin.toPath());
174 
175         setCredentialPath(testInfo, mCredential.toPath());
176 
177         testInfo.getBuildInfo().addBuildAttribute(IS_READY_KEY, "true");
178     }
179 
180     @Override
tearDown(TestInformation testInfo, Throwable e)181     public void tearDown(TestInformation testInfo, Throwable e) {
182         try {
183             cleanUp(mFileSystem.getPath(getSdkPath(testInfo)));
184         } catch (IOException ioException) {
185             CLog.e(ioException);
186         }
187     }
188 
cleanUp(Path path)189     private static void cleanUp(Path path) throws IOException {
190         if (path == null || !Files.exists(path)) {
191             return;
192         }
193 
194         MoreFiles.deleteRecursively(path);
195     }
196 
197     @VisibleForTesting
198     interface RunUtilProvider {
get()199         IRunUtil get();
200     }
201 }
202