1 #!/usr/bin/env python3 2 3 from __future__ import print_function 4 5 import argparse 6 import collections 7 import difflib 8 import os 9 import re 10 import subprocess 11 import sys 12 import unittest 13 14 from compat import TemporaryDirectory, makedirs 15 import ndk_toolchain 16 17 18 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) 19 VNDK_DEF_TOOL = os.path.join(SCRIPT_DIR, '..', 'vndk_definition_tool.py') 20 21 INPUT_DIR = os.path.join(SCRIPT_DIR ,'testdata', 'test_elfdump', 'input') 22 EXPECTED_DIR = os.path.join(SCRIPT_DIR, 'testdata', 'test_elfdump', 'expected') 23 test_dir_base = None 24 25 26 def run_elf_dump(path): 27 cmd = [sys.executable, VNDK_DEF_TOOL, 'elfdump', path] 28 return subprocess.check_output(cmd, universal_newlines=True) 29 30 31 class ELFDumpTest(unittest.TestCase): 32 @classmethod 33 def setUpClass(cls): 34 cls.targets = ndk_toolchain.create_targets() 35 36 if test_dir_base: 37 cls.test_dir_base = test_dir_base 38 else: 39 cls.tmp_dir = TemporaryDirectory() 40 cls.test_dir_base = cls.tmp_dir.name 41 42 cls._build_fixtures(cls.target_name) 43 44 @classmethod 45 def tearDownClass(cls): 46 if not test_dir_base: 47 cls.tmp_dir.cleanup() 48 49 @classmethod 50 def _build_fixtures(cls, target_name): 51 target = cls.targets[target_name] 52 53 cls.expected_dir = os.path.join(EXPECTED_DIR, target_name) 54 cls.test_dir = os.path.join(cls.test_dir_base, target_name) 55 56 makedirs(cls.test_dir, exist_ok=True) 57 58 # Compile main.o. 59 src_file = os.path.join(INPUT_DIR, 'main.c') 60 obj_file = os.path.join(cls.test_dir, 'main.o') 61 target.compile(obj_file, src_file, []) 62 63 # Link main.out. 64 out_file = os.path.join(cls.test_dir, 'main.out') 65 target.link(out_file, [obj_file], ['-ldl', '-lc', '-lstdc++']) 66 67 # Compile test.o. 68 src_file = os.path.join(INPUT_DIR, 'test.c') 69 obj_file = os.path.join(cls.test_dir, 'test.o') 70 target.compile(obj_file, src_file, []) 71 72 # Link libtest.so. 73 out_file = os.path.join(cls.test_dir, 'libtest.so') 74 target.link(out_file, [obj_file], ['-shared', '-lc']) 75 76 # Link libtest-rpath.so. 77 out_file = os.path.join(cls.test_dir, 'libtest-rpath.so') 78 target.link(out_file, [obj_file], 79 ['-shared', '-lc', '-Wl,-rpath,$ORIGIN/../lib', 80 '-Wl,--disable-new-dtags']) 81 82 # Link libtest-rpath-multi.so. 83 out_file = os.path.join(cls.test_dir, 'libtest-rpath-multi.so') 84 target.link(out_file, [obj_file], 85 ['-shared', '-lc', '-Wl,-rpath,/system/lib:/vendor/lib', 86 '-Wl,--disable-new-dtags']) 87 88 # Link libtest-runpath.so. 89 out_file = os.path.join(cls.test_dir, 'libtest-runpath.so') 90 target.link(out_file, [obj_file], 91 ['-shared', '-lc', '-Wl,-rpath,$ORIGIN/../lib', 92 '-Wl,--enable-new-dtags']) 93 94 # Link libtest-runpath-multi.so. 95 out_file = os.path.join(cls.test_dir, 'libtest-runpath-multi.so') 96 target.link(out_file, [obj_file], 97 ['-shared', '-lc', '-Wl,-rpath,/system/lib:/vendor/lib', 98 '-Wl,--enable-new-dtags']) 99 100 def _remove_size_lines(self, lines): 101 """Remove file size information because they may vary.""" 102 prefixes = ( 103 'FILE_SIZE\t', 104 'RO_SEG_FILE_SIZE\t', 105 'RO_SEG_MEM_SIZE\t', 106 'RW_SEG_FILE_SIZE\t', 107 'RW_SEG_MEM_SIZE\t', 108 ) 109 patt = re.compile('|'.join('(?:' + re.escape(x) +')' for x in prefixes)) 110 return [line for line in lines if not patt.match(line)] 111 112 def _assert_equal_to_file(self, expected_file_name, actual): 113 actual = actual.splitlines(True) 114 expected_file_path = os.path.join(self.expected_dir, expected_file_name) 115 with open(expected_file_path, 'r') as f: 116 expected = f.readlines() 117 self.assertEqual(self._remove_size_lines(expected), 118 self._remove_size_lines(actual)) 119 120 def _test_main_out(self): 121 out_file = os.path.join(self.test_dir, 'main.out') 122 self._assert_equal_to_file('main.out.txt', run_elf_dump(out_file)) 123 124 def _test_libtest(self, expected_file_name, lib_name): 125 lib_file = os.path.join(self.test_dir, lib_name) 126 self._assert_equal_to_file(expected_file_name, run_elf_dump(lib_file)) 127 128 129 def create_target_test(target_name): 130 def test_main(self): 131 self._test_main_out() 132 133 def test_libtest(self): 134 self._test_libtest('libtest.so.txt', 'libtest.so') 135 136 def test_libtest_rpath(self): 137 self._test_libtest('libtest-rpath.so.txt', 'libtest-rpath.so') 138 139 def test_libtest_rpath_multi(self): 140 self._test_libtest('libtest-rpath-multi.so.txt', 141 'libtest-rpath-multi.so') 142 143 def test_libtest_runpath(self): 144 self._test_libtest('libtest-runpath.so.txt', 'libtest-runpath.so') 145 146 def test_libtest_runpath_multi(self): 147 self._test_libtest('libtest-runpath-multi.so.txt', 148 'libtest-runpath-multi.so') 149 150 class_name = 'ELFDumpTest_' + target_name 151 globals()[class_name] = type( 152 class_name, (ELFDumpTest,), 153 dict(test_main=test_main, 154 test_libtest=test_libtest, 155 test_libtest_rpath=test_libtest_rpath, 156 test_libtest_rpath_multi=test_libtest_rpath_multi, 157 test_libtest_runpath=test_libtest_runpath, 158 test_libtest_runpath_multi=test_libtest_runpath_multi, 159 target_name=target_name)) 160 161 162 for target in ('arm', 'arm64', 'mips', 'mips64', 'x86', 'x86_64'): 163 create_target_test(target) 164 165 166 def main(): 167 # Parse command line arguments. 168 parser = argparse.ArgumentParser() 169 parser.add_argument('--test-dir', help='directory for temporary files') 170 parser.add_argument('--expected-dir', help='directory with expected output') 171 args, unittest_args = parser.parse_known_args() 172 173 # Convert command line options. 174 global expected_dir 175 global test_dir_base 176 177 if args.expected_dir: 178 expected_dir = args.expected_dir 179 if args.test_dir: 180 test_dir_base = args.test_dir 181 182 # Run unit test. 183 unittest.main(argv=[sys.argv[0]] + unittest_args) 184 185 if __name__ == '__main__': 186 main() 187