• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.testtype;
18 
19 import com.android.ddmlib.testrunner.ITestRunListener;
20 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
21 import com.android.tradefed.build.IBuildInfo;
22 import com.android.tradefed.config.Option;
23 import com.android.tradefed.config.OptionClass;
24 import com.android.tradefed.device.DeviceNotAvailableException;
25 import com.android.tradefed.device.ITestDevice;
26 import com.android.tradefed.log.LogUtil.CLog;
27 import com.android.ddmlib.Log.LogLevel;
28 import com.android.tradefed.result.ITestInvocationListener;
29 import com.android.tradefed.util.ArrayUtil;
30 import com.android.tradefed.util.CommandResult;
31 import com.android.tradefed.util.CommandStatus;
32 import com.android.tradefed.util.FileUtil;
33 import com.android.tradefed.util.StreamUtil;
34 import com.android.tradefed.util.JsonUtil;
35 import com.android.tradefed.util.IRunUtil;
36 import com.android.tradefed.util.RunUtil;
37 import com.android.tradefed.util.VtsDashboardUtil;
38 import com.android.tradefed.util.VtsVendorConfigFileUtil;
39 import com.android.tradefed.testtype.IAbi;
40 
41 import org.json.JSONArray;
42 import org.json.JSONObject;
43 import org.json.JSONException;
44 
45 import java.io.FileReader;
46 import java.io.BufferedReader;
47 import java.io.BufferedWriter;
48 
49 import java.io.File;
50 import java.io.FileNotFoundException;
51 import java.io.FileWriter;
52 import java.io.IOException;
53 import java.io.InputStream;
54 import java.io.PrintWriter;
55 import java.nio.file.Paths;
56 import java.util.TreeSet;
57 import java.util.Set;
58 import java.util.Collection;
59 import java.util.ArrayList;
60 
61 /**
62  * A Test that runs a vts multi device test package (part of Vendor Test Suite,
63  * VTS) on given device.
64  */
65 
66 @OptionClass(alias = "vtsmultidevicetest")
67 public class VtsMultiDeviceTest implements IDeviceTest, IRemoteTest, ITestFilterReceiver,
68 IRuntimeHintProvider, ITestCollector, IBuildReceiver, IAbiReceiver {
69 
70     static final String ANDROIDDEVICE = "AndroidDevice";
71     static final String BUILD = "build";
72     static final String BUILD_ID = "build_id";
73     static final String BUILD_TARGET = "build_target";
74     static final String COVERAGE_PROPERTY = "ro.vts.coverage";
75     static final String DATA_FILE_PATH = "data_file_path";
76     static final String LOG_PATH = "log_path";
77     static final String NAME = "name";
78     static final String OS_NAME = "os.name";
79     static final String WINDOWS = "Windows";
80     static final String PYTHONPATH = "PYTHONPATH";
81     static final String SERIAL = "serial";
82     static final String TESTMODULE = "TestModule";
83     static final String TEST_PLAN_REPORT_FILE = "TEST_PLAN_REPORT_FILE";
84     static final String TEST_SUITE = "test_suite";
85     static final String TEST_MAX_TIMEOUT = "test_max_timeout";
86     static final String VIRTUAL_ENV_PATH = "VIRTUALENVPATH";
87     static final String ABI_NAME = "abi_name";
88     static final String ABI_BITNESS = "abi_bitness";
89     static final String SKIP_ON_32BIT_ABI = "skip_on_32bit_abi";
90     static final String SKIP_ON_64BIT_ABI = "skip_on_64bit_abi";
91     static final String SKIP_IF_THERMAL_THROTTLING = "skip_if_thermal_throttling";
92     static final String RUN_32BIT_ON_64BIT_ABI = "run_32bit_on_64bit_abi";
93     static final String VTS = "vts";
94     static final String CONFIG_FILE_EXTENSION = ".config";
95     static final String INCLUDE_FILTER = "include_filter";
96     static final String EXCLUDE_FILTER = "exclude_filter";
97     static final String EXCLUDE_OVER_INCLUDE = "exclude_over_include";
98     static final String BINARY_TEST_SOURCE = "binary_test_source";
99     static final String BINARY_TEST_WORKING_DIRECTORY = "binary_test_working_directory";
100     static final String BINARY_TEST_ENVP = "binary_test_envp";
101     static final String BINARY_TEST_ARGS = "binary_test_args";
102     static final String BINARY_TEST_LD_LIBRARY_PATH = "binary_test_ld_library_path";
103     static final String BINARY_TEST_PROFILING_LIBRARY_PATH = "binary_test_profiling_library_path";
104     static final String BINARY_TEST_DISABLE_FRAMEWORK = "binary_test_disable_framework";
105     static final String BINARY_TEST_STOP_NATIVE_SERVERS = "binary_test_stop_native_servers";
106     static final String BINARY_TEST_TYPE_GTEST = "gtest";
107     static final String BINARY_TEST_TYPE_LLVMFUZZER = "llvmfuzzer";
108     static final String BINARY_TEST_TYPE_HAL_HIDL_GTEST = "hal_hidl_gtest";
109     static final String BINARY_TEST_TYPE_HAL_HIDL_REPLAY_TEST = "hal_hidl_replay_test";
110     static final String BINARY_TEST_TYPE_HOST_BINARY_TEST = "host_binary_test";
111     static final String BUG_REPORT_ON_FAILURE = "bug_report_on_failure";
112     static final String ENABLE_COVERAGE = "enable_coverage";
113     static final String ENABLE_PROFILING = "enable_profiling";
114     static final String GTEST_BATCH_MODE = "gtest_batch_mode";
115     static final String SAVE_TRACE_FIEL_REMOTE = "save_trace_file_remote";
116     static final String OUTPUT_COVERAGE_REPORT = "output_coverage_report";
117     static final String GLOBAL_COVERAGE = "global_coverage";
118     static final String LTP_NUMBER_OF_THREADS = "ltp_number_of_threads";
119     static final String NATIVE_SERVER_PROCESS_NAME = "native_server_process_name";
120     static final String PASSTHROUGH_MODE = "passthrough_mode";
121     static final String PRECONDITION_HWBINDER_SERVICE = "precondition_hwbinder_service";
122     static final String PRECONDITION_FEATURE = "precondition_feature";
123     static final String PRECONDITION_FILE_PATH_PREFIX = "precondition_file_path_prefix";
124     static final String PRECONDITION_LSHAL = "precondition_lshal";
125     static final String PRECONDITION_VINTF = "precondition_vintf";
126     static final String ENABLE_SYSTRACE = "enable_systrace";
127     static final String HAL_HIDL_REPLAY_TEST_TRACE_PATHS = "hal_hidl_replay_test_trace_paths";
128     static final String HAL_HIDL_PACKAGE_NAME = "hal_hidl_package_name";
129     static final String REPORT_MESSAGE_FILE_NAME = "report_proto.msg";
130     static final String SYSTRACE_PROCESS_NAME = "systrace_process_name";
131     static final String TEMPLATE_BINARY_TEST_PATH = "vts/testcases/template/binary_test/binary_test";
132     static final String TEMPLATE_GTEST_BINARY_TEST_PATH = "vts/testcases/template/gtest_binary_test/gtest_binary_test";
133     static final String TEMPLATE_LLVMFUZZER_TEST_PATH = "vts/testcases/template/llvmfuzzer_test/llvmfuzzer_test";
134     static final String TEMPLATE_HAL_HIDL_GTEST_PATH = "vts/testcases/template/hal_hidl_gtest/hal_hidl_gtest";
135     static final String TEMPLATE_HAL_HIDL_REPLAY_TEST_PATH = "vts/testcases/template/hal_hidl_replay_test/hal_hidl_replay_test";
136     static final String TEMPLATE_HOST_BINARY_TEST_PATH = "vts/testcases/template/host_binary_test/host_binary_test";
137     static final long TEST_ABORT_TIMEOUT_MSECS = 1000 * 15;
138     static final String TEST_RUN_SUMMARY_FILE_NAME = "test_run_summary.json";
139     static final float DEFAULT_TARGET_VERSION = -1;
140     static final String DEFAULT_TESTCASE_CONFIG_PATH = "vts/tools/vts-tradefed/res/default/DefaultTestCase.config";
141 
142     private ITestDevice mDevice = null;
143     private IAbi mAbi = null;
144 
145     @Option(name = "test-timeout",
146             description = "The amount of time (in milliseconds) for a test invocation. "
147                     + "If the test cannot finish before timeout, it should interrupt itself and "
148                     + "clean up in " + TEST_ABORT_TIMEOUT_MSECS + "ms. Hence the actual timeout "
149                     + "is the specified value + " + TEST_ABORT_TIMEOUT_MSECS + "ms.",
150             isTimeVal = true)
151     private long mTestTimeout = 1000 * 60 * 60 * 3;
152 
153     @Option(name = "test-module-name",
154         description = "The name for a test module.")
155     private String mTestModuleName = null;
156 
157     @Option(name = "test-case-path",
158             description = "The path for test case.")
159     private String mTestCasePath = null;
160 
161     @Option(name = "test-config-path",
162             description = "The path for test case config file.")
163     private String mTestConfigPath = null;
164 
165     @Option(name = "precondition-hwbinder-service",
166             description = "The name of a HW binder service needed to run the test.")
167     private String mPreconditionHwBinderServiceName = null;
168 
169     @Option(name = "precondition-feature",
170         description = "The name of a `pm`-listable feature needed to run the test.")
171     private String mPreconditionFeature = null;
172 
173     @Option(name = "precondition-file-path-prefix",
174         description = "The path prefix of a file (e.g., shared lib) needed to run the test.")
175     private String mPreconditionFilePathPrefix = null;
176 
177     @Option(name = "precondition-lshal",
178         description = "The name of a `lshal`-listable feature needed to run the test.")
179     private String mPreconditionLshal = null;
180 
181     @Option(name = "precondition-vintf",
182             description = "The full name of a HAL specified in vendor/manifest.xml and "
183                     + "needed to run the test (e.g., android.hardware.graphics.mapper@2.0). "
184                     + "this can override precondition-lshal option.")
185     private String mPreconditionVintf = null;
186 
187     @Option(name = "precondition-vintf-override",
188             description = "If precondition-lshal is present and precondition-vintf is not, "
189                     + "set precondition-vintf to the value of precondition-lshal. "
190                     + "The test runner will find the HAL in manifest.xml instead of lshal.")
191     private boolean mPreconditionVintfOverride = false;
192 
193     @Option(name = "use-stdout-logs",
194             description = "Flag that determines whether to use std:out to parse output.")
195     private boolean mUseStdoutLogs = false;
196 
197     @Option(name = "include-filter",
198             description = "The positive filter of the test names to run.")
199     private Set<String> mIncludeFilters = new TreeSet<>();
200 
201     @Option(name = "exclude-filter",
202             description = "The negative filter of the test names to run.")
203     private Set<String> mExcludeFilters = new TreeSet<>();
204 
205     @Option(name = "exclude-over-include",
206             description = "The negative filter of the test names to run.")
207     private boolean mExcludeOverInclude = false;
208 
209     @Option(name = "runtime-hint", description = "The hint about the test's runtime.",
210             isTimeVal = true)
211     private long mRuntimeHint = 60000;  // 1 minute
212 
213     @Option(name = "enable-profiling", description = "Enable profiling for the tests.")
214     private boolean mEnableProfiling = false;
215 
216     @Option(name = "save-trace-file-remote",
217             description = "Whether to save the trace file in remote storage.")
218     private boolean mSaveTraceFileRemote = false;
219 
220     @Option(name = "enable-systrace", description = "Enable systrace for the tests.")
221     private boolean mEnableSystrace = false;
222 
223     @Option(name = "enable-coverage",
224             description = "Enable coverage for the tests. In order for coverage to be measured, " +
225                           "ro.vts.coverage system must have value \"1\" to indicate the target " +
226                           "build is coverage instrumented.")
227     private boolean mEnableCoverage = true;
228 
229     @Option(name = "global-coverage", description = "True to measure coverage for entire test, "
230                     + "measure coverage for each test case otherwise. Currently, only global "
231                     + "coverage is supported for binary tests")
232     private boolean mGlobalCoverage = true;
233 
234     @Option(name = "output-coverage-report", description = "Whether to store raw coverage report.")
235     private boolean mOutputCoverageReport = false;
236 
237     // Another design option is to parse a string or use enum for host preference on BINDER,
238     // PASSTHROUGH and DEFAULT(which is BINDER). Also in the future, we might want to deal with
239     // the case of target preference on PASSTHROUGH (if host does not specify to use BINDER mode).
240     @Option(name = "passthrough-mode", description = "Set getStub to use passthrough mode. "
241         + "Value true means use passthrough mode if available; false for binderized mode if "
242         + "available. Default is false")
243     private boolean mPassthroughMode = false;
244 
245     @Option(name = "ltp-number-of-threads",
246             description = "Number of threads to run the LTP test cases. "
247                     + "0 means using number of avaiable CPU threads.")
248     private int mLtpNumberOfThreads = -1;
249 
250     @Option(name = "skip-on-32bit-abi",
251         description = "Whether to skip tests on 32bit ABI.")
252     private boolean mSkipOn32BitAbi = false;
253 
254     @Option(name = "skip-on-64bit-abi",
255         description = "Whether to skip tests on 64bit ABI.")
256     private boolean mSkipOn64BitAbi = false;
257 
258     @Option(name = "skip-if-thermal-throttling",
259             description = "Whether to skip tests if target device suffers from thermal throttling.")
260     private boolean mSkipIfThermalThrottling = false;
261 
262     @Option(name = "run-32bit-on-64bit-abi",
263             description = "Whether to run 32bit tests on 64bit ABI.")
264     private boolean mRun32bBitOn64BitAbi = false;
265 
266     @Option(name = "binary-test-source",
267             description = "Binary test source paths relative to vts testcase directory on host."
268                     + "Format of tags:"
269                     + "    <source>: source without tag."
270                     + "    <tag>::<source>: source with tag. Can be used to separate 32bit and 64"
271                     + "            bit tests with same file name."
272                     + "    <tag1>::<source1>, <tag1>::<source2>, <tag2>::<source3>: multiple"
273                     + "            sources connected by comma. White spaces in-between"
274                     + "            will be ignored."
275                     + "Format of each source string:"
276                     + "    <source file>: push file and create test case."
277                     + "            Source is relative path of source file under vts's testcases"
278                     + "            folder. Source file will be pushed to working directory"
279                     + "            discarding original directory structure, and test cases will"
280                     + "            be created using the pushed file."
281                     + "    <source file>-><destination file>: push file and create test case."
282                     + "            Destination path is absolute path on device. Test cases will"
283                     + "            be created using the pushed file."
284                     + "    <source file>->: push file only."
285                     + "            Push the source file to its' tag's corresponding"
286                     + "            working directory. Test case will not be created on"
287                     + "            this file. This is equivalent to but simpler than specifying a"
288                     + "            working directory for the tag and use VtsFilePusher to push the"
289                     + "            file to the directory."
290                     + "    -><destination file>: create test case only."
291                     + "            Destination is absolute device side path."
292                     + "    Note: each path in source string can be a directory. However, the"
293                     + "          default binary test runner and gtest binary test runner does not"
294                     + "          support creating test cases from a directory. You will need to"
295                     + "          override the binary test runner's CreateTestCase method in python."
296                     + "    If you wish to push a source file to a specific destination and not"
297                     + "    create a test case from it, please use VtsFilePusher.")
298     private Collection<String> mBinaryTestSource = new ArrayList<>();
299 
300     @Option(name = "binary-test-working-directory", description = "Working directories for binary "
301                     + "tests. Tags can be added to the front of each directory using '::' as delimiter. "
302                     + "However, each tag should only has one working directory. This option is optional for "
303                     + "binary tests. If not specified, different directories will be used for files with "
304                     + "different tags.")
305     private Collection<String> mBinaryTestWorkingDirectory = new ArrayList<>();
306 
307     @Option(name = "binary-test-envp", description = "Additional environment path for binary "
308         + "tests. Tags can be added to the front of each directory using '::' as delimiter. "
309         + "There can be multiple instances of binary-test-envp for a same tag, which will "
310         + "later automatically be combined.")
311     private Collection<String> mBinaryTestEnvp = new ArrayList<>();
312 
313     @Option(name = "binary-test-args", description = "Additional args or flags for binary "
314         + "tests. Tags can be added to the front of each directory using '::' as delimiter. "
315         + "There can be multiple instances of binary-test-args for a same tag, which will "
316         + "later automatically be combined.")
317     private Collection<String> mBinaryTestArgs = new ArrayList<>();
318 
319     @Option(name = "binary-test-ld-library-path", description = "LD_LIBRARY_PATH for binary "
320                     + "tests. Tags can be added to the front of each instance using '::' as delimiter. "
321                     + "Multiple directories can be added under a same tag using ':' as delimiter. "
322                     + "There can be multiple instances of ld-library-path for a same tag, which will "
323                     + "later automatically be combined using ':' as delimiter. Paths without a tag "
324                     + "will only used for binaries without tag. This option is optional for binary tests.")
325     private Collection<String> mBinaryTestLdLibraryPath = new ArrayList<>();
326 
327     @Option(name = "binary-test-profiling-library-path", description = "Path to lookup and load "
328             + "profiling libraries for tests with profiling enabled. Tags can be added to the "
329             + "front of each directory using '::' as delimiter. Only one directory could be "
330             + "specified for the same tag. This option is optional for binary tests. If not "
331             + "specified, default directories will be used for files with different tags.")
332     private Collection<String> mBinaryTestProfilingLibraryPath = new ArrayList<>();
333 
334     @Option(name = "binary-test-disable-framework", description = "Adb stop/start before/after test.")
335     private boolean mBinaryTestDisableFramework = false;
336 
337     @Option(name = "binary-test-stop-native-servers",
338             description = "Set to stop all properly configured native servers during the testing.")
339     private boolean mBinaryTestStopNativeServers = false;
340 
341     @Option(name = "bug-report-on-failure",
342             description = "To catch bugreport zip file at the end of failed test cases. "
343                     + "If set to true, a report will be caught through adh shell command at the end of each failed "
344                     + "test cases.")
345     private boolean mBugReportOnFailure = false;
346 
347     @Option(name = "native-server-process-name",
348             description = "Name of a native server process. The runner checks to make sure "
349                     + "each specified native server process is not running after the framework stop.")
350     private Collection<String> mNativeServerProcessName = new ArrayList<>();
351 
352     @Option(name = "binary-test-type", description = "Binary test type. Only specify this when "
353             + "running an extended binary test without a python test file. Available options: gtest")
354     private String mBinaryTestType = "";
355 
356     @Option(name = "hal-hidl-replay-test-trace-path", description = "The path of a trace file to replay.")
357     private Collection<String> mHalHidlReplayTestTracePaths = new ArrayList<>();
358 
359     @Option(name = "hal-hidl-package-name", description = "The name of a target HIDL HAL package "
360             + "e.g., 'android.hardware.light@2.0'.")
361     private String mHalHidlPackageName = null;
362 
363     @Option(name = "systrace-process-name", description = "Process name for systrace.")
364     private String mSystraceProcessName = null;
365 
366     @Option(name = "collect-tests-only",
367             description = "Only invoke the test binary to collect list of applicable test cases. "
368                     + "All test run callbacks will be triggered, but test execution will "
369                     + "not be actually carried out.")
370     private boolean mCollectTestsOnly = false;
371 
372     @Option(name = "gtest-batch-mode", description = "Run Gtest binaries in batch mode.")
373     private boolean mGtestBatchMode = false;
374 
375     // This variable is set in order to include the directory that contains the
376     // python test cases. This is set before calling the method.
377     // {@link #doRunTest(IRunUtil, String, String)}.
378     public String mPythonPath = null;
379 
380     @Option(name = "python-binary", description = "python binary to use "
381             + "(optional)")
382     private String mPythonBin = null;
383     private IRunUtil mRunUtil = null;
384     private IBuildInfo mBuildInfo = null;
385     private String mRunName = "VtsHostDrivenTest";
386     // the path of a dir which contains the test data files.
387     private String mTestCaseDataDir = "./";
388 
389     private VtsVendorConfigFileUtil configReader = null;
390 
391     /**
392      * @return the mRunUtil
393      */
getRunUtil()394     public IRunUtil getRunUtil() {
395         return mRunUtil;
396     }
397 
398     /**
399      * @param mRunUtil the mRunUtil to set
400      */
setRunUtil(IRunUtil mRunUtil)401     public void setRunUtil(IRunUtil mRunUtil) {
402         this.mRunUtil = mRunUtil;
403     }
404 
405     /**
406      * {@inheritDoc}
407      */
408     @Override
setDevice(ITestDevice device)409     public void setDevice(ITestDevice device) {
410         mDevice = device;
411     }
412 
413     /**
414      * {@inheritDoc}
415      */
416     @Override
getDevice()417     public ITestDevice getDevice() {
418         return mDevice;
419     }
420 
setTestCasePath(String path)421     public void setTestCasePath(String path){
422         mTestCasePath = path;
423     }
424 
setTestConfigPath(String path)425     public void setTestConfigPath(String path){
426         mTestConfigPath = path;
427     }
428 
429     /**
430      * {@inheritDoc}
431      */
432     @Override
addIncludeFilter(String filter)433     public void addIncludeFilter(String filter) {
434         mIncludeFilters.add(cleanFilter(filter));
435     }
436 
437     /**
438      * {@inheritDoc}
439      */
440     @Override
addAllIncludeFilters(Set<String> filters)441     public void addAllIncludeFilters(Set<String> filters) {
442         for (String filter : filters) {
443             mIncludeFilters.add(cleanFilter(filter));
444         }
445     }
446 
447     /**
448      * {@inheritDoc}
449      */
450     @Override
addExcludeFilter(String filter)451     public void addExcludeFilter(String filter) {
452         mExcludeFilters.add(cleanFilter(filter));
453     }
454 
455     /**
456      * {@inheritDoc}
457      */
458     @Override
addAllExcludeFilters(Set<String> filters)459     public void addAllExcludeFilters(Set<String> filters) {
460         for (String filter : filters) {
461             mExcludeFilters.add(cleanFilter(filter));
462         }
463     }
464 
465     /*
466      * Conforms filters using a {@link com.android.ddmlib.testrunner.TestIdentifier} format
467      * to be recognized by the GTest executable.
468      */
cleanFilter(String filter)469     private String cleanFilter(String filter) {
470         return filter.replace('#', '.');
471     }
472 
473     /**
474      * {@inheritDoc}
475      */
476     @Override
getRuntimeHint()477     public long getRuntimeHint() {
478         return mRuntimeHint;
479     }
480 
481     /**
482      * {@inheritDoc}
483      */
484     @Override
setCollectTestsOnly(boolean shouldCollectTest)485     public void setCollectTestsOnly(boolean shouldCollectTest) {
486         mCollectTestsOnly = shouldCollectTest;
487     }
488 
489     /**
490      * {@inheritDoc}
491      */
492     @SuppressWarnings("deprecation")
493     @Override
run(ITestInvocationListener listener)494     public void run(ITestInvocationListener listener)
495             throws IllegalArgumentException, DeviceNotAvailableException {
496         if (mDevice == null) {
497             throw new DeviceNotAvailableException("Device has not been set");
498         }
499 
500         if (mTestCasePath == null) {
501             if (!mBinaryTestSource.isEmpty()) {
502                 String template;
503                 switch (mBinaryTestType) {
504                     case BINARY_TEST_TYPE_GTEST:
505                         template = TEMPLATE_GTEST_BINARY_TEST_PATH;
506                         break;
507                     case BINARY_TEST_TYPE_HAL_HIDL_GTEST:
508                         template = TEMPLATE_HAL_HIDL_GTEST_PATH;
509                         break;
510                     case BINARY_TEST_TYPE_HOST_BINARY_TEST:
511                         template = TEMPLATE_HOST_BINARY_TEST_PATH;
512                         break;
513                     default:
514                         template = TEMPLATE_BINARY_TEST_PATH;
515                 }
516                 CLog.i("Using default test case template at %s.", template);
517                 setTestCasePath(template);
518                 if (mEnableCoverage && !mGlobalCoverage) {
519                     CLog.e("Only global coverage is supported for test type %s.", mBinaryTestType);
520                     throw new RuntimeException("Failed to produce VTS runner test config");
521                 }
522             } else if (mBinaryTestType.equals(BINARY_TEST_TYPE_HAL_HIDL_REPLAY_TEST)) {
523                 setTestCasePath(TEMPLATE_HAL_HIDL_REPLAY_TEST_PATH);
524             } else if (mBinaryTestType.equals(BINARY_TEST_TYPE_LLVMFUZZER)) {
525                 // Fuzz test don't need test-case-path.
526                 setTestCasePath(TEMPLATE_LLVMFUZZER_TEST_PATH);
527             } else {
528                 throw new IllegalArgumentException("test-case-path is not set.");
529             }
530         }
531 
532         setPythonPath();
533 
534         if (mPythonBin == null) {
535             mPythonBin = getPythonBinary();
536         }
537 
538         if (mRunUtil == null){
539             mRunUtil = new RunUtil();
540             mRunUtil.setEnvVariable(PYTHONPATH, mPythonPath);
541         }
542 
543         doRunTest(listener);
544     }
545 
546     /**
547      * {@inheritDoc}
548      */
549     @Override
setBuild(IBuildInfo buildInfo)550     public void setBuild(IBuildInfo buildInfo) {
551         mBuildInfo = buildInfo;
552     }
553 
554     /**
555      * Populate a jsonObject with default fields.
556      * This method uses deepMergeJsonObjects method from JsonUtil to merge the default config file with the target
557      * config file. Field already defined in target config file will not be overwritten. Also, JSONArray will not be
558      * deep merged.
559      *
560      * @param jsonObject the target json object to populate
561      * @param testCaseDataDir data file path
562      * @throws IOException
563      * @throws JSONException
564      */
populateDefaultJsonFields(JSONObject jsonObject, String testCaseDataDir)565     private void populateDefaultJsonFields(JSONObject jsonObject, String testCaseDataDir)
566             throws IOException, JSONException {
567         CLog.i("Populating default fields to json object from %s", DEFAULT_TESTCASE_CONFIG_PATH);
568         String content = FileUtil.readStringFromFile(new File(mTestCaseDataDir, DEFAULT_TESTCASE_CONFIG_PATH));
569         JSONObject defaultJsonObject = new JSONObject(content);
570 
571         JsonUtil.deepMergeJsonObjects(jsonObject, defaultJsonObject);
572     }
573 
574     /**
575      * This method reads the provided VTS runner test json config, adds or updates some of its
576      * fields (e.g., to add build info and device serial IDs), and returns the updated json object.
577      * This method calls populateDefaultJsonFields to populate the config JSONObject if the config file is missing
578      * or some required fields is missing from the JSONObject. If test name is not specified, this method
579      * will use the config file's file name file without extension as test name. If config file is missing, this method
580      * will use the test case's class name as test name.
581      *
582      * @param log_path the path of a directory to store the VTS runner logs.
583      * @return the updated JSONObject as the new test config.
584      */
updateVtsRunnerTestConfig(JSONObject jsonObject)585     private void updateVtsRunnerTestConfig(JSONObject jsonObject)
586             throws IOException, JSONException, RuntimeException {
587         configReader = new VtsVendorConfigFileUtil();
588         if (configReader.LoadVendorConfig(mBuildInfo)) {
589             JSONObject vendorConfigJson = configReader.GetVendorConfigJson();
590             if (vendorConfigJson != null) {
591                 JsonUtil.deepMergeJsonObjects(jsonObject, vendorConfigJson);
592             }
593         }
594 
595         CLog.i("Load original test config %s %s", mTestCaseDataDir, mTestConfigPath);
596         String content = null;
597 
598         if (mTestConfigPath != null) {
599             content = FileUtil.readStringFromFile(
600                     new File(Paths.get(mTestCaseDataDir, mTestConfigPath).toString()));
601             CLog.i("Loaded original test config %s", content);
602             if (content != null) {
603                 JsonUtil.deepMergeJsonObjects(jsonObject, new JSONObject(content));
604             }
605         }
606 
607         populateDefaultJsonFields(jsonObject, mTestCaseDataDir);
608         CLog.i("Built a Json object using the loaded original test config");
609 
610         JSONArray deviceArray = new JSONArray();
611         JSONObject deviceItemObject = new JSONObject();
612         deviceItemObject.put(SERIAL, mDevice.getSerialNumber());
613         boolean coverageBuild = false;
614         try {
615             deviceItemObject.put("product_type", mDevice.getProductType());
616             deviceItemObject.put("product_variant", mDevice.getProductVariant());
617             deviceItemObject.put("build_alias", mDevice.getBuildAlias());
618             deviceItemObject.put("build_id", mDevice.getBuildId());
619             deviceItemObject.put("build_flavor", mDevice.getBuildFlavor());
620             String coverageProperty = mDevice.getProperty(COVERAGE_PROPERTY);
621             coverageBuild = coverageProperty != null && coverageProperty.equals("1");
622         } catch (DeviceNotAvailableException e) {
623             CLog.e("A device not available - continuing");
624             throw new RuntimeException("Failed to get device information");
625         }
626         deviceArray.put(deviceItemObject);
627 
628         JSONArray testBedArray = (JSONArray) jsonObject.get("test_bed");
629         if (testBedArray.length() == 0) {
630             JSONObject testBedItemObject = new JSONObject();
631             String testName;
632             if (mTestModuleName != null) {
633                 testName = mTestModuleName;
634             } else {
635                 CLog.w("--test-module-name not set (not recommended); deriving automatically");
636                 if (mTestConfigPath != null) {
637                     testName = new File(mTestConfigPath).getName();
638                     testName = testName.replace(CONFIG_FILE_EXTENSION, "");
639                 } else if (mTestCasePath != null) {
640                     testName = new File(mTestCasePath).getName();
641                 } else {
642                     throw new RuntimeException(
643                         "Failed to derive test module name; use --test-module-name option");
644                 }
645             }
646             CLog.logAndDisplay(LogLevel.INFO, "Setting test name as %s", testName);
647             testBedItemObject.put(NAME, testName);
648             testBedItemObject.put(ANDROIDDEVICE, deviceArray);
649             testBedArray.put(testBedItemObject);
650         } else if (testBedArray.length() == 1) {
651             JSONObject testBedItemObject = (JSONObject) testBedArray.get(0);
652             testBedItemObject.put(ANDROIDDEVICE, deviceArray);
653         } else {
654             CLog.e("Multi-device not yet supported: %d devices requested",
655                     testBedArray.length());
656             throw new RuntimeException("Failed to produce VTS runner test config");
657         }
658         jsonObject.put(DATA_FILE_PATH, mTestCaseDataDir);
659         CLog.i("Added %s = %s to the Json object", DATA_FILE_PATH, mTestCaseDataDir);
660 
661         JSONObject build = new JSONObject();
662         build.put(BUILD_ID, mBuildInfo.getBuildId());
663         build.put(BUILD_TARGET, mBuildInfo.getBuildTargetName());
664         jsonObject.put(BUILD, build);
665         CLog.i("Added %s to the Json object", BUILD);
666 
667         JSONObject suite = new JSONObject();
668         suite.put(NAME, mBuildInfo.getTestTag());
669         suite.put(INCLUDE_FILTER, new JSONArray(mIncludeFilters));
670         CLog.i("Added include filter to test suite: %s", mIncludeFilters);
671         suite.put(EXCLUDE_FILTER, new JSONArray(mExcludeFilters));
672         CLog.i("Added exclude filter to test suite: %s", mExcludeFilters);
673 
674         if (mExcludeOverInclude) {
675             jsonObject.put(EXCLUDE_OVER_INCLUDE, mExcludeOverInclude);
676             CLog.i("Added %s to the Json object", EXCLUDE_OVER_INCLUDE);
677         }
678 
679         jsonObject.put(TEST_SUITE, suite);
680         CLog.i("Added %s to the Json object", TEST_SUITE);
681 
682         jsonObject.put(TEST_MAX_TIMEOUT, mTestTimeout);
683         CLog.i("Added %s to the Json object: %d", TEST_MAX_TIMEOUT, mTestTimeout);
684 
685         if (mAbi != null) {
686             jsonObject.put(ABI_NAME, mAbi.getName());
687             CLog.i("Added %s to the Json object", ABI_NAME);
688             jsonObject.put(ABI_BITNESS, mAbi.getBitness());
689             CLog.i("Added %s to the Json object", ABI_BITNESS);
690         }
691 
692         if (mSkipOn32BitAbi) {
693             jsonObject.put(SKIP_ON_32BIT_ABI, mSkipOn32BitAbi);
694             CLog.i("Added %s to the Json object", SKIP_ON_32BIT_ABI);
695         }
696 
697         if (mSkipOn64BitAbi) {
698             jsonObject.put(SKIP_ON_64BIT_ABI, mSkipOn64BitAbi);
699             CLog.i("Added %s to the Json object", SKIP_ON_64BIT_ABI);
700         } else if (mRun32bBitOn64BitAbi) {
701             jsonObject.put(RUN_32BIT_ON_64BIT_ABI, mRun32bBitOn64BitAbi);
702             CLog.i("Added %s to the Json object", RUN_32BIT_ON_64BIT_ABI);
703         }
704 
705         if (mSkipIfThermalThrottling) {
706             jsonObject.put(SKIP_IF_THERMAL_THROTTLING, mSkipIfThermalThrottling);
707             CLog.i("Added %s to the Json object", SKIP_IF_THERMAL_THROTTLING);
708         }
709 
710         if (!mBinaryTestSource.isEmpty()) {
711             jsonObject.put(BINARY_TEST_SOURCE, new JSONArray(mBinaryTestSource));
712             CLog.i("Added %s to the Json object", BINARY_TEST_SOURCE);
713         }
714 
715         if (!mBinaryTestWorkingDirectory.isEmpty()) {
716             jsonObject.put(BINARY_TEST_WORKING_DIRECTORY,
717                     new JSONArray(mBinaryTestWorkingDirectory));
718             CLog.i("Added %s to the Json object", BINARY_TEST_WORKING_DIRECTORY);
719         }
720 
721         if (!mBinaryTestEnvp.isEmpty()) {
722             jsonObject.put(BINARY_TEST_ENVP, new JSONArray(mBinaryTestEnvp));
723             CLog.i("Added %s to the Json object", BINARY_TEST_ENVP);
724         }
725 
726         if (!mBinaryTestArgs.isEmpty()) {
727             jsonObject.put(BINARY_TEST_ARGS, new JSONArray(mBinaryTestArgs));
728             CLog.i("Added %s to the Json object", BINARY_TEST_ARGS);
729         }
730 
731         if (!mBinaryTestLdLibraryPath.isEmpty()) {
732             jsonObject.put(BINARY_TEST_LD_LIBRARY_PATH,
733                     new JSONArray(mBinaryTestLdLibraryPath));
734             CLog.i("Added %s to the Json object", BINARY_TEST_LD_LIBRARY_PATH);
735         }
736 
737         if (mBugReportOnFailure) {
738             jsonObject.put(BUG_REPORT_ON_FAILURE, mBugReportOnFailure);
739             CLog.i("Added %s to the Json object", BUG_REPORT_ON_FAILURE);
740         }
741 
742         if (mEnableProfiling) {
743             jsonObject.put(ENABLE_PROFILING, mEnableProfiling);
744             CLog.i("Added %s to the Json object", ENABLE_PROFILING);
745         }
746 
747         if (mSaveTraceFileRemote) {
748             jsonObject.put(SAVE_TRACE_FIEL_REMOTE, mSaveTraceFileRemote);
749             CLog.i("Added %s to the Json object", SAVE_TRACE_FIEL_REMOTE);
750         }
751 
752         if (mEnableSystrace) {
753             jsonObject.put(ENABLE_SYSTRACE, mEnableSystrace);
754             CLog.i("Added %s to the Json object", ENABLE_SYSTRACE);
755         }
756 
757         if (mEnableCoverage) {
758             jsonObject.put(GLOBAL_COVERAGE, mGlobalCoverage);
759             if (coverageBuild) {
760                 jsonObject.put(ENABLE_COVERAGE, mEnableCoverage);
761                 CLog.i("Added %s to the Json object", ENABLE_COVERAGE);
762             } else {
763                 CLog.i("Device build has coverage disabled");
764             }
765         }
766 
767         if (mOutputCoverageReport) {
768             jsonObject.put(OUTPUT_COVERAGE_REPORT, mOutputCoverageReport);
769             CLog.i("Added %s to the Json object", OUTPUT_COVERAGE_REPORT);
770         }
771 
772         if (mPreconditionHwBinderServiceName != null) {
773             jsonObject.put(PRECONDITION_HWBINDER_SERVICE, mPreconditionHwBinderServiceName);
774             CLog.i("Added %s to the Json object", PRECONDITION_HWBINDER_SERVICE);
775         }
776 
777         if (mPreconditionFeature != null) {
778             jsonObject.put(PRECONDITION_FEATURE, mPreconditionFeature);
779             CLog.i("Added %s to the Json object", PRECONDITION_FEATURE);
780         }
781 
782         if (mPreconditionFilePathPrefix != null) {
783             jsonObject.put(PRECONDITION_FILE_PATH_PREFIX, mPreconditionFilePathPrefix);
784             CLog.i("Added %s to the Json object", PRECONDITION_FILE_PATH_PREFIX);
785         }
786 
787         if (mPreconditionLshal != null) {
788             jsonObject.put(PRECONDITION_LSHAL, mPreconditionLshal);
789             CLog.i("Added %s to the Json object", PRECONDITION_LSHAL);
790         }
791 
792         if (mPreconditionVintf != null) {
793             jsonObject.put(PRECONDITION_VINTF, mPreconditionVintf);
794             CLog.i("Added %s to the Json object", PRECONDITION_VINTF);
795         }
796 
797         if (mPreconditionVintfOverride && mPreconditionLshal != null) {
798             if (mPreconditionVintf == null) {
799                 jsonObject.put(PRECONDITION_VINTF, mPreconditionLshal);
800                 CLog.i("Added %s to the Json object, overriding %s", PRECONDITION_VINTF,
801                         PRECONDITION_LSHAL);
802             } else {
803                 CLog.w("Ignored precondition-vintf-override as precondition-vintf is present");
804             }
805         }
806 
807         if (!mBinaryTestProfilingLibraryPath.isEmpty()) {
808             jsonObject.put(BINARY_TEST_PROFILING_LIBRARY_PATH,
809                     new JSONArray(mBinaryTestProfilingLibraryPath));
810             CLog.i("Added %s to the Json object", BINARY_TEST_PROFILING_LIBRARY_PATH);
811         }
812 
813         if (mBinaryTestDisableFramework) {
814             jsonObject.put(BINARY_TEST_DISABLE_FRAMEWORK, mBinaryTestDisableFramework);
815             CLog.i("Added %s to the Json object", BINARY_TEST_DISABLE_FRAMEWORK);
816         }
817 
818         if (mBinaryTestStopNativeServers) {
819             jsonObject.put(BINARY_TEST_STOP_NATIVE_SERVERS, mBinaryTestStopNativeServers);
820             CLog.i("Added %s to the Json object", BINARY_TEST_STOP_NATIVE_SERVERS);
821         }
822 
823         if (!mNativeServerProcessName.isEmpty()) {
824             jsonObject.put(NATIVE_SERVER_PROCESS_NAME, new JSONArray(mNativeServerProcessName));
825             CLog.i("Added %s to the Json object", NATIVE_SERVER_PROCESS_NAME);
826         }
827 
828         if (!mHalHidlReplayTestTracePaths.isEmpty()) {
829             jsonObject.put(HAL_HIDL_REPLAY_TEST_TRACE_PATHS,
830                     new JSONArray(mHalHidlReplayTestTracePaths));
831             CLog.i("Added %s to the Json object", HAL_HIDL_REPLAY_TEST_TRACE_PATHS);
832         }
833 
834         if (mHalHidlPackageName != null) {
835             jsonObject.put(HAL_HIDL_PACKAGE_NAME, mHalHidlPackageName);
836             CLog.i("Added %s to the Json object", SYSTRACE_PROCESS_NAME);
837         }
838 
839         if (mSystraceProcessName != null) {
840             jsonObject.put(SYSTRACE_PROCESS_NAME, mSystraceProcessName);
841             CLog.i("Added %s to the Json object", SYSTRACE_PROCESS_NAME);
842         }
843 
844         if (mPassthroughMode) {
845             jsonObject.put(PASSTHROUGH_MODE, mPassthroughMode);
846             CLog.i("Added %s to the Json object", PASSTHROUGH_MODE);
847         }
848 
849         if (mGtestBatchMode) {
850             jsonObject.put(GTEST_BATCH_MODE, mGtestBatchMode);
851             CLog.i("Added %s to the Json object", GTEST_BATCH_MODE);
852         }
853 
854         if (mLtpNumberOfThreads >= 0) {
855             jsonObject.put(LTP_NUMBER_OF_THREADS, mLtpNumberOfThreads);
856             CLog.i("Added %s to the Json object", LTP_NUMBER_OF_THREADS);
857         }
858     }
859 
860     /**
861      * Log a test module execution status to device logcat.
862      *
863      * @param status
864      * @return true if succesful, false otherwise
865      */
printToDeviceLogcatAboutTestModuleStatus(String status)866     private boolean printToDeviceLogcatAboutTestModuleStatus(String status) {
867         try {
868             mDevice.executeShellCommand(String.format(
869                     "log -p i -t \"VTS\" \"[Test Module] %s %s\"", mTestModuleName, status));
870         } catch (DeviceNotAvailableException e) {
871             CLog.w("Device unavailable while trying to write a message to logcat.");
872             return false;
873         }
874         return true;
875     }
876 
AddTestModuleKeys(String test_module_name, long test_module_timestamp)877     private boolean AddTestModuleKeys(String test_module_name, long test_module_timestamp) {
878         if (test_module_name.length() == 0 || test_module_timestamp == -1) {
879             CLog.e(String.format("Test module keys (%s,%d) are invalid.", test_module_name,
880                     test_module_timestamp));
881             return false;
882         }
883         File reportFile = mBuildInfo.getFile(TEST_PLAN_REPORT_FILE);
884 
885         try (FileWriter fw = new FileWriter(reportFile.getAbsoluteFile(), true);
886                 BufferedWriter bw = new BufferedWriter(fw); PrintWriter out = new PrintWriter(bw)) {
887             out.println(String.format("%s %s", test_module_name, test_module_timestamp));
888         } catch (IOException e) {
889             CLog.e(String.format(
890                     "Can't write to the test plan result file, %s", TEST_PLAN_REPORT_FILE));
891             return false;
892         }
893         return true;
894     }
895 
896     /**
897      * This method prepares a command for the test and runs the python file as
898      * given in the arguments.
899      *
900      * @param listener
901      * @param runUtil
902      * @param mTestCasePath
903      * @param mTestConfigPath
904      * @throws RuntimeException
905      * @throws IllegalArgumentException
906      */
doRunTest(ITestRunListener listener)907     private void doRunTest(ITestRunListener listener) throws RuntimeException, IllegalArgumentException {
908         CLog.i("Device serial number: " + mDevice.getSerialNumber());
909 
910         JSONObject jsonObject = new JSONObject();
911         File vtsRunnerLogDir = null;
912         try {
913             vtsRunnerLogDir = FileUtil.createTempDir("vts-runner-log");
914             updateVtsRunnerTestConfig(jsonObject);
915 
916             jsonObject.put(LOG_PATH,  vtsRunnerLogDir.getAbsolutePath());
917             CLog.i("Added %s to the Json object", LOG_PATH);
918         } catch(IOException e) {
919             throw new RuntimeException("Failed to read test config json file");
920         } catch(JSONException e) {
921             throw new RuntimeException("Failed to build updated test config json data");
922         }
923 
924         CLog.i("config json: %s", jsonObject.toString());
925 
926         String jsonFilePath = null;
927         try {
928             File tmpFile = FileUtil.createTempFile(
929                     mBuildInfo.getTestTag() + "-config-" + mBuildInfo.getDeviceSerial(), ".json");
930             jsonFilePath = tmpFile.getAbsolutePath();
931             CLog.i("config json file path: %s", jsonFilePath);
932             FileWriter fw = new FileWriter(jsonFilePath);
933             fw.write(jsonObject.toString());
934             fw.close();
935         } catch(IOException e) {
936             throw new RuntimeException("Failed to create device config json file");
937         }
938 
939         if (mPythonBin == null){
940             mPythonBin = getPythonBinary();
941         }
942         String[] baseOpts = {mPythonBin, "-m"};
943         String[] testModule = {mTestCasePath.replace("/", "."), jsonFilePath};
944         String[] cmd;
945         cmd = ArrayUtil.buildArray(baseOpts, testModule);
946 
947         printToDeviceLogcatAboutTestModuleStatus("BEGIN");
948         CommandResult commandResult =
949                 mRunUtil.runTimedCmd(mTestTimeout + TEST_ABORT_TIMEOUT_MSECS, cmd);
950 
951         if (commandResult != null) {
952             CommandStatus commandStatus = commandResult.getStatus();
953             if (commandStatus != CommandStatus.SUCCESS
954                 && commandStatus != CommandStatus.TIMED_OUT) {
955                 CLog.e("Python process failed");
956                 CLog.e("Python path: %s", mPythonPath);
957                 CLog.e("Stderr: %s", commandResult.getStderr());
958                 CLog.e("Stdout: %s", commandResult.getStdout());
959                 printVtsLogs(vtsRunnerLogDir);
960                 printToDeviceLogcatAboutTestModuleStatus("ERROR");
961                 throw new RuntimeException("Failed to run VTS test");
962             }
963             CLog.i("Standard output is: %s", commandResult.getStdout());
964             CLog.i("Parsing test result: %s", commandResult.getStderr());
965             printToDeviceLogcatAboutTestModuleStatus("END");
966         } else {
967             printToDeviceLogcatAboutTestModuleStatus("FRAMEWORK_ERROR");
968         }
969 
970         VtsMultiDeviceTestResultParser parser =
971                 new VtsMultiDeviceTestResultParser(listener, mRunName);
972 
973         if (mUseStdoutLogs) {
974             if (commandResult.getStdout() == null) {
975                 CLog.e("The std:out is null for CommandResult.");
976                 throw new RuntimeException("The std:out is null for CommandResult.");
977             }
978             parser.processNewLines(commandResult.getStdout().split("\n"));
979         } else {
980             // parse from test_run_summary.json instead of stdout
981             String jsonData = null;
982             JSONObject object = null;
983             File testRunSummary = getFileTestRunSummary(vtsRunnerLogDir);
984             if (testRunSummary == null) {
985                 throw new RuntimeException("Couldn't locate the file : " +
986                         TEST_RUN_SUMMARY_FILE_NAME);
987             }
988             try {
989                 jsonData = FileUtil.readStringFromFile(testRunSummary);
990                 CLog.i("Test Result Summary: %s", jsonData);
991                 object = new JSONObject(jsonData);
992             } catch (IOException e) {
993                 CLog.e("Error occurred in parsing Json file : %s", testRunSummary.toPath());
994             } catch (JSONException e) {
995                 CLog.e("Error occurred in parsing Json String : %s", jsonData);
996             }
997             if (object == null) {
998                 CLog.e("Json object is null.");
999                 throw new RuntimeException("Json object is null.");
1000             }
1001             parser.processJsonFile(object);
1002 
1003             try {
1004                 JSONObject planObject = object.getJSONObject(TESTMODULE);
1005                 String test_module_name = planObject.getString("Name");
1006                 long test_module_timestamp = planObject.getLong("Timestamp");
1007                 AddTestModuleKeys(test_module_name, test_module_timestamp);
1008             } catch (JSONException e) {
1009                 CLog.d("Key '%s' not found in result json summary", TESTMODULE);
1010             }
1011         }
1012         printVtsLogs(vtsRunnerLogDir);
1013 
1014         File reportMsg;
1015         int waitCount = 0;
1016         // Wait python process to finish for 3 minutes at most
1017         while ((reportMsg = FileUtil.findFile(vtsRunnerLogDir, REPORT_MESSAGE_FILE_NAME)) == null
1018                 && waitCount < 180) {
1019             try {
1020                 Thread.sleep(1000);
1021             } catch (Exception e) {
1022                 System.out.println(e);
1023             }
1024             waitCount++;
1025         }
1026 
1027         CLog.i("Report message path: %s", reportMsg);
1028 
1029         if (reportMsg == null) {
1030             CLog.e("Cannot find report message proto file.");
1031         } else if (reportMsg.length() > 0) {
1032             CLog.i("Uploading report message. File size: %s", reportMsg.length());
1033             VtsDashboardUtil dashboardUtil = new VtsDashboardUtil(configReader);
1034             dashboardUtil.Upload(reportMsg.getAbsolutePath());
1035         } else {
1036             CLog.i("Result uploading is not enabled.");
1037         }
1038 
1039         FileUtil.recursiveDelete(vtsRunnerLogDir);
1040         CLog.i("Deleted the runner log dir, %s.", vtsRunnerLogDir);
1041         if (jsonFilePath != null) {
1042           FileUtil.deleteFile(new File(jsonFilePath));
1043           CLog.i("Deleted the runner json config file, %s.", jsonFilePath);
1044         }
1045     }
1046 
1047     /**
1048      * This method return the file test_run_details.txt which is then used to parse logs.
1049      *
1050      * @param logDir : The file that needs to be converted
1051      * @return the file named test_run_details.txt
1052      * @throws IllegalArgumentException
1053      */
getFileTestRunSummary(File logDir)1054     private File getFileTestRunSummary(File logDir) throws IllegalArgumentException {
1055         File[] children;
1056         if (logDir == null) {
1057             throw new IllegalArgumentException("Argument logDir is null.");
1058         }
1059         children = logDir.listFiles();
1060         if (children != null) {
1061             for (File child : children) {
1062                 if (!child.isDirectory()) {
1063                     if (child.getName().equals(TEST_RUN_SUMMARY_FILE_NAME)) {
1064                         return child;
1065                     }
1066                 } else {
1067                     File file = getFileTestRunSummary(child);
1068                     if (file != null) {
1069                         return file;
1070                     }
1071                 }
1072             }
1073         }
1074         return null;
1075     }
1076 
1077     /**
1078      * The method prints all VTS runner log files
1079      *
1080      * @param logDir the File instance of the base log dir.
1081      */
printVtsLogs(File logDir)1082     private void printVtsLogs(File logDir) {
1083         File[] children;
1084         if (logDir == null) {
1085             CLog.e("Scan VTS log dir: null\n");
1086             return;
1087         }
1088         CLog.i("Scan VTS log dir %s\n", logDir.getAbsolutePath());
1089         children = logDir.listFiles();
1090         if (children != null) {
1091             for (File child : children) {
1092                 if (child.isDirectory()) {
1093                     printVtsLogs(child);
1094                 } else {
1095                     CLog.i("VTS log file %s\n", child.getAbsolutePath());
1096                     try {
1097                         if (child.getName().startsWith("vts_agent") &&
1098                                 child.getName().endsWith(".log")) {
1099                             CLog.i("Content: %s\n", FileUtil.readStringFromFile(child));
1100                         } else {
1101                             CLog.i("skip %s\n", child.getName());
1102                         }
1103                     } catch (IOException e) {
1104                         CLog.e("I/O error\n");
1105                     }
1106                 }
1107             }
1108         }
1109     }
1110 
1111     /**
1112      * This method returns whether the OS is Windows.
1113      */
isOnWindows()1114     private static boolean isOnWindows() {
1115         return System.getProperty(OS_NAME).contains(WINDOWS);
1116     }
1117 
1118     /**
1119      * This method sets the python path. It's based on the
1120      * assumption that the environment variable $ANDROID_BUILD_TOP is set.
1121      */
setPythonPath()1122     private void setPythonPath() {
1123         StringBuilder sb = new StringBuilder();
1124         String separator = File.pathSeparator;
1125         if (System.getenv(PYTHONPATH) != null) {
1126             sb.append(separator);
1127             sb.append(System.getenv(PYTHONPATH));
1128         }
1129 
1130         // to get the path for android-vts/testcases/ which keeps the VTS python code under vts.
1131         if (mBuildInfo != null) {
1132             CompatibilityBuildHelper mBuildHelper = new CompatibilityBuildHelper(mBuildInfo);
1133 
1134             File testDir = null;
1135             try {
1136                 testDir = mBuildHelper.getTestsDir();
1137             } catch (FileNotFoundException e) {
1138                 /* pass */
1139             }
1140             if (testDir != null) {
1141                 sb.append(separator);
1142                 mTestCaseDataDir = testDir.getAbsolutePath();
1143                 sb.append(mTestCaseDataDir);
1144             } else if (mBuildInfo.getFile(VTS) != null) {
1145                 sb.append(separator);
1146                 sb.append(mBuildInfo.getFile(VTS).getAbsolutePath()).append("/..");
1147             }
1148         }
1149 
1150         // for when one uses PythonVirtualenvPreparer.
1151         if (mBuildInfo.getFile(PYTHONPATH) != null) {
1152             sb.append(separator);
1153             sb.append(mBuildInfo.getFile(PYTHONPATH).getAbsolutePath());
1154         }
1155         if (System.getenv("ANDROID_BUILD_TOP") != null) {
1156             sb.append(separator);
1157             sb.append(System.getenv("ANDROID_BUILD_TOP")).append("/test");
1158         }
1159         if (sb.length() == 0) {
1160             throw new RuntimeException("Could not find python path on host machine");
1161         }
1162         mPythonPath = sb.substring(1);
1163         CLog.i("mPythonPath: %s", mPythonPath);
1164     }
1165 
1166     /**
1167      * This method gets the python binary.
1168      */
getPythonBinary()1169     private String getPythonBinary() {
1170         boolean isWindows = isOnWindows();
1171         String python = (isWindows? "python.exe": "python");
1172         File venvDir = mBuildInfo.getFile(VIRTUAL_ENV_PATH);
1173         if (venvDir != null) {
1174             String binDir = (isWindows? "Scripts": "bin");
1175             File pythonBinaryFile = new File(venvDir.getAbsolutePath(),
1176                     binDir + File.separator + python);
1177             String pythonBinPath = pythonBinaryFile.getAbsolutePath();
1178             if (pythonBinaryFile.exists()) {
1179                 CLog.i("Python path " + pythonBinPath + ".\n");
1180                 return pythonBinPath;
1181             }
1182             CLog.e(python + " doesn't exist under the " +
1183                    "created virtualenv dir (" + pythonBinPath + ").\n");
1184         } else {
1185           CLog.e(VIRTUAL_ENV_PATH + " not available in BuildInfo. " +
1186                  "Please use VtsPythonVirtualenvPreparer tartget preparer.\n");
1187         }
1188 
1189         IRunUtil runUtil = (mRunUtil == null ? RunUtil.getDefault() : mRunUtil);
1190         CommandResult c = runUtil.runTimedCmd(1000,
1191                 (isWindows ? "where" : "which"), python);
1192         String pythonBin = c.getStdout().trim();
1193         if (pythonBin.length() == 0) {
1194             throw new RuntimeException("Could not find python binary on host "
1195                     + "machine");
1196         }
1197         return pythonBin;
1198     }
1199 
1200     /**
1201      * {@inheritDoc}
1202      */
1203     @Override
setAbi(IAbi abi)1204     public void setAbi(IAbi abi){
1205         mAbi = abi;
1206     }
1207 }
1208