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