summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHsin-Yi Chen <hsinyichen@google.com>2017-02-23 01:40:46 +0000
committerandroid-build-merger <android-build-merger@google.com>2017-02-23 01:40:46 +0000
commitf576a9fd9c62199e38c41c312f5cd27e806ce2fb (patch)
treeb935b2c6f6857af93870d0aaf5cb39b6e34c3d2f
parent03d5fc8fab9919315d8f07acbac487004468bfcc (diff)
parenta42b1c7a9912e65e1d4f3c6aa7150aa78933b31f (diff)
downloadvndk-f576a9fd9c62199e38c41c312f5cd27e806ce2fb.tar.gz
Add test case for VNDK dependency
am: a42b1c7a99 Change-Id: Ib73a65ff51a9a058969147b1c3488397e52ddb19
-rw-r--r--__init__.py0
-rw-r--r--dependency/Android.mk27
-rw-r--r--dependency/AndroidTest.xml27
-rw-r--r--dependency/VtsVndkDependencyTest.py147
-rw-r--r--dependency/__init__.py0
-rw-r--r--dependency/elf_parser.py81
6 files changed, 282 insertions, 0 deletions
diff --git a/__init__.py b/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/__init__.py
diff --git a/dependency/Android.mk b/dependency/Android.mk
new file mode 100644
index 0000000..15af37a
--- /dev/null
+++ b/dependency/Android.mk
@@ -0,0 +1,27 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(call all-subdir-makefiles)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := VtsVndkDependencyTest
+
+VTS_CONFIG_SRC_DIR := testcases/vndk/dependency
+include test/vts/tools/build/Android.host_config.mk
+
diff --git a/dependency/AndroidTest.xml b/dependency/AndroidTest.xml
new file mode 100644
index 0000000..706e0ef
--- /dev/null
+++ b/dependency/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for VTS VNDK dependency test cases">
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
+ <option name="push-group" value="HostDrivenTest.push" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.VtsPythonVirtualenvPreparer">
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
+ <option name="test-module-name" value="VtsVndkDependencyTest" />
+ <option name="test-case-path" value="vts/testcases/vndk/dependency/VtsVndkDependencyTest" />
+ </test>
+</configuration>
+
diff --git a/dependency/VtsVndkDependencyTest.py b/dependency/VtsVndkDependencyTest.py
new file mode 100644
index 0000000..36f5241
--- /dev/null
+++ b/dependency/VtsVndkDependencyTest.py
@@ -0,0 +1,147 @@
+#!/usr/bin/env python3.4
+#
+# 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 logging
+import os
+import re
+import shutil
+import tempfile
+
+from vts.runners.host import asserts
+from vts.runners.host import base_test_with_webdb
+from vts.runners.host import test_runner
+from vts.runners.host import utils
+from vts.utils.python.controllers import android_device
+from vts.testcases.vndk.dependency import elf_parser
+
+class VtsVndkDependencyTest(base_test_with_webdb.BaseTestWithWebDbClass):
+ """A test case to verify vendor library dependency.
+
+ Attributes:
+ _temp_dir: The temporary directory to which the vendor partition is
+ copied.
+ _vendor_libs: Collection of strings. The names of the shared libraries
+ on vendor partition.
+ """
+ _SHELL_NAME = "vendor_dep_test_shell"
+ _VENDOR_PATH = "/vendor"
+ _LOW_LEVEL_NDK = [
+ "libc.so",
+ "libm.so",
+ "libz.so",
+ "liblog.so",
+ "libdl.so",
+ "libstdc++.so"
+ ]
+ _SAME_PROCESS_NDK = [re.compile(p) for p in [
+ "libEGL_.*\\.so$",
+ "libGLESv1_CM_.*\\.so$",
+ "libGLESv2_.*\\.so$",
+ "libGLESv3_.*\\.so$",
+ "vulkan.*\\.so$",
+ "libRSDriver.*\\.so$",
+ "libPVRRS\\.so$",
+ "gralloc-mapper@\\d+.\\d+-impl\\.so$",
+ ]]
+
+ def setUpClass(self):
+ """Initializes device and temporary directory."""
+ self.dut = self.registerController(android_device)[0]
+ self.dut.shell.InvokeTerminal(self._SHELL_NAME)
+ self._temp_dir = tempfile.mkdtemp()
+ self._vendor_libs = []
+
+ def tearDownClass(self):
+ """Deletes the temporary directory."""
+ logging.info("Delete %s", self._temp_dir)
+ shutil.rmtree(self._temp_dir)
+
+ def _isAllowedDependency(self, lib_name):
+ """Checks whether a library dependency is allowed.
+
+ A vendor library/executable is only allowed to depend on
+ - Low-level NDK
+ - Same-process NDK
+ - Other libraries on vendor partition
+
+ Args:
+ lib_name: String. The name of the depended library.
+
+ Returns:
+ A boolean representing whether the library is allowed.
+ """
+ if lib_name in self._vendor_libs or lib_name in self._LOW_LEVEL_NDK:
+ return True
+ for pattern in self._SAME_PROCESS_NDK:
+ if pattern.match(lib_name):
+ return True
+ return False
+
+ def _listSharedLibraries(self, path):
+ """Finds all shared libraries under a directory.
+
+ Args:
+ path: String. The path to search.
+
+ Returns:
+ Set of strings. The names of the found libraries.
+ """
+ results = set()
+ for root_dir, dir_names, file_names in os.walk(path):
+ for file_name in file_names:
+ if file_name.endswith(".so"):
+ results.add(file_name)
+ return results
+
+ def testElfDependency(self):
+ """Scans library/executable dependency on vendor partition."""
+ if not elf_parser.ElfParser.isSupported():
+ asserts.fail("readelf is not available")
+ logging.info("adb pull %s %s", self._VENDOR_PATH, self._temp_dir)
+ pull_output = self.dut.adb.pull(self._VENDOR_PATH, self._temp_dir)
+ logging.debug(pull_output)
+ self._vendor_libs = self._listSharedLibraries(self._temp_dir)
+ logging.info("Vendor libraries: " + str(self._vendor_libs))
+ error_count = 0
+ for root_dir, dir_names, file_names in os.walk(self._temp_dir):
+ for file_name in file_names:
+ file_path = os.path.join(root_dir, file_name)
+ elf = elf_parser.ElfParser(file_path)
+ if not elf.isValid():
+ logging.info("%s is not an ELF file", file_path)
+ continue
+ try:
+ dep_libs = elf.listDependencies()
+ except OSError as e:
+ error_count += 1
+ logging.exception("Cannot read %s: %s", file_path, str(e))
+ continue
+ logging.info("%s depends on: %s", file_path, str(dep_libs))
+ disallowed_libs = filter(
+ lambda x: not self._isAllowedDependency(x), dep_libs)
+ if len(disallowed_libs) == 0:
+ continue
+ error_count += 1
+ logging.error("%s depends on disallowed libs: %s",
+ file_path.replace(self._temp_dir, "", 1),
+ str(disallowed_libs))
+ asserts.assertEqual(error_count, 0,
+ "Total number of errors: " + str(error_count))
+
+if __name__ == "__main__":
+ test_runner.main()
+
diff --git a/dependency/__init__.py b/dependency/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dependency/__init__.py
diff --git a/dependency/elf_parser.py b/dependency/elf_parser.py
new file mode 100644
index 0000000..50e2584
--- /dev/null
+++ b/dependency/elf_parser.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python3.4
+#
+# 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 os
+import re
+
+from vts.runners.host import utils
+
+class ElfParser(object):
+ """This class reads an ELF file by parsing output of the command readelf.
+
+ Attributes:
+ _file_path: The path to the ELF file.
+ """
+
+ def __init__(self, file_path):
+ self._file_path = file_path
+
+ @staticmethod
+ def isSupported():
+ """Checks whether readelf is available."""
+ try:
+ utils.exe_cmd("readelf", "--version")
+ return True
+ except OSError:
+ return False
+
+ def isValid(self):
+ """Checks size and first 4 bytes of the ELF file.
+
+ Returns:
+ A boolean representing whether _file_path is a valid ELF.
+ """
+ try:
+ size = os.path.getsize(self._file_path)
+ # must be larger than 32-bit file header
+ if size < 52:
+ return False
+ except OSError:
+ return False
+ try:
+ with open(self._file_path, "rb") as f:
+ magic = f.read(4)
+ if list(bytearray(magic)) != [0x7f, 0x45, 0x4c, 0x46]:
+ return False
+ except IOError:
+ return False
+ return True
+
+ def listDependencies(self):
+ """Lists the shared libraries that the ELF depends on.
+
+ Returns:
+ List of strings. The names of the depended libraries.
+
+ Raises:
+ OSError if readelf fails.
+ """
+ pattern = re.compile("\\(NEEDED\\)\\s*Shared library: \[(.+)\]")
+ output = utils.exe_cmd("readelf", "--dynamic", self._file_path)
+ results = []
+ for line in output.split("\n"):
+ match = pattern.search(line)
+ if match:
+ results.append(match.group(1))
+ return results
+