def testRootSymlinks(self): """Checks the symlinks under root directory.""" error_msg = [] def _CheckSymlinks(dir_path): """Checks the symlinks under dir_path.""" current_symlinks = self._ListDir(dir_path, "symlink") logging.info("Current symlinks: %r", current_symlinks) unexpected_symlinks = current_symlinks - set(_ALLOWED_SYMLINKS) error_msg.extend("Unexpected symlink: " + l for l in unexpected_symlinks) # Checks symlink target. error_msg.extend( "Invalid symlink target: %s -> %s (expected: %s)" % (l, target, _ALLOWED_SYMLINKS[l]) for l, target in ((l, self._ReadLink(l)) for l in (current_symlinks - unexpected_symlinks)) if target != _ALLOWED_SYMLINKS[l]) for dir_path in self._dirs_to_check: _CheckSymlinks(dir_path) if error_msg: asserts.fail("UNEXPECTED ROOT SYMLINKS:\n%s" % "\n".join(error_msg))
def _TestNotInVndkDirecotory(self, vndk_dir, vndk_list_names, except_libs): """Verifies that VNDK directory doesn't contain specific files. Args: vndk_dir, The path to the VNDK directory on device. vndk_list_names: A list of strings, the categories of the VNDK libraries that should not be in the directory. except_libs: A set of strings, the file names of the libraries that are exceptions to this test. """ vndk_lists = vndk_data.LoadVndkLibraryLists(self.data_file_path, self._vndk_version, *vndk_list_names) asserts.assertTrue(vndk_lists, "Cannot load VNDK library lists.") vndk_set = set() for vndk_list in vndk_lists: vndk_set.update(path_utils.TargetBaseName(x) for x in vndk_list) vndk_set.difference_update(except_libs) logging.debug("vndk set: %s", vndk_set) unexpected = [ x for x in self._ListFiles(vndk_dir) if path_utils.TargetBaseName(x) in vndk_set ] if unexpected: logging.error("Unexpected files:\n%s", "\n".join(unexpected)) asserts.fail("Total number of errors: %d" % len(unexpected))
def runSelinuxFileTest(self, test_object): """Reads the file and checks that its content and permissions are valid. Args: test_object: inherits KernelSelinuxFileTestBase, contains the test functions """ logging.info("Testing existence of %s" % (test_object.get_path())) asserts.assertTrue( target_file_utils.Exists(test_object.get_path(), self.shell), "%s: File does not exist." % test_object.get_path()) logging.info("Testing permissions of %s" % (test_object.get_path())) try: permissions = target_file_utils.GetPermission( test_object.get_path(), self.shell) asserts.assertTrue( test_object.get_permission_checker()(permissions), "%s: File has invalid permissions (%s)" % (test_object.get_path(), permissions)) except (ValueError, IOError) as e: asserts.fail("Failed to assert permissions: %s" % str(e)) logging.info("Testing format of %s" % (test_object.get_path())) file_content = target_file_utils.ReadFileContent( test_object.get_path(), self.shell) asserts.assertTrue(test_object.result_correct(file_content), "Results not valid!")
def kernel_version(self): """Gets the kernel verison from the device. This method reads the output of command "uname -r" from the device. Returns: A tuple of kernel version information in the format of (version, patchlevel, sublevel). It will fail if failed to get the output or correct format from the output of "uname -r" command """ cmd = 'uname -r' out = self.adb.shell(cmd) out = out.strip() match = re.match(r"(\d+)\.(\d+)\.(\d+)", out) if match is None: asserts.fail("Failed to detect kernel version of device. out:%s", out) version = int(match.group(1)) patchlevel = int(match.group(2)) sublevel = int(match.group(3)) logging.info("Detected kernel version: %s", match.group(0)) return (version, patchlevel, sublevel)
def parseConfigFileToDict(self, file, configs): """Parse kernel config file to a dictionary. Args: file: file object, android-base.cfg or unzipped /proc/config.gz configs: dict to which config options in file will be added Returns: dict: {config_name: config_state} """ config_lines = [line.rstrip("\n") for line in file.readlines()] for line in config_lines: if line.startswith("#") and line.endswith("is not set"): match = re.search(r"CONFIG_\S+", line) if match is None: asserts.fail("Failed to parse config file") else: config_name = match.group(0) config_state = "n" elif line.startswith("CONFIG_"): config_name, config_state = line.split("=", 1) if config_state.startswith(("'", '"')): config_state = config_state[1:-1] else: continue configs[config_name] = config_state return configs
def runProcFileTest(self, test_object): """Reads from the file and checks that it parses and the content is valid. Args: test_object: inherits KernelProcFileTestBase, contains the test functions """ asserts.skipIf(test_object in TEST_OBJECTS_64 and not self.dut.is64Bit, "Skip test for 64-bit kernel.") filepath = test_object.get_path() target_file_utils.assertPermissionsAndExistence( self.shell, filepath, test_object.get_permission_checker()) logging.info("Testing format of %s", filepath) asserts.assertTrue( test_object.prepare_test(self.shell), "Setup failed!") if not test_object.test_format(): return file_content = self.ReadFileContent(filepath) try: parse_result = test_object.parse_contents(file_content) except (SyntaxError, ValueError, IndexError) as e: asserts.fail("Failed to parse! " + str(e)) asserts.assertTrue( test_object.result_correct(parse_result), "Results not valid!")
def testVendorProcessOpenLibraries(self): """Checks if vendor processes load shared libraries on system.""" asserts.skipIf(not vndk_utils.IsVndkRuntimeEnforced(self._dut), "VNDK runtime is not enforced on the device.") vndk_lists = vndk_data.LoadVndkLibraryLists( self.data_file_path, self._dut.vndk_version, vndk_data.LL_NDK, vndk_data.LL_NDK_PRIVATE, vndk_data.VNDK, vndk_data.VNDK_PRIVATE, vndk_data.VNDK_SP, vndk_data.VNDK_SP_PRIVATE) asserts.assertTrue(vndk_lists, "Cannot load VNDK library lists.") allowed_libs = set() for vndk_list in vndk_lists: allowed_libs.update(vndk_list) logging.debug("Allowed system libraries: %s", allowed_libs) asserts.assertTrue(self._dut.isAdbRoot, "Must be root to find all libraries in use.") cmds = self._ListProcessCommands( lambda x: (x.startswith("/odm/") or x.startswith("/vendor/"))) deps = self._ListOpenFiles( cmds.keys(), lambda x: (x.startswith("/system/") and x.endswith( ".so") and x not in allowed_libs)) if deps: error_lines = [ "%s %s %s" % (pid, cmds[pid], libs) for pid, libs in deps.iteritems() ] logging.error("pid command libraries\n%s", "\n".join(error_lines)) asserts.fail( "Number of vendor processes using system libraries: " + str(len(deps)))
def CheckImageHeader(self, boot_image, is_recovery=False): """Verifies the boot image header version, header size and recovery dtbo size. Args: boot_image: Path to the boot image. is_recovery: Indicates that the image is recovery if true. """ try: with open(boot_image, "rb") as image_file: image_file.read(8) # read boot magic host_image_header_version = unpack("10I", image_file.read(10 * 4))[8] asserts.assertEqual( host_image_header_version, 1, "Device does not have boot image of version 1") image_file.seek(BOOT_HEADER_DTBO_SIZE_OFFSET) recovery_dtbo_size = unpack("I", image_file.read(4))[0] image_file.read(8) # ignore recovery dtbo load address if is_recovery: asserts.assertNotEqual( recovery_dtbo_size, 0, "recovery partition for non-A/B devices must contain the recovery DTBO" ) boot_header_size = unpack("I", image_file.read(4))[0] expected_header_size = image_file.tell() asserts.assertEqual( boot_header_size, expected_header_size, "Test failure due to boot header size mismatch. Expected %s Actual %s" % (expected_header_size, boot_header_size)) except IOError as e: logging.exception(e) asserts.fail("Unable to open boot image file")
def VerifyTestResult(self, test_case, command_results): """Parse Gtest xml result output. Args: test_case: BinaryTestCase object, the test being run. This param is not currently used in this method. command_results: dict of lists, shell command result """ asserts.assertTrue(command_results, 'Empty command response.') asserts.assertEqual(len(command_results), 3, 'Abnormal command response.') for stdout in command_results[const.STDOUT]: if stdout and stdout.strip(): for line in stdout.split('\n'): logging.info(line) if any(command_results[const.EXIT_CODE]): # print stderr only when test fails. for stderr in command_results[const.STDERR]: if stderr and stderr.strip(): for line in stderr.split('\n'): logging.error(line) asserts.fail( 'Test {} failed with the following results: {}'.format( test_case, command_results))
def testKernelVersion(self): """Validate the kernel version of DUT is a valid kernel version. Returns: string, kernel version of device """ cmd = "uname -a" results = self.shell.Execute(cmd) logging.info("Shell command '%s' results: %s", cmd, results) match = re.search(r"(\d+)\.(\d+)\.(\d+)", results[const.STDOUT][0]) if match is None: asserts.fail("Failed to detect kernel version of device.") else: kernel_version = int(match.group(1)) kernel_patchlevel = int(match.group(2)) kernel_sublevel = int(match.group(3)) logging.info("Detected kernel version: %s.%s.%s" % (kernel_version, kernel_patchlevel, kernel_sublevel)) for v in self.supported_kernel_versions: if (kernel_version == v[0] and kernel_patchlevel == v[1] and kernel_sublevel >= v[2]): logging.info("Compliant kernel version %s.%s.%s found." % (kernel_version, kernel_patchlevel, kernel_sublevel)) return asserts.fail("Device is running an unsupported kernel version (%s.%s.%s)" % (kernel_version, kernel_patchlevel, kernel_sublevel))
def testCheckDTBOPartition(self): """Validates DTBO partition using mkdtboimg.py.""" try: slot_suffix = str(self.dut.getProp(PROPERTY_SLOT_SUFFIX)) except ValueError as e: logging.exception(e) slot_suffix = "" current_dtbo_partition = "dtbo" + slot_suffix dtbo_path = target_file_utils.FindFiles(self.shell, BLOCK_DEV_PATH, current_dtbo_partition, "-type l") logging.info("DTBO path %s", dtbo_path) if not dtbo_path: asserts.fail("Unable to find path to dtbo image on device.") host_dtbo_image = os.path.join(self.temp_dir, "dtbo") self.adb.pull("%s %s" % (dtbo_path[0], host_dtbo_image)) mkdtboimg_bin_path = os.path.join("host", "bin", "mkdtboimg.py") unpacked_dtbo_path = os.path.join(self.temp_dir, "dumped_dtbo") dtbo_dump_cmd = [ "%s" % mkdtboimg_bin_path, "dump", "%s" % host_dtbo_image, "-b", "%s" % unpacked_dtbo_path ] try: subprocess.check_call(dtbo_dump_cmd) except Exception as e: logging.exception(e) logging.error('dtbo_dump_cmd is: %s', dtbo_dump_cmd) asserts.fail("Invalid DTBO Image") # TODO(b/109892148) Delete code below once decompress option is enabled for mkdtboimg.py self.DecompressDTEntries(host_dtbo_image, unpacked_dtbo_path)
def CreateTestCases(self): '''Push files to device and create test case objects.''' source_list = list(map(self.ParseTestSource, self.binary_test_source)) source_list = filter(bool, source_list) logging.info('Parsed test sources: %s', source_list) # Push source files first for src, dst, tag in source_list: if src: if os.path.isdir(src): src = os.path.join(src, '.') logging.info('Pushing from %s to %s.', src, dst) self._dut.adb.push('{src} {dst}'.format(src=src, dst=dst)) self.shell.Execute('ls %s' % dst) # Then create test cases for src, dst, tag in source_list: if tag is not None: # tag not being None means to create a test case self.tags.add(tag) logging.info('Creating test case from %s with tag %s', dst, tag) testcase = self.CreateTestCase(dst, tag) if not testcase: continue if type(testcase) is list: self.testcases.extend(testcase) else: self.testcases.append(testcase) if type(self.testcases) is not list or len(self.testcases) == 0: asserts.fail("No test case is found or generated.")
def CheckImageHeader(self, boot_image, is_recovery=False): """Verifies the boot image format. Args: boot_image: Path to the boot image. is_recovery: Indicates that the image is recovery if true. """ try: with open(boot_image, "rb") as image_file: image_file.read(8) # read boot magic (kernel_size, _, ramdisk_size, _, _, _, _, page_size, host_image_header_version) = unpack("9I", image_file.read(9 * 4)) asserts.assertNotEqual( kernel_size, 0, "boot.img/recovery.img must contain kernel") if self.launch_api_level > api.PLATFORM_API_LEVEL_P: asserts.assertTrue( host_image_header_version >= 2, "Device must atleast have a boot image of version 2") asserts.assertNotEqual(ramdisk_size, 0, "boot.img must contain ramdisk") # ramdisk comes after the header and kernel pages num_kernel_pages = self.get_number_of_pages( kernel_size, page_size) ramdisk_offset = page_size * (1 + num_kernel_pages) image_file.seek(ramdisk_offset) ramdisk_buf = image_file.read(ramdisk_size) self.checkValidRamdisk(ramdisk_buf) else: asserts.assertTrue( host_image_header_version >= 1, "Device must atleast have a boot image of version 1") image_file.seek(BOOT_HEADER_DTBO_SIZE_OFFSET) recovery_dtbo_size = unpack("I", image_file.read(4))[0] image_file.read(8) # ignore recovery dtbo load address if is_recovery: asserts.assertNotEqual( recovery_dtbo_size, 0, "recovery partition for non-A/B devices must contain the recovery DTBO" ) boot_header_size = unpack("I", image_file.read(4))[0] if host_image_header_version > 1: dtb_size = unpack("I", image_file.read(4))[0] asserts.assertNotEqual( dtb_size, 0, "Boot/recovery image must contain DTB") image_file.read(8) # ignore DTB physical load address expected_header_size = image_file.tell() asserts.assertEqual( boot_header_size, expected_header_size, "Test failure due to boot header size mismatch. Expected %s Actual %s" % (expected_header_size, boot_header_size)) except IOError as e: logging.exception(e) asserts.fail("Unable to open boot image file")
def RunBenchmark(self, bits): """Runs the native binary and parses its result. Args: bits: integer (32 or 64), the number of bits in a word chosen at the compile time (e.g., 32- vs. 64-bit library). """ # Runs the benchmark. logging.info( "Start to run the benchmark with HIDL mode %s (%s bit mode)", self.hidl_hal_mode, bits) binary = "/data/local/tmp/%s/libhwbinder_benchmark%s" % (bits, bits) self.dut.adb.shell("chmod 755 %s" % binary) try: result = self.dut.adb.shell( "LD_LIBRARY_PATH=/system/lib%s:/data/local/tmp/%s/hw:" "/data/local/tmp/%s:" "$LD_LIBRARY_PATH %s -m %s" % (bits, bits, bits, binary, self.hidl_hal_mode.encode("utf-8"))) except adb.AdbError as e: asserts.fail("HwBinderPerformanceTest failed.") # Parses the result. stdout_lines = result.split("\n") logging.info("stdout: %s", stdout_lines) label_result = [] value_result = [] prefix = (self.LABEL_PREFIX_BINDERIZE if self.hidl_hal_mode == "BINDERIZE" else self.LABEL_PREFIX_PASSTHROUGH) for line in stdout_lines: if line.startswith(prefix): tokens = line.split() benchmark_name = tokens[0] time_in_ns = tokens[1].split()[0] logging.info(benchmark_name) logging.info(time_in_ns) label_result.append(benchmark_name.replace(prefix, "")) value_result.append(int(time_in_ns)) logging.info("result label for %sbits: %s", bits, label_result) logging.info("result value for %sbits: %s", bits, value_result) # To upload to the web DB. self.web.AddProfilingDataLabeledVector( "hwbinder_vector_roundtrip_latency_benchmark_%sbits" % bits, label_result, value_result, x_axis_label="Message Size (Bytes)", y_axis_label="Roundtrip HwBinder RPC Latency (naonseconds)") # Assertions to check the performance requirements for label, value in zip(label_result, value_result): if label in self.THRESHOLD[bits]: asserts.assertLess( value, self.THRESHOLD[bits][label], "%s ns for %s is longer than the threshold %s ns" % ( value, label, self.THRESHOLD[bits][label]))
def testSdkVersion(self): """Test that SDK version >= O (26).""" try: sdkVersion = int(self.getProp("ro.build.version.sdk")) asserts.assertTrue(sdkVersion >= ANDROID_O_API_VERSION, "VTS is for devices launching in O or above") except ValueError: asserts.fail("Unexpected value returned from getprop")
def testSdkVersion(self): """Test that SDK version >= O (26).""" try: sdkVersion = int(self.getProp("ro.build.version.sdk")) asserts.assertTrue(sdkVersion >= api.PLATFORM_API_LEVEL_O, "VTS is for devices launching in O or above") except ValueError as e: asserts.fail("Unexpected value returned from getprop: %s" % e)
def testSysPowerState(self): '''/sys/power/state controls the system sleep states.''' filepath = '/sys/power/state' self.IsReadWrite(filepath) content = target_file_utils.ReadFileContent(filepath, self.shell) allowed_states = ['freeze', 'mem', 'disk', 'standby'] for state in content.split(): if state not in allowed_states: asserts.fail("Invalid system power state: %s" % state)
def testReboot(self): """Tests if device is still responsive after reboot.""" try: self.dut.reboot() # If waitForBootCompletion() returns, the device must have # responded to an adb shell command. self.dut.waitForBootCompletion() except utils.TimeoutError: asserts.fail("Reboot failed.")
def testOemHook(self): """Test to ensure the deprecated IOemHook HAL is not present""" logging.debug("Check if API is present in VINTF") try: asserts.assertFalse(self.apiNameInVintf("IOemHook"), "IOemHook cannot be present") except TypeError as e: asserts.fail(str(e))
def VerifyTestResult(self, test_case, command_results): '''Parse Gtest xml result output. Sample <testsuites tests="1" failures="1" disabled="0" errors="0" timestamp="2017-05-24T18:32:10" time="0.012" name="AllTests"> <testsuite name="ConsumerIrHidlTest" tests="1" failures="1" disabled="0" errors="0" time="0.01"> <testcase name="TransmitTest" status="run" time="0.01" classname="ConsumerIrHidlTest"> <failure message="hardware/interfaces..." type=""> <![CDATA[hardware/interfaces...]]> </failure> </testcase> </testsuite> </testsuites> Args: test_case: GtestTestCase object, the test being run. This param is not currently used in this method. command_results: dict of lists, shell command result ''' asserts.assertTrue(command_results, 'Empty command response.') asserts.assertEqual(len(command_results), 3, 'Abnormal command response.') for item in command_results.values(): asserts.assertEqual( len(item), 2, 'Abnormal command result length: %s' % command_results) for stderr in command_results[const.STDERR]: if stderr and stderr.strip(): for line in stderr.split('\n'): logging.error(line) xml_str = command_results[const.STDOUT][1].strip() if self.batch_mode: self._ParseBatchResults(test_case, xml_str) return for stdout in command_results[const.STDOUT]: if stdout and stdout.strip(): for line in stdout.split('\n'): logging.info(line) asserts.assertFalse( command_results[const.EXIT_CODE][1], 'Failed to show Gtest XML output: %s' % command_results) root = xml.etree.ElementTree.fromstring(xml_str) asserts.assertEqual(root.get('tests'), '1', 'No tests available') if root.get('errors') != '0' or root.get('failures') != '0': messages = [x.get('message') for x in root.findall('.//failure')] asserts.fail('\n'.join([x for x in messages if x])) asserts.skipIf(root.get('disabled') == '1', 'Gtest test case disabled')
def GetPathPermission(self, path): '''Get the permission bits of a path, catching IOError.''' permission = '' try: permission = target_file_utils.GetPermission(path, self.shell) except IOError as e: logging.exception(e) asserts.fail('Path "%s" does not exist or has invalid ' 'permission bits' % path) return permission
def testBootImageHeader(self): """Validates boot image header.""" current_boot_partition = "boot" + str(self.slot_suffix) boot_path = target_file_utils.FindFiles( self.shell, BLOCK_DEV_PATH, current_boot_partition, "-type l") logging.info("Boot path %s", boot_path) if not boot_path: asserts.fail("Unable to find path to boot image on device.") host_boot_path = os.path.join(self.temp_dir, "boot.img") self.adb.pull("%s %s" % (boot_path[0], host_boot_path)) self.CheckImageHeader(host_boot_path)
def testNonExistingModule(self): '''Test whether numpy is not installed from default packages. This test assumes numpy is not in default package install list. If this turned otherwise, test logic here should be updated. ''' try: import numpy asserts.fail('numpy should not have been not installed yet.') except ImportError: pass
def setUpClass(self): self.dut = self.android_devices[0] if "arm" in self.dut.cpu_abi: self.ARCH64__NR_name_to_handle_at = 264 self.ARCH64__NR_open_by_handle_at = 265 self.ARCH64__NR_uselib = 1077 elif "x86" in self.dut.cpu_abi: self.ARCH64__NR_name_to_handle_at = 303 self.ARCH64__NR_open_by_handle_at = 304 self.ARCH64__NR_uselib = 134 else: asserts.fail("Unknown CPU ABI: %s" % self.dut.cpu_abi)
def testRecoveryImageHeader(self): """Validates recovery image header.""" asserts.skipIf(self.slot_suffix, "A/B devices do not have a separate recovery partition") recovery_path = target_file_utils.FindFiles(self.shell, BLOCK_DEV_PATH, "recovery", "-type l") logging.info("recovery path %s", recovery_path) if not recovery_path: asserts.fail("Unable to find path to recovery image on device.") host_recovery_path = os.path.join(self.temp_dir, "recovery.img") self.adb.pull("%s %s" % (recovery_path[0], host_recovery_path)) self.CheckImageHeader(host_recovery_path, True)
def testNonExistingModule(self): '''Test whether numpy is not installed from default packages. In the future, if this test fail due to numpy being added to default pip packages, we should pick another module that's not in default pip package list. ''' try: import numpy asserts.fail('numpy should not have been not installed yet.') except ImportError: pass
def testFirstApiLevel(self): """Test that device launched with O or later.""" try: firstApiLevel = self.getProp("ro.product.first_api_level", required=False) if firstApiLevel is None: asserts.skip("ro.product.first_api_level undefined") firstApiLevel = int(firstApiLevel) asserts.assertTrue( firstApiLevel >= ANDROID_O_API_VERSION, "VTS can only be run for new launches in O or above") except ValueError: asserts.fail("Unexpected value returned from getprop")
def testRootDirs(self): """Checks the subdirs under root directory.""" error_msg = [] for dir_path in self._dirs_to_check: current_dirs = self._ListDir(dir_path, "dir") logging.info("Current dirs: %r", current_dirs) unexpected_dirs = current_dirs - set(_ALLOWED_DIRS) error_msg.extend("Unexpected dir: " + d for d in unexpected_dirs) if error_msg: asserts.fail("UNEXPECTED ROOT DIRS:\n%s" % "\n".join(error_msg))
def testRootAllFileTypes(self): """Checks there is no path other than dirs, symlinks and files.""" error_msg = [] for dir_path in self._dirs_to_check: unknown_files = (self._ListDir(dir_path) - self._ListDir(dir_path, "dir") - self._ListDir(dir_path, "symlink") - self._ListDir(dir_path, "file")) error_msg.extend("Unexpected path: " + p for p in unknown_files) if error_msg: asserts.fail("UNEXPECTED ROOT PATHS:\n%s" % "\n".join(error_msg))
def testToast(self): """A sample test controlling Android device with sl4a. Shows a toast message on the device screen. """ logging.info("A toast message should show up on the devce's screen.") try: for index in range(2): self.dut.sl4a.makeToast("Hello World! %s" % index) time.sleep(1) except sl4a_client.ProtocolError as e: asserts.fail("Protocol error in an SL4A operation") except sl4a_client.ApiError as e: asserts.fail("API error during an SL4A API call")