#!/usr/bin/env python3 # # Copyright (C) 2020 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 posixpath as target_path_module import sys import unittest from vts.testcases.vndk import utils from vts.testcases.vndk.golden import vndk_data from vts.utils.python.vndk import vndk_utils class VtsVndkFilesTest(unittest.TestCase): """A test for VNDK files and directories. Attributes: _dut: The AndroidDevice under test. _vndk_version: The VNDK version of the device. """ # Some LL-NDK libraries may load the implementations with the same names # from /vendor/lib. Since a vendor may install an implementation of an # LL-NDK library with the same name, testNoLlndkInVendor doesn't raise # errors on these LL-NDK libraries. _LL_NDK_COLLIDING_NAMES = ("libEGL.so", "libGLESv1_CM.so", "libGLESv2.so", "libGLESv3.so") _TARGET_ODM_LIB = "/odm/{LIB}" _TARGET_VENDOR_LIB = "/vendor/{LIB}" def setUp(self): """Initializes attributes.""" serial_number = os.environ.get("ANDROID_SERIAL") self.assertTrue(serial_number, "$ANDROID_SERIAL is empty.") self._dut = utils.AndroidDevice(serial_number) self.assertTrue(self._dut.IsRoot(), "This test requires adb root.") self._vndk_version = self._dut.GetVndkVersion() def _ListFiles(self, dir_path): """Lists all files in a directory except subdirectories. Args: dir_path: A string, path to the directory on device. Returns: A list of strings, the file paths in the directory. """ if not self._dut.Exists(dir_path): logging.info("%s not found", dir_path) return [] return self._dut.FindFiles(dir_path, "*", "!", "-type", "d") def _Fail(self, unexpected_paths, message): """Logs error and fails current test. Args: unexpected_paths: A list of strings, the paths to be shown in the log message. message: A string, the error message. """ logging.error("Unexpected files:\n%s", "\n".join(unexpected_paths)) assert_lines = unexpected_paths[:20] if len(unexpected_paths) > 20: assert_lines.extend([ "...", "Total number of errors: %d" % len(unexpected_paths)]) assert_lines.append(message) self.fail("\n".join(assert_lines)) def _TestVndkDirectory(self, vndk_dir, vndk_list_names): """Verifies that the VNDK directory doesn't contain extra files. Args: vndk_dir: The path to the VNDK directory on device. vndk_list_names: Strings, the categories of the VNDK libraries that can be in the directory. """ vndk_lists = vndk_data.LoadVndkLibraryListsFromResources( self._vndk_version, *vndk_list_names) self.assertTrue(vndk_lists, "Cannot load VNDK library lists.") vndk_set = set().union(*vndk_lists) logging.debug("vndk set: %s", vndk_set) unexpected = [x for x in self._ListFiles(vndk_dir) if target_path_module.basename(x) not in vndk_set] if unexpected: self._Fail(unexpected, "The above libraries are not %s." % ", ".join(vndk_list_names)) def _TestNoLlndkInDirectory(self, lib_dir): """Verifies that the vendor directory doesn't contain LLNDK libraries. Args: lib_dir: The path to the directory on device. """ if vndk_utils.IsVndkRequired(self._dut): llndk_list = vndk_data.LoadVndkLibraryListsFromResources( self._vndk_version, vndk_data.LL_NDK)[0] else: llndk_list = self._dut.GetLlndkList() llndk_set = set(llndk_list).difference(self._LL_NDK_COLLIDING_NAMES) logging.debug("llndk set: %s", llndk_set) unexpected = [x for x in self._ListFiles(lib_dir) if target_path_module.basename(x) in llndk_set] if unexpected: self._Fail(unexpected, lib_dir + " must not contain LLNDK libraries.") def _TestVndkCoreDirectory(self, bitness): """Verifies that VNDK directory doesn't contain extra files.""" if not self._dut.GetCpuAbiList(bitness): logging.info("Skip the test as the device doesn't support %d-bit " "ABI.", bitness) return if not vndk_utils.IsVndkRuntimeEnforced(self._dut): logging.info("Skip the test as VNDK runtime is not enforced on " "the device.") return if not vndk_utils.IsVndkRequired(self._dut): logging.info("Skip the test as the device does not require VNDK.") return if vndk_utils.IsVndkInstalledInVendor(self._dut): logging.info("Skip the test as VNDK %s should be installed in " "vendor partition.", self._vndk_version) return self._TestVndkDirectory( vndk_utils.GetVndkDirectory(bitness, self._vndk_version), (vndk_data.VNDK, vndk_data.VNDK_PRIVATE, vndk_data.VNDK_SP, vndk_data.VNDK_SP_PRIVATE,)) def testVndkCoreDirectory32(self): """Runs _TestVndkCoreDirectory for 32-bit libraries.""" self._TestVndkCoreDirectory(32) def testVndkCoreDirectory64(self): """Runs _TestVndkCoreDirectory for 64-bit libraries.""" self._TestVndkCoreDirectory(64) def _TestNoLlndkInVendor(self, bitness): """Verifies that vendor partition has no LL-NDK libraries.""" if not self._dut.GetCpuAbiList(bitness): logging.info("Skip the test as the device doesn't support %d-bit " "ABI.", bitness) return self._TestNoLlndkInDirectory( vndk_utils.FormatVndkPath(self._TARGET_VENDOR_LIB, bitness)) def testNoLlndkInVendor32(self): """Runs _TestNoLlndkInVendor for 32-bit libraries.""" self._TestNoLlndkInVendor(32) def testNoLlndkInVendor64(self): """Runs _TestNoLlndkInVendor for 64-bit libraries.""" self._TestNoLlndkInVendor(64) def _TestNoLlndkInOdm(self, bitness): """Verifies that odm partition has no LL-NDK libraries.""" if not self._dut.GetCpuAbiList(bitness): logging.info("Skip the test as the device doesn't support %d-bit " "ABI.", bitness) return self._TestNoLlndkInDirectory( vndk_utils.FormatVndkPath(self._TARGET_ODM_LIB, bitness)) def testNoLlndkInOdm32(self): """Runs _TestNoLlndkInOdm for 32-bit libraries.""" self._TestNoLlndkInOdm(32) def testNoLlndkInOdm64(self): """Runs _TestNoLlndkInOdm for 64-bit libraries.""" self._TestNoLlndkInOdm(64) if __name__ == "__main__": # The logs are written to stdout so that TradeFed test runner can parse the # results from stderr. logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) # Setting verbosity is required to generate output that the TradeFed test # runner can parse. unittest.main(verbosity=3)