1*6777b538SAndroid Build Coastguard Worker /*
2*6777b538SAndroid Build Coastguard Worker  * Copyright (C) 2023 The Android Open Source Project
3*6777b538SAndroid Build Coastguard Worker  *
4*6777b538SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*6777b538SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*6777b538SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*6777b538SAndroid Build Coastguard Worker  *
8*6777b538SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*6777b538SAndroid Build Coastguard Worker  *
10*6777b538SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*6777b538SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*6777b538SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*6777b538SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*6777b538SAndroid Build Coastguard Worker  * limitations under the License.
15*6777b538SAndroid Build Coastguard Worker  */
16*6777b538SAndroid Build Coastguard Worker 
17*6777b538SAndroid Build Coastguard Worker package com.android.tests.chromium.host;
18*6777b538SAndroid Build Coastguard Worker 
19*6777b538SAndroid Build Coastguard Worker import static com.android.tests.chromium.host.InstrumentationFlags.COMMAND_LINE_FLAGS_KEY;
20*6777b538SAndroid Build Coastguard Worker import static com.android.tests.chromium.host.InstrumentationFlags.DUMP_COVERAGE_KEY;
21*6777b538SAndroid Build Coastguard Worker import static com.android.tests.chromium.host.InstrumentationFlags.EXTRA_SHARD_NANO_TIMEOUT_KEY;
22*6777b538SAndroid Build Coastguard Worker import static com.android.tests.chromium.host.InstrumentationFlags.LIBRARY_TO_LOAD_ACTIVITY_KEY;
23*6777b538SAndroid Build Coastguard Worker import static com.android.tests.chromium.host.InstrumentationFlags.NATIVE_TEST_ACTIVITY_KEY;
24*6777b538SAndroid Build Coastguard Worker import static com.android.tests.chromium.host.InstrumentationFlags.NATIVE_UNIT_TEST_ACTIVITY_KEY;
25*6777b538SAndroid Build Coastguard Worker import static com.android.tests.chromium.host.InstrumentationFlags.RUN_IN_SUBTHREAD_KEY;
26*6777b538SAndroid Build Coastguard Worker import static com.android.tests.chromium.host.InstrumentationFlags.STDOUT_FILE_KEY;
27*6777b538SAndroid Build Coastguard Worker import static com.android.tests.chromium.host.InstrumentationFlags.TEST_RUNNER;
28*6777b538SAndroid Build Coastguard Worker 
29*6777b538SAndroid Build Coastguard Worker import android.annotation.NonNull;
30*6777b538SAndroid Build Coastguard Worker 
31*6777b538SAndroid Build Coastguard Worker import com.android.ddmlib.MultiLineReceiver;
32*6777b538SAndroid Build Coastguard Worker import com.android.tradefed.config.Option;
33*6777b538SAndroid Build Coastguard Worker import com.android.tradefed.device.CollectingOutputReceiver;
34*6777b538SAndroid Build Coastguard Worker import com.android.tradefed.device.DeviceNotAvailableException;
35*6777b538SAndroid Build Coastguard Worker import com.android.tradefed.device.ITestDevice;
36*6777b538SAndroid Build Coastguard Worker import com.android.tradefed.invoker.TestInformation;
37*6777b538SAndroid Build Coastguard Worker import com.android.tradefed.log.LogUtil;
38*6777b538SAndroid Build Coastguard Worker import com.android.tradefed.result.ITestInvocationListener;
39*6777b538SAndroid Build Coastguard Worker import com.android.tradefed.result.LogDataType;
40*6777b538SAndroid Build Coastguard Worker import com.android.tradefed.result.FileInputStreamSource;
41*6777b538SAndroid Build Coastguard Worker import com.android.tradefed.testtype.GTestListTestParser;
42*6777b538SAndroid Build Coastguard Worker import com.android.tradefed.testtype.GTestResultParser;
43*6777b538SAndroid Build Coastguard Worker import com.android.tradefed.testtype.IDeviceTest;
44*6777b538SAndroid Build Coastguard Worker import com.android.tradefed.testtype.IRemoteTest;
45*6777b538SAndroid Build Coastguard Worker import com.android.tradefed.testtype.ITestCollector;
46*6777b538SAndroid Build Coastguard Worker import com.android.tradefed.testtype.ITestFilterReceiver;
47*6777b538SAndroid Build Coastguard Worker import com.android.tradefed.util.FileUtil;
48*6777b538SAndroid Build Coastguard Worker 
49*6777b538SAndroid Build Coastguard Worker import com.google.common.base.Joiner;
50*6777b538SAndroid Build Coastguard Worker import com.google.common.base.Strings;
51*6777b538SAndroid Build Coastguard Worker 
52*6777b538SAndroid Build Coastguard Worker import java.io.File;
53*6777b538SAndroid Build Coastguard Worker import java.io.IOException;
54*6777b538SAndroid Build Coastguard Worker import java.nio.file.Files;
55*6777b538SAndroid Build Coastguard Worker import java.time.Duration;
56*6777b538SAndroid Build Coastguard Worker import java.util.LinkedHashSet;
57*6777b538SAndroid Build Coastguard Worker import java.util.Set;
58*6777b538SAndroid Build Coastguard Worker import java.util.concurrent.TimeUnit;
59*6777b538SAndroid Build Coastguard Worker 
60*6777b538SAndroid Build Coastguard Worker /**
61*6777b538SAndroid Build Coastguard Worker  * A host-side test-runner capable of running Chromium unit-tests.
62*6777b538SAndroid Build Coastguard Worker  */
63*6777b538SAndroid Build Coastguard Worker public class ChromiumHostDrivenTest implements IRemoteTest, IDeviceTest, ITestCollector,
64*6777b538SAndroid Build Coastguard Worker         ITestFilterReceiver {
65*6777b538SAndroid Build Coastguard Worker 
66*6777b538SAndroid Build Coastguard Worker     private static final String CLEAR_CLANG_COVERAGE_FILES =
67*6777b538SAndroid Build Coastguard Worker             "find /data/misc/trace -name '*.profraw' -delete";
68*6777b538SAndroid Build Coastguard Worker     private static final Duration TESTS_TIMEOUT = Duration.ofMinutes(30);
69*6777b538SAndroid Build Coastguard Worker     private static final String GTEST_FLAG_PRINT_TIME = "--gtest_print_time";
70*6777b538SAndroid Build Coastguard Worker     private static final String GTEST_FLAG_FILTER = "--gtest_filter";
71*6777b538SAndroid Build Coastguard Worker     private static final String GTEST_FLAG_LIST_TESTS = "--gtest_list_tests";
72*6777b538SAndroid Build Coastguard Worker     private static final String GTEST_FLAG_FILE = "--gtest_flagfile";
73*6777b538SAndroid Build Coastguard Worker     @Option(
74*6777b538SAndroid Build Coastguard Worker         name = "include-filter",
75*6777b538SAndroid Build Coastguard Worker         description = "The set of annotations a test must have to be run.")
76*6777b538SAndroid Build Coastguard Worker     private Set<String> includeFilters = new LinkedHashSet<>();
77*6777b538SAndroid Build Coastguard Worker     @Option(
78*6777b538SAndroid Build Coastguard Worker         name = "exclude-filter",
79*6777b538SAndroid Build Coastguard Worker         description =
80*6777b538SAndroid Build Coastguard Worker             "The set of annotations to exclude tests from running. A test must have "
81*6777b538SAndroid Build Coastguard Worker                 + "none of the annotations in this list to run.")
82*6777b538SAndroid Build Coastguard Worker     private Set<String> excludeFilters = new LinkedHashSet<>();
83*6777b538SAndroid Build Coastguard Worker     private boolean collectTestsOnly = false;
84*6777b538SAndroid Build Coastguard Worker     private ITestDevice device = null;
85*6777b538SAndroid Build Coastguard Worker 
86*6777b538SAndroid Build Coastguard Worker     @Option(
87*6777b538SAndroid Build Coastguard Worker             name = "dump-native-coverage",
88*6777b538SAndroid Build Coastguard Worker             description = "Force APK under test to dump native test coverage upon exit"
89*6777b538SAndroid Build Coastguard Worker     )
90*6777b538SAndroid Build Coastguard Worker     private boolean isCoverageEnabled = false;
91*6777b538SAndroid Build Coastguard Worker     @Option(
92*6777b538SAndroid Build Coastguard Worker             name = "library-to-load",
93*6777b538SAndroid Build Coastguard Worker             description = "Name of the .so file under test"
94*6777b538SAndroid Build Coastguard Worker     )
95*6777b538SAndroid Build Coastguard Worker     private String libraryToLoad = "";
96*6777b538SAndroid Build Coastguard Worker 
97*6777b538SAndroid Build Coastguard Worker 
98*6777b538SAndroid Build Coastguard Worker     /**
99*6777b538SAndroid Build Coastguard Worker      * Creates a temporary file on the host machine then push it to the device in a temporary
100*6777b538SAndroid Build Coastguard Worker      * location It is necessary to create a temp file for output for each instrumentation run and
101*6777b538SAndroid Build Coastguard Worker      * not module invocation. This is preferred over using
102*6777b538SAndroid Build Coastguard Worker      * {@link com.android.tradefed.targetprep.RunCommandTargetPreparer}
103*6777b538SAndroid Build Coastguard Worker      * because RunCommandTargetPreparer is only run once before the test invocation which leads to
104*6777b538SAndroid Build Coastguard Worker      * incorrect parsing as the retries will all use the same file for test result outputs.
105*6777b538SAndroid Build Coastguard Worker      */
106*6777b538SAndroid Build Coastguard Worker     @NonNull
createTempResultFileOnDevice()107*6777b538SAndroid Build Coastguard Worker     private String createTempResultFileOnDevice() throws DeviceNotAvailableException {
108*6777b538SAndroid Build Coastguard Worker         File resultFile = null;
109*6777b538SAndroid Build Coastguard Worker         String deviceFileDestination;
110*6777b538SAndroid Build Coastguard Worker         try {
111*6777b538SAndroid Build Coastguard Worker             resultFile = FileUtil.createTempFile("gtest_results", ".txt");
112*6777b538SAndroid Build Coastguard Worker             deviceFileDestination = String.format("/data/local/tmp/%s", resultFile.getName());
113*6777b538SAndroid Build Coastguard Worker             getDevice().pushFile(resultFile, deviceFileDestination);
114*6777b538SAndroid Build Coastguard Worker             FileUtil.deleteFile(resultFile);
115*6777b538SAndroid Build Coastguard Worker         } catch (IOException e) {
116*6777b538SAndroid Build Coastguard Worker             throw new FailedChromiumGTestException(
117*6777b538SAndroid Build Coastguard Worker                     "Failed to create temp file for result on the device.", e);
118*6777b538SAndroid Build Coastguard Worker         } finally {
119*6777b538SAndroid Build Coastguard Worker             FileUtil.deleteFile(resultFile);
120*6777b538SAndroid Build Coastguard Worker         }
121*6777b538SAndroid Build Coastguard Worker         return deviceFileDestination;
122*6777b538SAndroid Build Coastguard Worker     }
123*6777b538SAndroid Build Coastguard Worker 
124*6777b538SAndroid Build Coastguard Worker     /**
125*6777b538SAndroid Build Coastguard Worker      * This creates the gtest filter string which indicates which test should be run.
126*6777b538SAndroid Build Coastguard Worker      * Sometimes the gtest filter is long (> 500 character) which results in creating
127*6777b538SAndroid Build Coastguard Worker      * a temporary flag file and have gtest result the filter from the flag file.
128*6777b538SAndroid Build Coastguard Worker      *
129*6777b538SAndroid Build Coastguard Worker      * @return A gtest argument for flag file or --gtest_filter directly.
130*6777b538SAndroid Build Coastguard Worker      */
131*6777b538SAndroid Build Coastguard Worker     @NonNull
getGTestFilters()132*6777b538SAndroid Build Coastguard Worker     private String getGTestFilters() throws DeviceNotAvailableException {
133*6777b538SAndroid Build Coastguard Worker         StringBuilder filter = new StringBuilder();
134*6777b538SAndroid Build Coastguard Worker         if (!includeFilters.isEmpty() || !excludeFilters.isEmpty()) {
135*6777b538SAndroid Build Coastguard Worker             filter.append(GTEST_FLAG_FILTER);
136*6777b538SAndroid Build Coastguard Worker             filter.append('=');
137*6777b538SAndroid Build Coastguard Worker             Joiner joiner = Joiner.on(":").skipNulls();
138*6777b538SAndroid Build Coastguard Worker             if (!includeFilters.isEmpty()) {
139*6777b538SAndroid Build Coastguard Worker                 joiner.appendTo(filter, includeFilters);
140*6777b538SAndroid Build Coastguard Worker             }
141*6777b538SAndroid Build Coastguard Worker             if (!excludeFilters.isEmpty()) {
142*6777b538SAndroid Build Coastguard Worker                 filter.append("-");
143*6777b538SAndroid Build Coastguard Worker                 joiner.appendTo(filter, excludeFilters);
144*6777b538SAndroid Build Coastguard Worker             }
145*6777b538SAndroid Build Coastguard Worker         }
146*6777b538SAndroid Build Coastguard Worker         String filterFlag = filter.toString();
147*6777b538SAndroid Build Coastguard Worker         // Handle long args
148*6777b538SAndroid Build Coastguard Worker         if (filterFlag.length() > 500) {
149*6777b538SAndroid Build Coastguard Worker             String tmpFlag = createFlagFileOnDevice(filterFlag);
150*6777b538SAndroid Build Coastguard Worker             return String.format("%s=%s", GTEST_FLAG_FILE, tmpFlag);
151*6777b538SAndroid Build Coastguard Worker         }
152*6777b538SAndroid Build Coastguard Worker         return filterFlag;
153*6777b538SAndroid Build Coastguard Worker     }
154*6777b538SAndroid Build Coastguard Worker 
155*6777b538SAndroid Build Coastguard Worker     /**
156*6777b538SAndroid Build Coastguard Worker      * Helper method for getGTestFilters which creates a temporary flag file and push it to device.
157*6777b538SAndroid Build Coastguard Worker      *
158*6777b538SAndroid Build Coastguard Worker      * If it fails to create a file then it will directly use the filter in the adb command.
159*6777b538SAndroid Build Coastguard Worker      *
160*6777b538SAndroid Build Coastguard Worker      * @param filter the string to append to the flag file.
161*6777b538SAndroid Build Coastguard Worker      * @return path to the flag file on device or null if it could not be created.
162*6777b538SAndroid Build Coastguard Worker      */
163*6777b538SAndroid Build Coastguard Worker     @NonNull
createFlagFileOnDevice(@onNull String filter)164*6777b538SAndroid Build Coastguard Worker     private String createFlagFileOnDevice(@NonNull String filter)
165*6777b538SAndroid Build Coastguard Worker             throws DeviceNotAvailableException {
166*6777b538SAndroid Build Coastguard Worker         File tmpFlagFile = null;
167*6777b538SAndroid Build Coastguard Worker         String devicePath;
168*6777b538SAndroid Build Coastguard Worker         try {
169*6777b538SAndroid Build Coastguard Worker             tmpFlagFile = FileUtil.createTempFile("flagfile", ".txt");
170*6777b538SAndroid Build Coastguard Worker             FileUtil.writeToFile(filter, tmpFlagFile);
171*6777b538SAndroid Build Coastguard Worker             devicePath = String.format("/data/local/tmp/%s", tmpFlagFile.getName());
172*6777b538SAndroid Build Coastguard Worker             getDevice().pushFile(tmpFlagFile, devicePath);
173*6777b538SAndroid Build Coastguard Worker         } catch (IOException e) {
174*6777b538SAndroid Build Coastguard Worker             throw new FailedChromiumGTestException(
175*6777b538SAndroid Build Coastguard Worker                     "Failed to create temp file for gtest filter flag on the device.", e);
176*6777b538SAndroid Build Coastguard Worker         } finally {
177*6777b538SAndroid Build Coastguard Worker             FileUtil.deleteFile(tmpFlagFile);
178*6777b538SAndroid Build Coastguard Worker         }
179*6777b538SAndroid Build Coastguard Worker         return devicePath;
180*6777b538SAndroid Build Coastguard Worker     }
181*6777b538SAndroid Build Coastguard Worker 
182*6777b538SAndroid Build Coastguard Worker     @NonNull
getAllGTestFlags()183*6777b538SAndroid Build Coastguard Worker     private String getAllGTestFlags() throws DeviceNotAvailableException {
184*6777b538SAndroid Build Coastguard Worker         String flags = String.format("%s %s", GTEST_FLAG_PRINT_TIME, getGTestFilters());
185*6777b538SAndroid Build Coastguard Worker         if (isCollectTestsOnly()) {
186*6777b538SAndroid Build Coastguard Worker             flags = String.format("%s %s", flags, GTEST_FLAG_LIST_TESTS);
187*6777b538SAndroid Build Coastguard Worker         }
188*6777b538SAndroid Build Coastguard Worker         return flags;
189*6777b538SAndroid Build Coastguard Worker     }
190*6777b538SAndroid Build Coastguard Worker 
191*6777b538SAndroid Build Coastguard Worker     /**
192*6777b538SAndroid Build Coastguard Worker      * The flags all exist in Chromium's instrumentation APK
193*6777b538SAndroid Build Coastguard Worker      * {@link org.chromium.build.gtest_apk.NativeTestInstrumentationTestRunner} and
194*6777b538SAndroid Build Coastguard Worker      * {@link org.chromium.native_test.NativeTest}.
195*6777b538SAndroid Build Coastguard Worker      *
196*6777b538SAndroid Build Coastguard Worker      * The following is a brief explanation for each flag
197*6777b538SAndroid Build Coastguard Worker      * <ul>
198*6777b538SAndroid Build Coastguard Worker      * <li> NATIVE_TEST_ACTIVITY_KEY: Indicates the name of the activity which should be
199*6777b538SAndroid Build Coastguard Worker      * started by the instrumentation APK. This activity is responsible for executing gtests.
200*6777b538SAndroid Build Coastguard Worker      * <li> RUN_IN_SUBTHREAD_KEY: Whether to run the tests in the main-thread or a sub-thread.
201*6777b538SAndroid Build Coastguard Worker      * <li> EXTRA_SHARD_NANO_TIMEOUT_KEY: Shard timeout (Equal to the test timeout and not
202*6777b538SAndroid Build Coastguard Worker      * important as we only use a single shard).
203*6777b538SAndroid Build Coastguard Worker      * <li> LIBRARY_TO_LOAD_ACTIVITY_KEY: Name of the native library which has the code under
204*6777b538SAndroid Build Coastguard Worker      * test. System.LoadLibrary will be invoked on the value of this flag
205*6777b538SAndroid Build Coastguard Worker      * <li> STDOUT_FILE_KEY: Path to the file where stdout/stderr will be redirected to.</li>
206*6777b538SAndroid Build Coastguard Worker      * <li> COMMAND_LINE_FLAGS_KEY: Command line flags delegated to the gtest executor. This is
207*6777b538SAndroid Build Coastguard Worker      * mostly used for gtest flags
208*6777b538SAndroid Build Coastguard Worker      * <li> DUMP_COVERAGE_KEY: Flag used to indicate that the apk should not exit before dumping
209*6777b538SAndroid Build Coastguard Worker      * native coverage.
210*6777b538SAndroid Build Coastguard Worker      * </ul>
211*6777b538SAndroid Build Coastguard Worker      *
212*6777b538SAndroid Build Coastguard Worker      * @param resultFilePath path to a temporary file on the device which the gtest result will be
213*6777b538SAndroid Build Coastguard Worker      *                       directed to
214*6777b538SAndroid Build Coastguard Worker      * @return an instrumentation command that can be executed using adb shell am instrument.
215*6777b538SAndroid Build Coastguard Worker      */
216*6777b538SAndroid Build Coastguard Worker     @NonNull
createRunAllTestsCommand(@onNull String resultFilePath)217*6777b538SAndroid Build Coastguard Worker     private String createRunAllTestsCommand(@NonNull String resultFilePath)
218*6777b538SAndroid Build Coastguard Worker             throws DeviceNotAvailableException {
219*6777b538SAndroid Build Coastguard Worker         InstrumentationCommandBuilder builder = new InstrumentationCommandBuilder(TEST_RUNNER)
220*6777b538SAndroid Build Coastguard Worker                 .addArgument(NATIVE_TEST_ACTIVITY_KEY, NATIVE_UNIT_TEST_ACTIVITY_KEY)
221*6777b538SAndroid Build Coastguard Worker                 .addArgument(RUN_IN_SUBTHREAD_KEY, "1")
222*6777b538SAndroid Build Coastguard Worker                 .addArgument(EXTRA_SHARD_NANO_TIMEOUT_KEY, String.valueOf(TESTS_TIMEOUT.toNanos()))
223*6777b538SAndroid Build Coastguard Worker                 .addArgument(LIBRARY_TO_LOAD_ACTIVITY_KEY, libraryToLoad)
224*6777b538SAndroid Build Coastguard Worker                 .addArgument(STDOUT_FILE_KEY, resultFilePath)
225*6777b538SAndroid Build Coastguard Worker                 .addArgument(COMMAND_LINE_FLAGS_KEY,
226*6777b538SAndroid Build Coastguard Worker                         String.format("'%s'", getAllGTestFlags()));
227*6777b538SAndroid Build Coastguard Worker         if (isCoverageEnabled) {
228*6777b538SAndroid Build Coastguard Worker             builder.addArgument(DUMP_COVERAGE_KEY, "true");
229*6777b538SAndroid Build Coastguard Worker         }
230*6777b538SAndroid Build Coastguard Worker         return builder.build();
231*6777b538SAndroid Build Coastguard Worker     }
232*6777b538SAndroid Build Coastguard Worker 
233*6777b538SAndroid Build Coastguard Worker     /**
234*6777b538SAndroid Build Coastguard Worker      * Those logs can be found in host_log_%s.txt which is bundled with test execution.
235*6777b538SAndroid Build Coastguard Worker      *
236*6777b538SAndroid Build Coastguard Worker      * @param cmd Command used to instrumentation, this has all the flags which can help debugging
237*6777b538SAndroid Build Coastguard Worker      *            unusual behaviour.
238*6777b538SAndroid Build Coastguard Worker      */
printHostLogs(@onNull String cmd)239*6777b538SAndroid Build Coastguard Worker     private void printHostLogs(@NonNull String cmd) {
240*6777b538SAndroid Build Coastguard Worker         LogUtil.CLog.i(String.format("[Cronet] Library to be loaded: %s\n", libraryToLoad));
241*6777b538SAndroid Build Coastguard Worker         LogUtil.CLog.i(String.format("[Cronet] Command used to run gtests: adb shell %s\n", cmd));
242*6777b538SAndroid Build Coastguard Worker         LogUtil.CLog.i(String.format("[Cronet] Native-Coverage = %b", isCoverageEnabled));
243*6777b538SAndroid Build Coastguard Worker     }
244*6777b538SAndroid Build Coastguard Worker 
245*6777b538SAndroid Build Coastguard Worker     /**
246*6777b538SAndroid Build Coastguard Worker      * This is automatically invoked by the {@link com.android.tradefed.testtype.HostTest}.
247*6777b538SAndroid Build Coastguard Worker      *
248*6777b538SAndroid Build Coastguard Worker      * @param testInfo The {@link TestInformation} object containing useful information to run
249*6777b538SAndroid Build Coastguard Worker      *                 tests.
250*6777b538SAndroid Build Coastguard Worker      * @param listener the {@link ITestInvocationListener} of test results
251*6777b538SAndroid Build Coastguard Worker      */
252*6777b538SAndroid Build Coastguard Worker     @Override
run(TestInformation testInfo, ITestInvocationListener listener)253*6777b538SAndroid Build Coastguard Worker     public void run(TestInformation testInfo, ITestInvocationListener listener)
254*6777b538SAndroid Build Coastguard Worker             throws DeviceNotAvailableException {
255*6777b538SAndroid Build Coastguard Worker         if (Strings.isNullOrEmpty(libraryToLoad)) {
256*6777b538SAndroid Build Coastguard Worker             throw new IllegalStateException("No library provided to be loaded.");
257*6777b538SAndroid Build Coastguard Worker         }
258*6777b538SAndroid Build Coastguard Worker         String resultFilePath = createTempResultFileOnDevice();
259*6777b538SAndroid Build Coastguard Worker         String cmd = createRunAllTestsCommand(resultFilePath);
260*6777b538SAndroid Build Coastguard Worker         printHostLogs(cmd);
261*6777b538SAndroid Build Coastguard Worker         getDevice().executeShellCommand(CLEAR_CLANG_COVERAGE_FILES);
262*6777b538SAndroid Build Coastguard Worker         ITestInvocationListener listenerWithTime = new TestListenerWithTime(
263*6777b538SAndroid Build Coastguard Worker                 System.currentTimeMillis(), listener);
264*6777b538SAndroid Build Coastguard Worker         getDevice().executeShellCommand(cmd, new CollectingOutputReceiver(),
265*6777b538SAndroid Build Coastguard Worker                 /* maxTimeBeforeTimeOut */ TESTS_TIMEOUT.toMinutes(),
266*6777b538SAndroid Build Coastguard Worker                 /* timeUnit */ TimeUnit.MINUTES,
267*6777b538SAndroid Build Coastguard Worker                 /* retryAttempts */ 1);
268*6777b538SAndroid Build Coastguard Worker         parseAndReport(resultFilePath, listenerWithTime);
269*6777b538SAndroid Build Coastguard Worker     }
270*6777b538SAndroid Build Coastguard Worker 
parseAndReport(@onNull String resultFilePath, @NonNull ITestInvocationListener listener)271*6777b538SAndroid Build Coastguard Worker     private void parseAndReport(@NonNull String resultFilePath,
272*6777b538SAndroid Build Coastguard Worker             @NonNull ITestInvocationListener listener) throws DeviceNotAvailableException {
273*6777b538SAndroid Build Coastguard Worker         File resultFile = device.pullFile(resultFilePath);
274*6777b538SAndroid Build Coastguard Worker         if (resultFile == null) {
275*6777b538SAndroid Build Coastguard Worker             throw new FailedChromiumGTestException(
276*6777b538SAndroid Build Coastguard Worker                     "Failed to retrieve gtest results file from device.");
277*6777b538SAndroid Build Coastguard Worker         }
278*6777b538SAndroid Build Coastguard Worker         try (FileInputStreamSource data = new FileInputStreamSource(resultFile)) {
279*6777b538SAndroid Build Coastguard Worker             listener.testLog(
280*6777b538SAndroid Build Coastguard Worker                 String.format("gtest_output_%s", resultFile.getName()), LogDataType.TEXT, data);
281*6777b538SAndroid Build Coastguard Worker         }
282*6777b538SAndroid Build Coastguard Worker         // Loading all the lines is fine since this is done on the host-machine.
283*6777b538SAndroid Build Coastguard Worker         String[] lines;
284*6777b538SAndroid Build Coastguard Worker         try {
285*6777b538SAndroid Build Coastguard Worker             lines = Files.readAllLines(resultFile.toPath()).toArray(String[]::new);
286*6777b538SAndroid Build Coastguard Worker         } catch (IOException e) {
287*6777b538SAndroid Build Coastguard Worker             throw new FailedChromiumGTestException(
288*6777b538SAndroid Build Coastguard Worker                     "Failed to read gtest results file on host machine.", e);
289*6777b538SAndroid Build Coastguard Worker         }
290*6777b538SAndroid Build Coastguard Worker         MultiLineReceiver parser;
291*6777b538SAndroid Build Coastguard Worker         // the parser automatically reports the test result back to the infra through the listener.
292*6777b538SAndroid Build Coastguard Worker         if (isCollectTestsOnly()) {
293*6777b538SAndroid Build Coastguard Worker             parser = new GTestListTestParser(libraryToLoad, listener);
294*6777b538SAndroid Build Coastguard Worker         } else {
295*6777b538SAndroid Build Coastguard Worker             parser = new GTestResultParser(libraryToLoad, listener);
296*6777b538SAndroid Build Coastguard Worker         }
297*6777b538SAndroid Build Coastguard Worker         parser.processNewLines(lines);
298*6777b538SAndroid Build Coastguard Worker         parser.done();
299*6777b538SAndroid Build Coastguard Worker     }
300*6777b538SAndroid Build Coastguard Worker 
301*6777b538SAndroid Build Coastguard Worker     // ------- Everything below is called by HostTest and should not be invoked manually -----
isCollectTestsOnly()302*6777b538SAndroid Build Coastguard Worker     public boolean isCollectTestsOnly() {
303*6777b538SAndroid Build Coastguard Worker         return collectTestsOnly;
304*6777b538SAndroid Build Coastguard Worker     }
305*6777b538SAndroid Build Coastguard Worker 
306*6777b538SAndroid Build Coastguard Worker     @Override
setCollectTestsOnly(boolean shouldCollectTest)307*6777b538SAndroid Build Coastguard Worker     public void setCollectTestsOnly(boolean shouldCollectTest) {
308*6777b538SAndroid Build Coastguard Worker         collectTestsOnly = shouldCollectTest;
309*6777b538SAndroid Build Coastguard Worker     }
310*6777b538SAndroid Build Coastguard Worker 
cleanFilter(String filter)311*6777b538SAndroid Build Coastguard Worker     public String cleanFilter(String filter) {
312*6777b538SAndroid Build Coastguard Worker         return filter.replace('#', '.');
313*6777b538SAndroid Build Coastguard Worker     }
314*6777b538SAndroid Build Coastguard Worker 
315*6777b538SAndroid Build Coastguard Worker     @Override
addIncludeFilter(String filter)316*6777b538SAndroid Build Coastguard Worker     public void addIncludeFilter(String filter) {
317*6777b538SAndroid Build Coastguard Worker         includeFilters.add(cleanFilter(filter));
318*6777b538SAndroid Build Coastguard Worker     }
319*6777b538SAndroid Build Coastguard Worker 
320*6777b538SAndroid Build Coastguard Worker     @Override
addAllIncludeFilters(Set<String> filters)321*6777b538SAndroid Build Coastguard Worker     public void addAllIncludeFilters(Set<String> filters) {
322*6777b538SAndroid Build Coastguard Worker         for (String filter : filters) {
323*6777b538SAndroid Build Coastguard Worker             includeFilters.add(cleanFilter(filter));
324*6777b538SAndroid Build Coastguard Worker         }
325*6777b538SAndroid Build Coastguard Worker     }
326*6777b538SAndroid Build Coastguard Worker 
327*6777b538SAndroid Build Coastguard Worker     @Override
addExcludeFilter(String filter)328*6777b538SAndroid Build Coastguard Worker     public void addExcludeFilter(String filter) {
329*6777b538SAndroid Build Coastguard Worker         excludeFilters.add(cleanFilter(filter));
330*6777b538SAndroid Build Coastguard Worker     }
331*6777b538SAndroid Build Coastguard Worker 
332*6777b538SAndroid Build Coastguard Worker     @Override
addAllExcludeFilters(Set<String> filters)333*6777b538SAndroid Build Coastguard Worker     public void addAllExcludeFilters(Set<String> filters) {
334*6777b538SAndroid Build Coastguard Worker         for (String filter : filters) {
335*6777b538SAndroid Build Coastguard Worker             excludeFilters.add(cleanFilter(filter));
336*6777b538SAndroid Build Coastguard Worker         }
337*6777b538SAndroid Build Coastguard Worker     }
338*6777b538SAndroid Build Coastguard Worker 
339*6777b538SAndroid Build Coastguard Worker     @Override
clearIncludeFilters()340*6777b538SAndroid Build Coastguard Worker     public void clearIncludeFilters() {
341*6777b538SAndroid Build Coastguard Worker         includeFilters.clear();
342*6777b538SAndroid Build Coastguard Worker     }
343*6777b538SAndroid Build Coastguard Worker 
344*6777b538SAndroid Build Coastguard Worker     @Override
getIncludeFilters()345*6777b538SAndroid Build Coastguard Worker     public Set<String> getIncludeFilters() {
346*6777b538SAndroid Build Coastguard Worker         return includeFilters;
347*6777b538SAndroid Build Coastguard Worker     }
348*6777b538SAndroid Build Coastguard Worker 
349*6777b538SAndroid Build Coastguard Worker     @Override
getExcludeFilters()350*6777b538SAndroid Build Coastguard Worker     public Set<String> getExcludeFilters() {
351*6777b538SAndroid Build Coastguard Worker         return excludeFilters;
352*6777b538SAndroid Build Coastguard Worker     }
353*6777b538SAndroid Build Coastguard Worker 
354*6777b538SAndroid Build Coastguard Worker     @Override
clearExcludeFilters()355*6777b538SAndroid Build Coastguard Worker     public void clearExcludeFilters() {
356*6777b538SAndroid Build Coastguard Worker         excludeFilters.clear();
357*6777b538SAndroid Build Coastguard Worker     }
358*6777b538SAndroid Build Coastguard Worker 
359*6777b538SAndroid Build Coastguard Worker     @Override
getDevice()360*6777b538SAndroid Build Coastguard Worker     public ITestDevice getDevice() {
361*6777b538SAndroid Build Coastguard Worker         return device;
362*6777b538SAndroid Build Coastguard Worker     }
363*6777b538SAndroid Build Coastguard Worker 
364*6777b538SAndroid Build Coastguard Worker     @Override
setDevice(ITestDevice device)365*6777b538SAndroid Build Coastguard Worker     public void setDevice(ITestDevice device) {
366*6777b538SAndroid Build Coastguard Worker         this.device = device;
367*6777b538SAndroid Build Coastguard Worker     }
368*6777b538SAndroid Build Coastguard Worker }