| #!/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() |