• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.testtype;
18 
19 import com.android.ddmlib.IShellOutputReceiver;
20 import com.android.tradefed.build.BuildInfoKey.BuildInfoFileKey;
21 import com.android.tradefed.build.DeviceBuildInfo;
22 import com.android.tradefed.build.IBuildInfo;
23 import com.android.tradefed.config.OptionClass;
24 import com.android.tradefed.device.DeviceNotAvailableException;
25 import com.android.tradefed.log.LogUtil.CLog;
26 import com.android.tradefed.result.ITestInvocationListener;
27 import com.android.tradefed.util.CommandResult;
28 import com.android.tradefed.util.CommandStatus;
29 import com.android.tradefed.util.FileUtil;
30 import com.android.tradefed.util.RunUtil;
31 import com.android.tradefed.util.ShellOutputReceiverStream;
32 
33 import org.json.JSONException;
34 import org.json.JSONObject;
35 
36 import java.io.File;
37 import java.io.IOException;
38 import java.util.ArrayList;
39 import java.util.List;
40 
41 /** A Test that runs a native test package. */
42 @OptionClass(alias = "hostgtest")
43 public class HostGTest extends GTestBase implements IAbiReceiver, IBuildReceiver {
44     private static final long DEFAULT_HOST_COMMAND_TIMEOUT_MS = 2 * 60 * 1000;
45 
46     private IBuildInfo mBuildInfo = null;
47     private IAbi mAbi = null;
48 
49     @Override
setAbi(IAbi abi)50     public void setAbi(IAbi abi) {
51         this.mAbi = abi;
52     }
53 
54     @Override
getAbi()55     public IAbi getAbi() {
56         return this.mAbi;
57     }
58 
59     @Override
setBuild(IBuildInfo buildInfo)60     public void setBuild(IBuildInfo buildInfo) {
61         this.mBuildInfo = buildInfo;
62     }
63 
64     /**
65      * @param cmd command that want to execute in host
66      * @return the {@link CommandResult} of command
67      */
executeHostCommand(String cmd)68     public CommandResult executeHostCommand(String cmd) {
69         return executeHostCommand(cmd, DEFAULT_HOST_COMMAND_TIMEOUT_MS);
70     }
71 
72     /**
73      * @param cmd command that want to execute in host
74      * @param timeoutMs timeout for command in milliseconds
75      * @return the {@link CommandResult} of command
76      */
executeHostCommand(String cmd, long timeoutMs)77     public CommandResult executeHostCommand(String cmd, long timeoutMs) {
78         String[] cmds = cmd.split("\\s+");
79         return RunUtil.getDefault().runTimedCmd(timeoutMs, cmds);
80     }
81 
82     /**
83      * @param cmd command that want to execute in host
84      * @param timeoutMs timeout for command in milliseconds
85      * @param receiver the result parser
86      * @return the {@link CommandResult} of command
87      */
executeHostGTestCommand( String cmd, long timeoutMs, IShellOutputReceiver receiver)88     public CommandResult executeHostGTestCommand(
89             String cmd, long timeoutMs, IShellOutputReceiver receiver) {
90         RunUtil runUtil = new RunUtil();
91         String[] cmds = cmd.split("\\s+");
92 
93         if (getShardCount() > 0) {
94             if (isCollectTestsOnly()) {
95                 CLog.w(
96                         "--collect-tests-only option ignores sharding parameters, and will cause "
97                                 + "each shard to collect all tests.");
98             }
99             runUtil.setEnvVariable("GTEST_SHARD_INDEX", Integer.toString(getShardIndex()));
100             runUtil.setEnvVariable("GTEST_TOTAL_SHARDS", Integer.toString(getShardCount()));
101         }
102 
103         // Set the RunUtil to combine stderr with stdout so that they are interleaved correctly.
104         runUtil.setRedirectStderrToStdout(true);
105 
106         // If there's a shell output receiver to pass results along to, then
107         // ShellOutputReceiverStream will write that into the IShellOutputReceiver. If not, the
108         // command output will just be ignored.
109         CommandResult result;
110         try (ShellOutputReceiverStream stream = new ShellOutputReceiverStream(receiver)) {
111             result = runUtil.runTimedCmd(timeoutMs, stream, null, cmds);
112         } catch (IOException e) {
113             throw new RuntimeException(
114                     "Should never happen, ShellOutputReceiverStream.close is a no-op");
115         }
116         return result;
117     }
118 
119     /** {@inheritDoc} */
120     @Override
loadFilter(String binaryOnHost)121     public String loadFilter(String binaryOnHost) {
122         try {
123             CLog.i("Loading filter from file for key: '%s'", getTestFilterKey());
124             String filterFileName = String.format("%s%s", binaryOnHost, FILTER_EXTENSION);
125             File filterFile = new File(filterFileName);
126             if (filterFile.exists()) {
127                 CommandResult cmdResult =
128                         executeHostCommand(String.format("cat %s", filterFileName));
129                 String content = cmdResult.getStdout();
130                 if (content != null && !content.isEmpty()) {
131                     JSONObject filter = new JSONObject(content);
132                     String key = getTestFilterKey();
133                     JSONObject filterObject = filter.getJSONObject(key);
134                     return filterObject.getString("filter");
135                 }
136                 CLog.e("Error with content of the filter file %s: %s", filterFile, content);
137             } else {
138                 CLog.e("Filter file %s not found", filterFile);
139             }
140         } catch (JSONException e) {
141             CLog.e(e);
142         }
143         return null;
144     }
145 
146     /**
147      * Run the given gtest binary
148      *
149      * @param resultParser the test run output parser
150      * @param fullPath absolute file system path to gtest binary
151      * @param flags gtest execution flags
152      */
runTest( final IShellOutputReceiver resultParser, final String fullPath, final String flags)153     private void runTest(
154             final IShellOutputReceiver resultParser, final String fullPath, final String flags) {
155         try {
156             for (String cmd : getBeforeTestCmd()) {
157                 CommandResult result = executeHostCommand(cmd);
158                 if (!result.getStatus().equals(CommandStatus.SUCCESS)) {
159                     throw new RuntimeException(
160                             "'Before test' command failed: " + result.getStderr());
161                 }
162             }
163 
164             long maxTestTimeMs = getMaxTestTimeMs();
165             String cmd = getGTestCmdLine(fullPath, flags);
166             CommandResult testResult = executeHostGTestCommand(cmd, maxTestTimeMs, resultParser);
167             // TODO: Switch throwing exceptions to use ITestInvocation.testRunFailed
168             switch (testResult.getStatus()) {
169                 case FAILED:
170                     // Check the command exit code. If it's 1, then this is just a red herring;
171                     // gtest returns 1 when a test fails.
172                     final Integer exitCode = testResult.getExitCode();
173                     if (exitCode == null || exitCode != 1) {
174                         throw new RuntimeException(
175                                 String.format("Command run failed with exit code %s", exitCode));
176                     }
177                     break;
178                 case TIMED_OUT:
179                     throw new RuntimeException(
180                             String.format("Command run timed out after %d ms", maxTestTimeMs));
181                 case EXCEPTION:
182                     throw new RuntimeException("Command run failed with exception");
183             }
184         } finally {
185             resultParser.flush();
186             for (String cmd : getAfterTestCmd()) {
187                 CommandResult result = executeHostCommand(cmd);
188                 if (!result.getStatus().equals(CommandStatus.SUCCESS)) {
189                     throw new RuntimeException(
190                             "'After test' command failed: " + result.getStderr());
191                 }
192             }
193         }
194     }
195 
196     /** {@inheritDoc} */
197     @Override
run(ITestInvocationListener listener)198     public void run(ITestInvocationListener listener)
199             throws DeviceNotAvailableException { // DNAE is part of IRemoteTest.
200         // Get testcases directory using the key HOST_LINKED_DIR first.
201         // If the directory is null, then get testcase directory from getTestDir() since *TS will
202         // invoke setTestDir().
203         List<File> scanDirs = new ArrayList<>();
204         File hostLinkedDir = mBuildInfo.getFile(BuildInfoFileKey.HOST_LINKED_DIR);
205         if (hostLinkedDir != null) {
206             scanDirs.add(hostLinkedDir);
207         }
208         File testsDir = ((DeviceBuildInfo) mBuildInfo).getTestsDir();
209         if (testsDir != null) {
210             scanDirs.add(testsDir);
211         }
212 
213         String moduleName = getTestModule();
214         File gTestFile = null;
215         try {
216             gTestFile = FileUtil.findFile(moduleName, mAbi, scanDirs.toArray(new File[] {}));
217         } catch (IOException e) {
218             throw new RuntimeException(e);
219         }
220 
221         if (gTestFile == null || gTestFile.isDirectory()) {
222             // If we ended up here we most likely failed to find the proper file as is, so we
223             // search for it with a potential suffix (which is allowed).
224             try {
225                 File byBaseName =
226                         FileUtil.findFile(moduleName + ".*", mAbi, scanDirs.toArray(new File[] {}));
227                 if (byBaseName != null && byBaseName.isFile()) {
228                     gTestFile = byBaseName;
229                 }
230             } catch (IOException e) {
231                 throw new RuntimeException(e);
232             }
233         }
234 
235         if (gTestFile == null) {
236             throw new RuntimeException(
237                     String.format(
238                             "Fail to find native test %s in directory %s.", moduleName, scanDirs));
239         }
240 
241         if (!gTestFile.canExecute()) {
242             throw new RuntimeException(
243                     String.format("%s is not executable!", gTestFile.getAbsolutePath()));
244         }
245 
246         // TODO: Need to support XML test output based on isEnableXmlOutput
247         IShellOutputReceiver resultParser = createResultParser(gTestFile.getName(), listener);
248         String flags = getAllGTestFlags(gTestFile.getName());
249         CLog.i("Running gtest %s %s", gTestFile.getName(), flags);
250         String filePath = gTestFile.getAbsolutePath();
251         runTest(resultParser, filePath, flags);
252     }
253 }
254