• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 subprocess
17import tempfile
18import unittest
19from pathlib import Path
20
21from pw_build_info import build_id
22
23# Since build_id.cc depends on pw_preprocessor, we have to use the in-tree path.
24_MODULE_DIR = Path(__file__).parent.parent.resolve()
25_MODULE_PY_DIR = Path(__file__).parent.resolve()
26
27_SHA1_BUILD_ID_LENGTH = 20
28
29
30class TestGnuBuildId(unittest.TestCase):
31    """Unit tests for GNU build ID parsing."""
32
33    def test_build_id_correctness(self):
34        """Tests to ensure GNU build IDs are read/written correctly."""
35        with tempfile.TemporaryDirectory() as exe_dir:
36            exe_file = Path(exe_dir) / 'print_build_id.elf'
37
38            # Compiles a binary that prints the embedded GNU build id.
39            cmd = [
40                'clang++',
41                'build_id.cc',
42                _MODULE_PY_DIR / 'print_build_id.cc',
43                '-Ipublic',
44                '-I../pw_polyfill/public',
45                '-I../pw_preprocessor/public',
46                '-I../pw_span/public',
47                '-std=c++17',
48                '-fuse-ld=lld',
49                '-Wl,-Tadd_build_id_to_default_linker_script.ld',
50                '-Wl,--build-id=sha1',
51                '-o',
52                exe_file,
53            ]
54
55            process = subprocess.run(
56                cmd,
57                stdout=subprocess.PIPE,
58                stderr=subprocess.STDOUT,
59                cwd=_MODULE_DIR,
60            )
61            self.assertEqual(
62                process.returncode, 0, process.stdout.decode(errors='replace')
63            )
64
65            # Run the compiled binary so the printed build ID can be read.
66            process = subprocess.run(
67                [exe_file],
68                stdout=subprocess.PIPE,
69                stderr=subprocess.STDOUT,
70                cwd=_MODULE_DIR,
71            )
72            self.assertEqual(process.returncode, 0)
73
74            with open(exe_file, 'rb') as elf:
75                expected = build_id.read_build_id_from_section(elf)
76                self.assertEqual(len(expected), _SHA1_BUILD_ID_LENGTH)
77                self.assertEqual(
78                    process.stdout.decode().rstrip(), expected.hex()
79                )
80
81                # Test method that parses using symbol information.
82                expected = build_id.read_build_id_from_symbol(elf)
83                self.assertEqual(len(expected), _SHA1_BUILD_ID_LENGTH)
84                self.assertEqual(
85                    process.stdout.decode().rstrip(), expected.hex()
86                )
87
88                # Test the user-facing method.
89                expected = build_id.read_build_id(elf)
90                self.assertEqual(len(expected), _SHA1_BUILD_ID_LENGTH)
91                self.assertEqual(
92                    process.stdout.decode().rstrip(), expected.hex()
93                )
94
95
96if __name__ == '__main__':
97    unittest.main()
98