1#!/usr/bin/env python3
2
3import os
4import unittest
5import sys
6import tempfile
7
8import_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
9import_path = os.path.abspath(os.path.join(import_path, 'utils'))
10sys.path.insert(1, import_path)
11
12from utils import (AOSP_DIR, read_output_content, run_abi_diff,
13                   run_header_abi_dumper)
14from module import Module
15
16
17SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
18INPUT_DIR = os.path.join(SCRIPT_DIR, 'input')
19EXPECTED_DIR = os.path.join(SCRIPT_DIR, 'expected')
20REF_DUMP_DIR = os.path.join(SCRIPT_DIR, 'reference_dumps')
21
22
23def make_and_copy_reference_dumps(module, reference_dump_dir=REF_DUMP_DIR):
24    output_content = module.make_dump()
25
26    dump_dir = os.path.join(reference_dump_dir, module.arch)
27    os.makedirs(dump_dir, exist_ok=True)
28
29    dump_path = os.path.join(dump_dir, module.get_dump_name())
30    with open(dump_path, 'w') as f:
31        f.write(output_content)
32
33    return dump_path
34
35
36class HeaderCheckerTest(unittest.TestCase):
37    @classmethod
38    def setUpClass(cls):
39        cls.maxDiff = None
40
41    def setUp(self):
42        self.tmp_dir = None
43
44    def tearDown(self):
45        if self.tmp_dir:
46            self.tmp_dir.cleanup()
47            self.tmp_dir = None
48
49    def get_tmp_dir(self):
50        if not self.tmp_dir:
51            self.tmp_dir = tempfile.TemporaryDirectory()
52        return self.tmp_dir.name
53
54    def run_and_compare(self, input_path, expected_path, cflags=[]):
55        with open(expected_path, 'r') as f:
56            expected_output = f.read()
57        actual_output = run_header_abi_dumper(input_path, cflags)
58        self.assertEqual(actual_output, expected_output)
59
60    def run_and_compare_name(self, name, cflags=[]):
61        input_path = os.path.join(INPUT_DIR, name)
62        expected_path = os.path.join(EXPECTED_DIR, name)
63        self.run_and_compare(input_path, expected_path, cflags)
64
65    def run_and_compare_name_cpp(self, name, cflags=[]):
66        self.run_and_compare_name(name, cflags + ['-x', 'c++', '-std=c++11'])
67
68    def run_and_compare_name_c_cpp(self, name, cflags=[]):
69        self.run_and_compare_name(name, cflags)
70        self.run_and_compare_name_cpp(name, cflags)
71
72    def run_and_compare_abi_diff(self, old_dump, new_dump, lib, arch,
73                                 expected_return_code, flags=[]):
74        actual_output = run_abi_diff(old_dump, new_dump, arch, lib, flags)
75        self.assertEqual(actual_output, expected_return_code)
76
77    def prepare_and_run_abi_diff(self, old_ref_dump_path, new_ref_dump_path,
78                                 target_arch, expected_return_code, flags=[]):
79        self.run_and_compare_abi_diff(old_ref_dump_path, new_ref_dump_path,
80                                      'test', target_arch,
81                                      expected_return_code, flags)
82
83    def get_or_create_ref_dump(self, module, create):
84        if create:
85            return make_and_copy_reference_dumps(module, self.get_tmp_dir())
86        return os.path.join(REF_DUMP_DIR, module.arch, module.get_dump_name())
87
88    def prepare_and_run_abi_diff_all_archs(self, old_lib, new_lib,
89                                           expected_return_code, flags=[],
90                                           create_old=False, create_new=True):
91        old_modules = Module.get_test_modules_by_name(old_lib)
92        new_modules = Module.get_test_modules_by_name(new_lib)
93        self.assertEqual(len(old_modules), len(new_modules))
94
95        for old_module, new_module in zip(old_modules, new_modules):
96            self.assertEqual(old_module.arch, new_module.arch)
97            old_ref_dump_path = self.get_or_create_ref_dump(old_module,
98                                                            create_old)
99            new_ref_dump_path = self.get_or_create_ref_dump(new_module,
100                                                            create_new)
101            self.prepare_and_run_abi_diff(
102                old_ref_dump_path, new_ref_dump_path, new_module.arch,
103                expected_return_code, flags)
104
105    def prepare_and_absolute_diff_all_archs(self, old_lib, new_lib):
106        old_modules = Module.get_test_modules_by_name(old_lib)
107        new_modules = Module.get_test_modules_by_name(new_lib)
108        self.assertEqual(len(old_modules), len(new_modules))
109
110        for old_module, new_module in zip(old_modules, new_modules):
111            self.assertEqual(old_module.arch, new_module.arch)
112            old_ref_dump_path = self.get_or_create_ref_dump(old_module, False)
113            new_ref_dump_path = self.get_or_create_ref_dump(new_module, True)
114            self.assertEqual(
115                read_output_content(old_ref_dump_path, AOSP_DIR),
116                read_output_content(new_ref_dump_path, AOSP_DIR))
117
118    def test_example1_cpp(self):
119        self.run_and_compare_name_cpp('example1.cpp')
120
121    def test_example1_h(self):
122        self.run_and_compare_name_cpp('example1.h')
123
124    def test_example2_h(self):
125        self.run_and_compare_name_cpp('example2.h')
126
127    def test_example3_h(self):
128        self.run_and_compare_name_cpp('example3.h')
129
130    def test_undeclared_types_h(self):
131        self.prepare_and_absolute_diff_all_archs(
132            'undeclared_types.h', 'undeclared_types.h')
133
134    def test_known_issues_h(self):
135        self.prepare_and_absolute_diff_all_archs(
136            'known_issues.h', 'known_issues.h')
137
138    def test_libc_and_cpp(self):
139        self.prepare_and_run_abi_diff_all_archs(
140            "libc_and_cpp", "libc_and_cpp", 0)
141
142    def test_libc_and_cpp_and_libc_and_cpp_with_unused_struct(self):
143        self.prepare_and_run_abi_diff_all_archs(
144            "libc_and_cpp", "libc_and_cpp_with_unused_struct", 0)
145
146    def test_libc_and_cpp_and_libc_and_cpp_with_unused_struct_allow(self):
147        self.prepare_and_run_abi_diff_all_archs(
148            "libc_and_cpp", "libc_and_cpp_with_unused_struct", 0,
149            ["-allow-unreferenced-changes"])
150
151    def test_libc_and_cpp_and_libc_and_cpp_with_unused_struct_check_all(self):
152        self.prepare_and_run_abi_diff_all_archs(
153            "libc_and_cpp", "libc_and_cpp_with_unused_struct", 1,
154            ['-check-all-apis'])
155
156    def test_libc_and_cpp_with_unused_struct_and_libc_and_cpp_with_unused_cstruct(
157            self):
158        self.prepare_and_run_abi_diff_all_archs(
159            "libc_and_cpp_with_unused_struct",
160            "libc_and_cpp_with_unused_cstruct", 0,
161            ['-check-all-apis', '-allow-unreferenced-changes'])
162
163    def test_libc_and_cpp_and_libc_and_cpp_with_unused_struct_check_all_advice(
164            self):
165        self.prepare_and_run_abi_diff_all_archs(
166            "libc_and_cpp", "libc_and_cpp_with_unused_struct", 0,
167            ['-check-all-apis', '-advice-only'])
168
169    def test_libc_and_cpp_opaque_pointer_diff(self):
170        self.prepare_and_run_abi_diff_all_archs(
171            "libc_and_cpp_with_opaque_ptr_a",
172            "libc_and_cpp_with_opaque_ptr_b", 8,
173            ['-consider-opaque-types-different'], True, True)
174
175    def test_libgolden_cpp_return_type_diff(self):
176        self.prepare_and_run_abi_diff_all_archs(
177            "libgolden_cpp", "libgolden_cpp_return_type_diff", 8)
178
179    def test_libgolden_cpp_add_odr(self):
180        self.prepare_and_run_abi_diff_all_archs(
181            "libgolden_cpp", "libgolden_cpp_odr", 0,
182            ['-check-all-apis', '-allow-unreferenced-changes'])
183
184    def test_libgolden_cpp_add_function(self):
185        self.prepare_and_run_abi_diff_all_archs(
186            "libgolden_cpp", "libgolden_cpp_add_function", 4)
187
188    def test_libgolden_cpp_add_function_allow_extension(self):
189        self.prepare_and_run_abi_diff_all_archs(
190            "libgolden_cpp", "libgolden_cpp_add_function", 0,
191            ['-allow-extensions'])
192
193    def test_libgolden_cpp_add_function_and_elf_symbol(self):
194        self.prepare_and_run_abi_diff_all_archs(
195            "libgolden_cpp", "libgolden_cpp_add_function_and_unexported_elf",
196            4)
197
198    def test_libgolden_cpp_fabricated_function_ast_removed_diff(self):
199        self.prepare_and_run_abi_diff_all_archs(
200            "libgolden_cpp_add_function_sybmol_only",
201            "libgolden_cpp_add_function", 0, [], False, False)
202
203    def test_libgolden_cpp_change_function_access(self):
204        self.prepare_and_run_abi_diff_all_archs(
205            "libgolden_cpp", "libgolden_cpp_change_function_access", 8)
206
207    def test_libgolden_cpp_add_global_variable(self):
208        self.prepare_and_run_abi_diff_all_archs(
209            "libgolden_cpp", "libgolden_cpp_add_global_variable", 4)
210
211    def test_libgolden_cpp_change_global_var_access(self):
212        self.prepare_and_run_abi_diff_all_archs(
213            "libgolden_cpp_add_global_variable",
214            "libgolden_cpp_add_global_variable_private", 8)
215
216    def test_libgolden_cpp_parameter_type_diff(self):
217        self.prepare_and_run_abi_diff_all_archs(
218            "libgolden_cpp", "libgolden_cpp_parameter_type_diff", 8)
219
220    def test_libgolden_cpp_with_vtable_diff(self):
221        self.prepare_and_run_abi_diff_all_archs(
222            "libgolden_cpp", "libgolden_cpp_vtable_diff", 8)
223
224    def test_libgolden_cpp_member_diff_advice_only(self):
225        self.prepare_and_run_abi_diff_all_archs(
226            "libgolden_cpp", "libgolden_cpp_member_diff", 0, ['-advice-only'])
227
228    def test_libgolden_cpp_member_diff(self):
229        self.prepare_and_run_abi_diff_all_archs(
230            "libgolden_cpp", "libgolden_cpp_member_diff", 8)
231
232    def test_libgolden_cpp_change_member_access(self):
233        self.prepare_and_run_abi_diff_all_archs(
234            "libgolden_cpp", "libgolden_cpp_change_member_access", 8)
235
236    def test_libgolden_cpp_enum_extended(self):
237        self.prepare_and_run_abi_diff_all_archs(
238            "libgolden_cpp", "libgolden_cpp_enum_extended", 4)
239
240    def test_libgolden_cpp_enum_diff(self):
241        self.prepare_and_run_abi_diff_all_archs(
242            "libgolden_cpp", "libgolden_cpp_enum_diff", 8)
243
244    def test_libgolden_cpp_member_fake_diff(self):
245        self.prepare_and_run_abi_diff_all_archs(
246            "libgolden_cpp", "libgolden_cpp_member_fake_diff", 0)
247
248    def test_libgolden_cpp_member_integral_type_diff(self):
249        self.prepare_and_run_abi_diff_all_archs(
250            "libgolden_cpp", "libgolden_cpp_member_integral_type_diff", 8)
251
252    def test_libgolden_cpp_member_cv_diff(self):
253        self.prepare_and_run_abi_diff_all_archs(
254            "libgolden_cpp", "libgolden_cpp_member_cv_diff", 8)
255
256    def test_libgolden_cpp_unreferenced_elf_symbol_removed(self):
257        self.prepare_and_run_abi_diff_all_archs(
258            "libgolden_cpp", "libgolden_cpp_unreferenced_elf_symbol_removed",
259            16)
260
261    def test_libreproducability(self):
262        self.prepare_and_absolute_diff_all_archs(
263            "libreproducability", "libreproducability")
264
265    def test_libgolden_cpp_member_name_changed(self):
266        self.prepare_and_run_abi_diff_all_archs(
267            "libgolden_cpp", "libgolden_cpp_member_name_changed", 0)
268
269    def test_libgolden_cpp_member_function_pointer_changed(self):
270        self.prepare_and_run_abi_diff_all_archs(
271            "libgolden_cpp_function_pointer",
272            "libgolden_cpp_function_pointer_parameter_added", 8, [],
273            True, True)
274
275    def test_libgolden_cpp_internal_struct_access_upgraded(self):
276        self.prepare_and_run_abi_diff_all_archs(
277            "libgolden_cpp_internal_private_struct",
278            "libgolden_cpp_internal_public_struct", 0, [], True, True)
279
280    def test_libgolden_cpp_internal_struct_access_downgraded(self):
281        self.prepare_and_run_abi_diff_all_archs(
282            "libgolden_cpp_internal_public_struct",
283            "libgolden_cpp_internal_private_struct", 8, [], True, True)
284
285    def test_libgolden_cpp_inheritance_type_changed(self):
286        self.prepare_and_run_abi_diff_all_archs(
287            "libgolden_cpp", "libgolden_cpp_inheritance_type_changed", 8, [],
288            True, True)
289
290    def test_libpure_virtual_function(self):
291        self.prepare_and_absolute_diff_all_archs(
292            "libpure_virtual_function", "libpure_virtual_function")
293
294    def test_libc_and_cpp_in_json(self):
295        self.prepare_and_absolute_diff_all_archs(
296            "libgolden_cpp_json", "libgolden_cpp_json")
297
298    def test_libc_and_cpp_in_protobuf_and_json(self):
299        self.prepare_and_run_abi_diff_all_archs(
300            "libgolden_cpp", "libgolden_cpp_json", 0,
301            ["-input-format-old", "ProtobufTextFormat",
302             "-input-format-new", "Json"])
303
304    def test_opaque_type_self_diff(self):
305        lsdump = os.path.join(
306            SCRIPT_DIR, "abi_dumps", "opaque_ptr_types.lsdump")
307        self.run_and_compare_abi_diff(
308            lsdump, lsdump, "libexample", "arm64", 0,
309            ["-input-format-old", "Json", "-input-format-new", "Json",
310             "-consider-opaque-types-different"])
311
312    def test_allow_adding_removing_weak_symbols(self):
313        module_old = Module.get_test_modules_by_name("libweak_symbols_old")[0]
314        module_new = Module.get_test_modules_by_name("libweak_symbols_new")[0]
315        lsdump_old = self.get_or_create_ref_dump(module_old, False)
316        lsdump_new = self.get_or_create_ref_dump(module_new, False)
317
318        options = ["-input-format-old", "Json", "-input-format-new", "Json"]
319
320        # If `-allow-adding-removing-weak-symbols` is not specified, removing a
321        # weak symbol must be treated as an incompatible change.
322        self.run_and_compare_abi_diff(
323            lsdump_old, lsdump_new, "libweak_symbols", "arm64", 8, options)
324
325        # If `-allow-adding-removing-weak-symbols` is specified, removing a
326        # weak symbol must be fine and mustn't be a fatal error.
327        self.run_and_compare_abi_diff(
328            lsdump_old, lsdump_new, "libweak_symbols", "arm64", 0,
329            options + ["-allow-adding-removing-weak-symbols"])
330
331    def test_linker_shared_object_file_and_version_script(self):
332        base_dir = os.path.join(
333            SCRIPT_DIR, 'integration', 'version_script_example')
334
335        cases = [
336            'libversion_script_example',
337            'libversion_script_example_no_mytag',
338            'libversion_script_example_no_private',
339        ]
340
341        for module_name in cases:
342            module = Module.get_test_modules_by_name(module_name)[0]
343            example_lsdump_old = self.get_or_create_ref_dump(module, False)
344            example_lsdump_new = self.get_or_create_ref_dump(module, True)
345            self.run_and_compare_abi_diff(
346                example_lsdump_old, example_lsdump_new,
347                module_name, "arm64", 0,
348                ["-input-format-old", "Json", "-input-format-new", "Json"])
349
350    def test_no_source(self):
351        self.prepare_and_run_abi_diff_all_archs(
352            "libempty", "libempty", 0,
353            ["-input-format-old", "Json", "-input-format-new", "Json"])
354
355    def test_golden_anonymous_enum(self):
356        self.prepare_and_absolute_diff_all_archs(
357            "libgolden_anonymous_enum", "libgolden_anonymous_enum")
358
359    def test_swap_anonymous_enum(self):
360        self.prepare_and_run_abi_diff_all_archs(
361            "libgolden_anonymous_enum", "libswap_anonymous_enum", 0,
362            ["-input-format-old", "Json", "-input-format-new", "Json",
363             "-check-all-apis"])
364
365    def test_swap_anonymous_enum_field(self):
366        self.prepare_and_run_abi_diff_all_archs(
367            "libgolden_anonymous_enum", "libswap_anonymous_enum_field", 0,
368            ["-input-format-old", "Json", "-input-format-new", "Json",
369             "-check-all-apis"])
370
371    def test_anonymous_enum_odr(self):
372        self.prepare_and_absolute_diff_all_archs(
373            "libanonymous_enum_odr", "libanonymous_enum_odr")
374
375
376if __name__ == '__main__':
377    unittest.main()
378