Merge "Handle timeout in test runner"
diff --git a/harnesses/tradefed/src/com/android/compatibility/common/tradefed/targetprep/HidlProfilerPreparer.java b/harnesses/tradefed/src/com/android/compatibility/common/tradefed/targetprep/HidlProfilerPreparer.java
index 46dc357..4ee9a0c 100644
--- a/harnesses/tradefed/src/com/android/compatibility/common/tradefed/targetprep/HidlProfilerPreparer.java
+++ b/harnesses/tradefed/src/com/android/compatibility/common/tradefed/targetprep/HidlProfilerPreparer.java
@@ -46,7 +46,7 @@
  * This is used when one wants to do such setup and cleanup operations in Java instead of the
  * VTS Python runner, Python test template, or Python test case.
  */
-@OptionClass(alias = "push-file")
+@OptionClass(alias = "hidl-profiler-preparer")
 public class HidlProfilerPreparer implements ITargetCleaner, IAbiReceiver {
     private static final String LOG_TAG = "HidlProfilerPreparer";
 
diff --git a/runners/target/vts_hal_hidl_target/Android.bp b/runners/target/vts_hal_hidl_target/Android.bp
index c570060..8fa0675 100644
--- a/runners/target/vts_hal_hidl_target/Android.bp
+++ b/runners/target/vts_hal_hidl_target/Android.bp
@@ -18,7 +18,8 @@
     name: "VtsHalHidlTargetTestBase",
     srcs : [
         "VtsHalHidlTargetTestBase.cpp",
-        "VtsHalHidlTargetCallbackBase.cpp"
+        "VtsHalHidlTargetCallbackBase.cpp",
+        "VtsHalHidlTargetTestEnvBase.cpp"
     ],
 
     shared_libs: [
diff --git a/runners/target/vts_hal_hidl_target/VtsHalHidlTargetTestBase.cpp b/runners/target/vts_hal_hidl_target/VtsHalHidlTargetTestBase.cpp
index 6755719..f07026e 100644
--- a/runners/target/vts_hal_hidl_target/VtsHalHidlTargetTestBase.cpp
+++ b/runners/target/vts_hal_hidl_target/VtsHalHidlTargetTestBase.cpp
@@ -22,7 +22,7 @@
 
 namespace testing {
 
-std::string VtsHalHidlTargetTestBase::PropertyGet(const char* name) {
+string VtsHalHidlTargetTestBase::PropertyGet(const char* name) {
   char value[PROP_VALUE_MAX] = {0};
   __system_property_get(name, value);
   return value;
diff --git a/runners/target/vts_hal_hidl_target/VtsHalHidlTargetTestEnvBase.cpp b/runners/target/vts_hal_hidl_target/VtsHalHidlTargetTestEnvBase.cpp
new file mode 100644
index 0000000..b402f8f
--- /dev/null
+++ b/runners/target/vts_hal_hidl_target/VtsHalHidlTargetTestEnvBase.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "VtsHalHidlTargetTestEnvBase.h"
+
+#include <string>
+
+#include <utils/Log.h>
+
+#define LOG_TAG "VtsHalHidlTargetTestEnvBase"
+
+static constexpr const char* kListFlag = "--list_registered_services";
+static constexpr const char* kServceInstanceFlag = "--hal_service_instance";
+
+using namespace std;
+
+namespace testing {
+
+void VtsHalHidlTargetTestEnvBase::SetUp() {
+  if (!inited_) {
+    ALOGE("Environment not inited, did you forget to call init()?");
+    abort();
+  }
+  // Register services used in the test.
+  registerTestServices();
+  // For a dummy run which just print the registered hal services.
+  if (listService_) {
+    listRegisteredServices();
+    exit(0);
+  }
+  // Call the customized setup process.
+  HidlSetUp();
+}
+
+void VtsHalHidlTargetTestEnvBase::TearDown() {
+  // Call the customized teardown process.
+  HidlTearDown();
+}
+
+void VtsHalHidlTargetTestEnvBase::init(int* argc, char** argv) {
+  if (inited_) return;
+  for (int i = 1; i < *argc; i++) {
+    if (parseVtsTestOption(argv[i])) {
+      // Shift the remainder of the argv list left by one.
+      for (int j = i; j != *argc; j++) {
+        argv[j] = argv[j + 1];
+      }
+
+      // Decrements the argument count.
+      (*argc)--;
+
+      // We also need to decrement the iterator as we just removed an element.
+      i--;
+    }
+  }
+  inited_ = true;
+}
+
+bool VtsHalHidlTargetTestEnvBase::parseVtsTestOption(const char* arg) {
+  // str and flag must not be NULL.
+  if (arg == NULL) return false;
+
+  if (strncmp(arg, kListFlag, strlen(kListFlag)) == 0) {
+    listService_ = true;
+    return true;
+  }
+
+  if (strncmp(arg, kServceInstanceFlag, strlen(kServceInstanceFlag)) == 0) {
+    // value is the past after "--hal_service_instance="
+    const char* value = arg + strlen(kServceInstanceFlag) + 1;
+    addHalServiceInstance(string(value));
+    return true;
+  }
+  return false;
+}
+
+void VtsHalHidlTargetTestEnvBase::addHalServiceInstance(
+    string halServiceInstance) {
+  // hal_service_instance follows the format:
+  // package@version::interface/service_name e.g.:
+  // android.hardware.vibrator@1.0::IVibrator/default
+  string instance_name =
+      halServiceInstance.substr(0, halServiceInstance.find('/'));
+  string service_name =
+      halServiceInstance.substr(halServiceInstance.find('/') + 1);
+  // Fail the process if trying to pass multiple service names for the same
+  // service instance.
+  if (halServiceInstances_.find(instance_name) != halServiceInstances_.end()) {
+    ALOGE("Exisitng instance %s with name %s", instance_name.c_str(),
+          halServiceInstances_[instance_name].c_str());
+    abort();
+  }
+  halServiceInstances_[instance_name] = service_name;
+}
+
+string VtsHalHidlTargetTestEnvBase::getServiceName(string instanceName) {
+  if (halServiceInstances_.find(instanceName) != halServiceInstances_.end()) {
+    return halServiceInstances_[instanceName];
+  }
+  // Could not find the instance.
+  return "";
+}
+
+void VtsHalHidlTargetTestEnvBase::registerTestService(string package,
+                                                      string version,
+                                                      string interfaceName) {
+  string FQName = package + '@' + version + "::" + interfaceName;
+  registeredHalServices_.insert(FQName);
+}
+
+void VtsHalHidlTargetTestEnvBase::listRegisteredServices() {
+  for (string service : registeredHalServices_) {
+    printf("hal_service: %s\n", service.c_str());
+  }
+}
+
+}  // namespace testing
diff --git a/runners/target/vts_hal_hidl_target/VtsHalHidlTargetTestEnvBase.h b/runners/target/vts_hal_hidl_target/VtsHalHidlTargetTestEnvBase.h
new file mode 100644
index 0000000..2740ccd
--- /dev/null
+++ b/runners/target/vts_hal_hidl_target/VtsHalHidlTargetTestEnvBase.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VTS_HAL_HIDL_TARGET_TEST_ENV_BASE_H
+#define __VTS_HAL_HIDL_TARGET_TEST_ENV_BASE_H
+
+#include <gtest/gtest.h>
+
+using namespace std;
+
+namespace testing {
+
+// A class for test environment setup
+class VtsHalHidlTargetTestEnvBase : public ::testing::Environment {
+ public:
+  VtsHalHidlTargetTestEnvBase() {}
+
+  /*
+   * SetUp process, should not be overridden by the test.
+   */
+  void SetUp() final;
+
+  /*
+   * TearDown process, should not be overridden by the test.
+   */
+  void TearDown() final;
+
+  /*
+   * Test should override this method for any custom setup process.
+   */
+  virtual void HidlSetUp() {}
+
+  /*
+   * Test should override this method for any custom teardown process.
+   */
+  virtual void HidlTearDown() {}
+
+  /*
+   * Test should override this method to register hal services used in the test.
+   */
+  virtual void registerTestServices() {}
+
+  /* Parses the command line argument, extracts the vts reserved flags and
+   * leaves other options untouched.
+   * Must be called when the test environment is created is registered.
+   */
+  void init(int* argc, char** argv);
+
+  /*
+   * Gets the service name for a hal instance. Returns empty string if the hal
+   * instance is unkonwn (not in hal_instances_).
+   */
+  string getServiceName(string instanceName);
+
+  /*
+   * Adds a hal sevice identified with the package_name, version and
+   * interface name into registeredHalServices_.
+   */
+  void registerTestService(string package, string version,
+                           string interfaceName);
+
+ private:
+  /*
+   * Parses VTS specific flags, currently support two flags:
+   * --list_registered_services to print all registered service.
+   * --hal_service_instance to pass a running service instance. e.g.
+   * --hal_service_instance=android.hardware.vibrator@1.0::IVibrator/default
+   * It is possible to have mulitple --hal_service_instance options passed if
+   * mutliple hal service is used in the test.
+   * Returns true if successfully pased the given arg, false if arg is null or
+   * unknown flag.
+   */
+  bool parseVtsTestOption(const char* arg);
+
+  /*
+   * Prints all registered sercives.
+   */
+  void listRegisteredServices();
+
+  /*
+   * Internal method to add a hal service instance.
+   */
+  void addHalServiceInstance(string halServiceInstance);
+
+  // Map of hal instances with their correpoding service names.
+  map<string, string> halServiceInstances_;
+  // Set of all hal services used in the test.
+  set<string> registeredHalServices_;
+  // Flag to print registered hal services and exit the process.
+  bool listService_ = false;
+  // Flag whether init is called.
+  bool inited_ = false;
+};
+
+}  // namespace testing
+
+#endif  // __VTS_HAL_HIDL_TARGET_TEST_ENV_BASE_H
\ No newline at end of file
diff --git a/testcases/template/gtest_binary_test/gtest_binary_test.py b/testcases/template/gtest_binary_test/gtest_binary_test.py
index c919130..12bbef2 100644
--- a/testcases/template/gtest_binary_test/gtest_binary_test.py
+++ b/testcases/template/gtest_binary_test/gtest_binary_test.py
@@ -78,7 +78,7 @@
         ld_library_path = self.ld_library_path[
             tag] if tag in self.ld_library_path else None
         profiling_library_path = self.profiling_library_path[
-            tag] if tag in self.ld_library_path else None
+            tag] if tag in self.profiling_library_path else None
 
         args += " --gtest_list_tests"
         list_test_case = binary_test_case.BinaryTestCase(
diff --git a/testcases/template/hal_hidl_gtest/hal_hidl_gtest.py b/testcases/template/hal_hidl_gtest/hal_hidl_gtest.py
index 9236f42..554b919 100644
--- a/testcases/template/hal_hidl_gtest/hal_hidl_gtest.py
+++ b/testcases/template/hal_hidl_gtest/hal_hidl_gtest.py
@@ -14,13 +14,14 @@
 # limitations under the License.
 #
 
+import copy
 import logging
 
 from vts.runners.host import const
 from vts.runners.host import keys
 from vts.runners.host import test_runner
 from vts.testcases.template.gtest_binary_test import gtest_binary_test
-
+from vts.testcases.template.gtest_binary_test import gtest_test_case
 from vts.utils.python.cpu import cpu_frequency_scaling
 
 
@@ -72,6 +73,107 @@
         if passthrough_opt or self.coverage.enabled:
             self._EnablePassthroughMode()
 
+    # @Override
+    def CreateTestCase(self, path, tag=''):
+        '''Create a list of GtestTestCase objects from a binary path.
+
+        Support testing against different service names by first executing a
+        dummpy test case which lists all the registered hal services. Then
+        query the service name(s) for each registered service with lshal.
+        For each service name, create a new test case each with the service
+        name as an additional argument.
+
+        Args:
+            path: string, absolute path of a gtest binary on device
+            tag: string, a tag that will be appended to the end of test name
+
+        Returns:
+            A list of GtestTestCase objects.
+        '''
+        initial_test_cases = super(HidlHalGTest, self).CreateTestCase(path,
+                                                                      tag)
+        if not initial_test_cases:
+            return initial_test_cases
+        # first, run one test with --list_registered_services.
+        list_service_test_case = copy.copy(initial_test_cases[0])
+        list_service_test_case.args += " --list_registered_services"
+        results = self.shell.Execute(list_service_test_case.GetRunCommand())
+        if (results[const.EXIT_CODE][0]):
+            logging.error('Failed to list test cases from binary %s',
+                          list_service_test_case.path)
+        # parse the results to get the registered service list.
+        registered_services = []
+        for line in results[const.STDOUT][0].split('\n'):
+            line = str(line)
+            if line.startswith('hal_service: '):
+                service = line[len('hal_service: '):]
+                registered_services.append(service)
+
+        # If no service registered, return the initial test cases directly.
+        if not registered_services:
+            return initial_test_cases
+
+        # find the correponding service name(s) for each registered service and
+        # store the mapping in dict service_instances.
+        service_instances = {}
+        for service in registered_services:
+            cmd = '"lshal -i | grep -o %s/.* | sort -u"' % service
+            out = str(self._dut.adb.shell(cmd)).split()
+            service_names = map(lambda x: x[x.find('/') + 1:], out)
+            logging.info("registered service: %s with name: %s" %
+                         (service, ' '.join(service_names)))
+            service_instances[service] = service_names
+
+        # get all the combination of service instances.
+        service_instance_combinations = self._GetServiceInstancesCombinations(
+            registered_services, service_instances)
+
+        new_test_cases = []
+        for test_case in initial_test_cases:
+            for instance_combination in service_instance_combinations:
+                new_test_case = copy.copy(test_case)
+                for instance in instance_combination:
+                    new_test_case.args += " --hal_service_instance=" + instance
+                    new_test_case.tag = instance[instance.find(
+                        '/'):] + new_test_case.tag
+                new_test_cases.append(new_test_case)
+        return new_test_cases
+
+    @classmethod
+    def _GetServiceInstancesCombinations(self, services, service_instances):
+        '''Create all combinations of instances for all services.
+
+        Args:
+            services: list, all services used in the test. e.g. [s1, s2]
+            service_instances: dictionary, mapping of each service and the
+                               corresponding service name(s).
+                               e.g. {"s1": ["n1"], "s2": ["n2", "n3"]}
+
+        Returns:
+            A list of all service instance combinations.
+            e.g. [[s1/n1, s2/n2], [s1/n1, s2/n3]]
+        '''
+
+        service_instance_combinations = []
+        if not services:
+            return service_instance_combinations
+        service = services.pop()
+        pre_instance_combs = self._GetServiceInstancesCombinations(
+            services, service_instances)
+        if service not in service_instances:
+            return pre_instance_combs
+        for name in service_instances[service]:
+            if not pre_instance_combs:
+                new_instance_comb = [service + '/' + name]
+                service_instance_combinations.append(new_instance_comb)
+            else:
+                for instance_comb in pre_instance_combs:
+                    new_instance_comb = [service + '/' + name]
+                    new_instance_comb.extend(instance_comb)
+                    service_instance_combinations.append(new_instance_comb)
+
+        return service_instance_combinations
+
     def _EnablePassthroughMode(self):
         """Enable passthrough mode by setting getStub to true.
 
@@ -94,21 +196,20 @@
         super(HidlHalGTest, self).setUp()
 
         if (self._skip_if_thermal_throttling and
-            getattr(self, "_cpu_freq", None)):
+                getattr(self, "_cpu_freq", None)):
             self._cpu_freq.SkipIfThermalThrottling(retry_delay_secs=30)
 
     def tearDown(self):
         """Skips the test case if there is thermal throttling."""
         if (self._skip_if_thermal_throttling and
-            getattr(self, "_cpu_freq", None)):
+                getattr(self, "_cpu_freq", None)):
             self._cpu_freq.SkipIfThermalThrottling()
 
         super(HidlHalGTest, self).tearDown()
 
     def tearDownClass(self):
         """Turns off CPU frequency scaling."""
-        if (not self._skip_all_testcases and
-            getattr(self, "_cpu_freq", None)):
+        if (not self._skip_all_testcases and getattr(self, "_cpu_freq", None)):
             logging.info("Enable CPU frequency scaling")
             self._cpu_freq.EnableCpuScaling()
 
diff --git a/testcases/template/hal_hidl_gtest/hal_hidl_gtest_unittest.py b/testcases/template/hal_hidl_gtest/hal_hidl_gtest_unittest.py
new file mode 100644
index 0000000..bde230e
--- /dev/null
+++ b/testcases/template/hal_hidl_gtest/hal_hidl_gtest_unittest.py
@@ -0,0 +1,54 @@
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import unittest
+
+from vts.testcases.template.hal_hidl_gtest import hal_hidl_gtest
+
+class HidlHalGTestUnitTest(unittest.TestCase):
+    """Tests for hal hidl gtest template"""
+
+    def testGetServiceInstancesCombinations(self):
+        """Test the function to get service instance combinations"""
+
+        comb1 = hal_hidl_gtest.HidlHalGTest._GetServiceInstancesCombinations(
+            [], {})
+        self.assertEquals(0, len(comb1))
+        comb2 = hal_hidl_gtest.HidlHalGTest._GetServiceInstancesCombinations(
+            ["s1"], {})
+        self.assertEquals(0, len(comb2))
+        comb3 = hal_hidl_gtest.HidlHalGTest._GetServiceInstancesCombinations(
+            ["s1"], {"s1": ["n1"]})
+        self.assertEqual([["s1/n1"]], comb3)
+        comb4 = hal_hidl_gtest.HidlHalGTest._GetServiceInstancesCombinations(
+            ["s1"], {"s1": ["n1", "n2"]})
+        self.assertEqual([["s1/n1"], ["s1/n2"]], comb4)
+        comb5 = hal_hidl_gtest.HidlHalGTest._GetServiceInstancesCombinations(
+            ["s1", "s2"], {"s1": ["n1", "n2"]})
+        self.assertEqual([["s1/n1"], ["s1/n2"]], comb5)
+        comb6 = hal_hidl_gtest.HidlHalGTest._GetServiceInstancesCombinations(
+            ["s1", "s2"], {"s1": ["n1", "n2"],
+                           "s2": ["n3"]})
+        self.assertEqual([["s2/n3", "s1/n1"], ["s2/n3", "s1/n2"]], comb6)
+        comb7 = hal_hidl_gtest.HidlHalGTest._GetServiceInstancesCombinations(
+            ["s1", "s2"], {"s1": ["n1", "n2"],
+                           "s2": ["n3", "n4"]})
+        self.assertEqual([["s2/n3", "s1/n1"], ["s2/n3", "s1/n2"],
+                          ["s2/n4", "s1/n1"], ["s2/n4", "s1/n2"]], comb7)
+
+
+if __name__ == '__main__':
+    unittest.main()