#!/usr/bin/env python3 from __future__ import print_function import argparse import collections import difflib import os import re import subprocess import sys import unittest from compat import TemporaryDirectory, makedirs import ndk_toolchain SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) VNDK_DEF_TOOL = os.path.join(SCRIPT_DIR, '..', 'vndk_definition_tool.py') INPUT_DIR = os.path.join(SCRIPT_DIR ,'testdata', 'test_elfdump', 'input') EXPECTED_DIR = os.path.join(SCRIPT_DIR, 'testdata', 'test_elfdump', 'expected') test_dir_base = None def run_elf_dump(path): cmd = [sys.executable, VNDK_DEF_TOOL, 'elfdump', path] return subprocess.check_output(cmd, universal_newlines=True) class ELFDumpTest(unittest.TestCase): @classmethod def setUpClass(cls): cls.targets = ndk_toolchain.create_targets() if test_dir_base: cls.test_dir_base = test_dir_base else: cls.tmp_dir = TemporaryDirectory() cls.test_dir_base = cls.tmp_dir.name cls._build_fixtures(cls.target_name) @classmethod def tearDownClass(cls): if not test_dir_base: cls.tmp_dir.cleanup() @classmethod def _build_fixtures(cls, target_name): target = cls.targets[target_name] cls.expected_dir = os.path.join(EXPECTED_DIR, target_name) cls.test_dir = os.path.join(cls.test_dir_base, target_name) makedirs(cls.test_dir, exist_ok=True) # Compile main.o. src_file = os.path.join(INPUT_DIR, 'main.c') obj_file = os.path.join(cls.test_dir, 'main.o') target.compile(obj_file, src_file, []) # Link main.out. out_file = os.path.join(cls.test_dir, 'main.out') target.link(out_file, [obj_file], ['-ldl', '-lc', '-lstdc++']) # Compile test.o. src_file = os.path.join(INPUT_DIR, 'test.c') obj_file = os.path.join(cls.test_dir, 'test.o') target.compile(obj_file, src_file, []) # Link libtest.so. out_file = os.path.join(cls.test_dir, 'libtest.so') target.link(out_file, [obj_file], ['-shared', '-lc']) # Link libtest-rpath.so. out_file = os.path.join(cls.test_dir, 'libtest-rpath.so') target.link(out_file, [obj_file], ['-shared', '-lc', '-Wl,-rpath,$ORIGIN/../lib', '-Wl,--disable-new-dtags']) # Link libtest-rpath-multi.so. out_file = os.path.join(cls.test_dir, 'libtest-rpath-multi.so') target.link(out_file, [obj_file], ['-shared', '-lc', '-Wl,-rpath,/system/lib:/vendor/lib', '-Wl,--disable-new-dtags']) # Link libtest-runpath.so. out_file = os.path.join(cls.test_dir, 'libtest-runpath.so') target.link(out_file, [obj_file], ['-shared', '-lc', '-Wl,-rpath,$ORIGIN/../lib', '-Wl,--enable-new-dtags']) # Link libtest-runpath-multi.so. out_file = os.path.join(cls.test_dir, 'libtest-runpath-multi.so') target.link(out_file, [obj_file], ['-shared', '-lc', '-Wl,-rpath,/system/lib:/vendor/lib', '-Wl,--enable-new-dtags']) def _remove_size_lines(self, lines): """Remove file size information because they may vary.""" prefixes = ( 'FILE_SIZE\t', 'RO_SEG_FILE_SIZE\t', 'RO_SEG_MEM_SIZE\t', 'RW_SEG_FILE_SIZE\t', 'RW_SEG_MEM_SIZE\t', ) patt = re.compile('|'.join('(?:' + re.escape(x) +')' for x in prefixes)) return [line for line in lines if not patt.match(line)] def _assert_equal_to_file(self, expected_file_name, actual): actual = actual.splitlines(True) expected_file_path = os.path.join(self.expected_dir, expected_file_name) with open(expected_file_path, 'r') as f: expected = f.readlines() self.assertEqual(self._remove_size_lines(expected), self._remove_size_lines(actual)) def _test_main_out(self): out_file = os.path.join(self.test_dir, 'main.out') self._assert_equal_to_file('main.out.txt', run_elf_dump(out_file)) def _test_libtest(self, expected_file_name, lib_name): lib_file = os.path.join(self.test_dir, lib_name) self._assert_equal_to_file(expected_file_name, run_elf_dump(lib_file)) def create_target_test(target_name): def test_main(self): self._test_main_out() def test_libtest(self): self._test_libtest('libtest.so.txt', 'libtest.so') def test_libtest_rpath(self): self._test_libtest('libtest-rpath.so.txt', 'libtest-rpath.so') def test_libtest_rpath_multi(self): self._test_libtest('libtest-rpath-multi.so.txt', 'libtest-rpath-multi.so') def test_libtest_runpath(self): self._test_libtest('libtest-runpath.so.txt', 'libtest-runpath.so') def test_libtest_runpath_multi(self): self._test_libtest('libtest-runpath-multi.so.txt', 'libtest-runpath-multi.so') class_name = 'ELFDumpTest_' + target_name globals()[class_name] = type( class_name, (ELFDumpTest,), dict(test_main=test_main, test_libtest=test_libtest, test_libtest_rpath=test_libtest_rpath, test_libtest_rpath_multi=test_libtest_rpath_multi, test_libtest_runpath=test_libtest_runpath, test_libtest_runpath_multi=test_libtest_runpath_multi, target_name=target_name)) for target in ('arm', 'arm64', 'mips', 'mips64', 'x86', 'x86_64'): create_target_test(target) def main(): # Parse command line arguments. parser = argparse.ArgumentParser() parser.add_argument('--test-dir', help='directory for temporary files') parser.add_argument('--expected-dir', help='directory with expected output') args, unittest_args = parser.parse_known_args() # Convert command line options. global expected_dir global test_dir_base if args.expected_dir: expected_dir = args.expected_dir if args.test_dir: test_dir_base = args.test_dir # Run unit test. unittest.main(argv=[sys.argv[0]] + unittest_args) if __name__ == '__main__': main()