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