Home | History | Annotate | Download | only in tests
      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