xref: /aosp_15_r20/external/pigweed/pw_build_info/py/build_id_test.py (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1# Copyright 2021 The Pigweed Authors
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4# use this file except in compliance with the License. You may obtain a copy of
5# the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations under
13# the License.
14"""Tests for pw_build_info's GNU build ID support."""
15
16import os
17import subprocess
18from pathlib import Path
19import tempfile
20import unittest
21
22from pw_build_info import build_id
23
24# Since build_id.cc depends on pw_preprocessor, we have to use the in-tree path
25# to reference the dependent source files.
26_MODULE_DIR = Path(__file__).resolve().parent.parent
27_MODULE_PY_DIR = Path(__file__).resolve().parent
28
29_SHA1_BUILD_ID_LENGTH = 20
30
31
32class TestGnuBuildId(unittest.TestCase):
33    """Unit tests for GNU build ID parsing."""
34
35    def test_build_id_correctness(self):
36        """Tests to ensure GNU build IDs are read/written correctly."""
37        self.assertTrue('PW_PIGWEED_CIPD_INSTALL_DIR' in os.environ)
38        sysroot = Path(os.environ['PW_PIGWEED_CIPD_INSTALL_DIR']).joinpath(
39            "clang_sysroot"
40        )
41        with tempfile.TemporaryDirectory() as exe_dir:
42            exe_file = Path(exe_dir) / 'print_build_id.elf'
43
44            # Compiles a binary that prints the embedded GNU build id.
45            cmd = [
46                'clang++',
47                _MODULE_DIR / 'build_id.cc',
48                _MODULE_PY_DIR / 'print_build_id.cc',
49                f'-I{_MODULE_DIR}/public',
50                f'-I{_MODULE_DIR}/../pw_polyfill/public',
51                f'-I{_MODULE_DIR}/../pw_preprocessor/public',
52                f'-I{_MODULE_DIR}/../pw_span/public',
53                '--sysroot=%s' % sysroot,
54                '-std=c++17',
55                '-fuse-ld=lld',
56                f'-Wl,-T{_MODULE_DIR}/add_build_id_to_default_linker_script.ld',
57                f'-Wl,-L{_MODULE_DIR}',
58                '-Wl,--build-id=sha1',
59                '-o',
60                exe_file,
61            ]
62
63            process = subprocess.run(
64                cmd,
65                stdout=subprocess.PIPE,
66                stderr=subprocess.STDOUT,
67            )
68            self.assertEqual(
69                process.returncode, 0, process.stdout.decode(errors='replace')
70            )
71
72            # Run the compiled binary so the printed build ID can be read.
73            process = subprocess.run(
74                [exe_file],
75                stdout=subprocess.PIPE,
76                stderr=subprocess.STDOUT,
77            )
78            self.assertEqual(process.returncode, 0)
79
80            with open(exe_file, 'rb') as elf:
81                expected = build_id.read_build_id_from_section(elf)
82                self.assertEqual(len(expected), _SHA1_BUILD_ID_LENGTH)
83                self.assertEqual(
84                    process.stdout.decode().rstrip(), expected.hex()
85                )
86
87                # Test method that parses using symbol information.
88                expected = build_id.read_build_id_from_symbol(elf)
89                self.assertEqual(len(expected), _SHA1_BUILD_ID_LENGTH)
90                self.assertEqual(
91                    process.stdout.decode().rstrip(), expected.hex()
92                )
93
94                # Test the user-facing method.
95                expected = build_id.read_build_id(elf)
96                self.assertEqual(len(expected), _SHA1_BUILD_ID_LENGTH)
97                self.assertEqual(
98                    process.stdout.decode().rstrip(), expected.hex()
99                )
100
101
102if __name__ == '__main__':
103    unittest.main()
104