| #! /usr/bin/env python3 |
| # Copyright 2020 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| # Unit tests for remote_link. |
| # |
| # Usage: |
| # |
| # tools/clang/scripts/remote_link_unit_tests.py |
| # |
| # A coverage report combining these tests with the integration tests |
| # in remote_link_integration_tests.py can be generated by running: |
| # |
| # env COVERAGE_FILE=.coverage.unit python3 third_party/pycoverage run \ |
| # tools/clang/scripts/remote_link_unit_tests.py |
| # env COVERAGE_FILE=.coverage.integration python3 third_party/pycoverage \ |
| # run tools/clang/scripts/remote_link_integration_tests.py |
| # python3 third_party/pycoverage combine |
| # python3 third_party/pycoverage html |
| # |
| # The report will be available as htmlcov/index.html |
| |
| import remote_ld |
| import remote_link |
| |
| import os |
| import unittest |
| from unittest import mock |
| |
| from remote_link_test_utils import named_directory, working_directory |
| |
| |
| class FakeFs(object): |
| """ |
| Context manager that mocks the functions through which remote_link |
| interacts with the filesystem. |
| """ |
| |
| def __init__(self, bitcode_files=None, other_files=None): |
| self.bitcode_files = set(bitcode_files or []) |
| self.other_files = set(other_files or []) |
| |
| def ensure_file(path): |
| self.other_files.add(path) |
| |
| def exists(path): |
| return path in self.bitcode_files or path in self.other_files |
| |
| def is_bitcode_file(path): |
| return path in self.bitcode_files |
| |
| self.mock_ensure_file = mock.patch('remote_link.ensure_file', ensure_file) |
| self.mock_exists = mock.patch('os.path.exists', exists) |
| self.mock_is_bitcode_file = mock.patch('remote_link.is_bitcode_file', |
| is_bitcode_file) |
| |
| def __enter__(self): |
| self.mock_ensure_file.start() |
| self.mock_exists.start() |
| self.mock_is_bitcode_file.start() |
| return self |
| |
| def __exit__(self, exnty, *args, **kwargs): |
| self.mock_is_bitcode_file.stop() |
| self.mock_exists.stop() |
| self.mock_ensure_file.stop() |
| return exnty is None |
| |
| |
| class RemoteLinkUnitTest(unittest.TestCase): |
| """ |
| Unit tests for remote_link. |
| """ |
| |
| def test_analyze_expanded_args_nocodegen(self): |
| with FakeFs(other_files=['foo.o', 'bar.o']): |
| self.assertIsNone(remote_ld.RemoteLinkUnix().analyze_expanded_args( |
| ['clang', 'foo.o', 'bar.o', '-o', 'foo'], 'foo', 'clang', 'lto.foo', |
| 'common', False)) |
| |
| def test_analyze_expanded_args_one_codegen(self): |
| with FakeFs(bitcode_files=['foo.o'], other_files=['bar.o']): |
| result = remote_ld.RemoteLinkUnix().analyze_expanded_args( |
| ['clang', 'foo.o', 'bar.o', '-o', 'foo'], 'foo', 'clang', 'lto.foo', |
| 'common', False) |
| self.assertIsNotNone(result) |
| self.assertNotEqual(len(result.codegen), 0) |
| self.assertEqual(result.codegen[0][1], 'foo.o') |
| self.assertEqual(len(result.codegen), 1) |
| self.assertIn('foo.o', result.index_params) |
| self.assertIn('bar.o', result.index_params) |
| self.assertIn('bar.o', result.final_params) |
| # foo.o should not be in final_params because it will be added via |
| # the used object file. |
| self.assertNotIn('foo.o', result.final_params) |
| |
| def test_analyze_expanded_args_params(self): |
| with FakeFs(bitcode_files=['foo.o']): |
| result = remote_ld.RemoteLinkUnix().analyze_expanded_args([ |
| 'clang', '-O2', '--target=arm-none-eabi', '-march=armv7-a', |
| '-flto=thin', '-fsplit-lto-unit', '-fwhole-program-vtables', |
| '-fsanitize=cfi', '-g', '-gsplit-dwarf', '-mllvm', |
| '-generate-type-units', 'foo.o', '-o', 'foo' |
| ], 'foo', 'clang', 'lto.foo', 'common', False) |
| self.assertIsNotNone(result) |
| self.assertIn('-Wl,-plugin-opt=obj-path=lto.foo/foo.split.o', |
| result.index_params) |
| self.assertIn('-O2', result.index_params) |
| self.assertIn('--target=arm-none-eabi', result.codegen_params) |
| self.assertIn('-march=armv7-a', result.codegen_params) |
| self.assertIn('-g', result.index_params) |
| self.assertIn('-gsplit-dwarf', result.index_params) |
| self.assertIn('-mllvm -generate-type-units', |
| ' '.join(result.index_params)) |
| self.assertIn('-flto=thin', result.index_params) |
| self.assertIn('-fwhole-program-vtables', result.index_params) |
| self.assertIn('-fsanitize=cfi', result.index_params) |
| |
| self.assertIn('-O2', result.codegen_params) |
| self.assertIn('--target=arm-none-eabi', result.codegen_params) |
| self.assertIn('-march=armv7-a', result.codegen_params) |
| self.assertIn('-gsplit-dwarf', result.codegen_params) |
| self.assertIn('-mllvm -generate-type-units', |
| ' '.join(result.codegen_params)) |
| self.assertNotIn('-flto=thin', result.codegen_params) |
| self.assertNotIn('-fwhole-program-vtables', result.codegen_params) |
| self.assertNotIn('-fsanitize=cfi', result.codegen_params) |
| |
| self.assertIn('-flto=thin', result.final_params) |
| |
| def test_codegen_params_default(self): |
| with FakeFs(bitcode_files=['foo.o'], other_files=['bar.o']): |
| result = remote_ld.RemoteLinkUnix().analyze_expanded_args( |
| ['clang', 'foo.o', 'bar.o', '-o', 'foo'], 'foo', 'clang', 'lto.foo', |
| 'common', False) |
| # Codegen optimization level should default to 2. |
| self.assertIn('-O2', result.codegen_params) |
| # -fdata-sections and -ffunction-sections default to on to match the |
| # behavior of local linking. |
| self.assertIn('-fdata-sections', result.codegen_params) |
| self.assertIn('-ffunction-sections', result.codegen_params) |
| |
| def test_codegen_params_default_cl(self): |
| with FakeFs(bitcode_files=['foo.obj'], other_files=['bar.obj']): |
| result = remote_link.RemoteLinkWindows().analyze_expanded_args( |
| ['clang-cl', 'foo.obj', 'bar.obj', '-Fefoo.exe'], 'foo.exe', |
| 'clang-cl', 'lto.foo', 'common', False) |
| # Codegen optimization level should default to 2. |
| self.assertIn('-O2', result.codegen_params) |
| # -Gw and -Gy default to on to match the behavior of local linking. |
| self.assertIn('-Gw', result.codegen_params) |
| self.assertIn('-Gy', result.codegen_params) |
| |
| def test_codegen_params_no_data_sections(self): |
| with FakeFs(bitcode_files=['foo.o'], other_files=['bar.o']): |
| result = remote_ld.RemoteLinkUnix().analyze_expanded_args( |
| ['clang', '-fno-data-sections', 'foo.o', 'bar.o', '-o', 'foo'], 'foo', |
| 'clang', 'lto.foo', 'common', False) |
| self.assertNotIn('-fdata-sections', result.codegen_params) |
| self.assertIn('-ffunction-sections', result.codegen_params) |
| |
| def test_codegen_params_no_function_sections(self): |
| with FakeFs(bitcode_files=['foo.o'], other_files=['bar.o']): |
| result = remote_ld.RemoteLinkUnix().analyze_expanded_args( |
| ['clang', '-fno-function-sections', 'foo.o', 'bar.o', '-o', 'foo'], |
| 'foo', 'clang', 'lto.foo', 'common', False) |
| self.assertIn('-fdata-sections', result.codegen_params) |
| self.assertNotIn('-ffunction-sections', result.codegen_params) |
| |
| def test_codegen_params_no_data_sections_cl(self): |
| with FakeFs(bitcode_files=['foo.obj'], other_files=['bar.obj']): |
| result = remote_link.RemoteLinkWindows().analyze_expanded_args( |
| ['clang-cl', '/Gw-', 'foo.obj', 'bar.obj', '/Fefoo.exe'], 'foo.exe', |
| 'clang-cl', 'lto.foo', 'common', False) |
| self.assertNotIn('-fdata-sections', result.codegen_params) |
| self.assertNotIn('-Gw', result.codegen_params) |
| self.assertNotIn('/Gw', result.codegen_params) |
| self.assertIn('-Gy', result.codegen_params) |
| |
| def test_codegen_params_no_function_sections_cl(self): |
| with FakeFs(bitcode_files=['foo.obj'], other_files=['bar.obj']): |
| result = remote_link.RemoteLinkWindows().analyze_expanded_args( |
| ['clang-cl', '/Gy-', 'foo.obj', 'bar.obj', '/Fefoo.exe'], 'foo.exe', |
| 'clang-cl', 'lto.foo', 'common', False) |
| self.assertIn('-Gw', result.codegen_params) |
| self.assertNotIn('-ffunction-sections', result.codegen_params) |
| self.assertNotIn('-Gy', result.codegen_params) |
| self.assertNotIn('/Gy', result.codegen_params) |
| |
| def test_codegen_params_explicit_data_and_function_sections(self): |
| with FakeFs(bitcode_files=['foo.o'], other_files=['bar.o']): |
| result = remote_ld.RemoteLinkUnix().analyze_expanded_args([ |
| 'clang', '-ffunction-sections', '-fdata-sections', 'foo.o', 'bar.o', |
| '-o', 'foo' |
| ], 'foo', 'clang', 'lto.foo', 'common', False) |
| self.assertIn('-fdata-sections', result.codegen_params) |
| self.assertIn('-ffunction-sections', result.codegen_params) |
| |
| def test_codegen_params_explicit_data_and_function_sections_cl(self): |
| with FakeFs(bitcode_files=['foo.obj'], other_files=['bar.obj']): |
| result = remote_link.RemoteLinkWindows().analyze_expanded_args( |
| ['clang-cl', '/Gy', '-Gw', 'foo.obj', 'bar.obj', '/Fefoo.exe'], |
| 'foo.exe', 'clang-cl', 'lto.foo', 'common', False) |
| self.assertIn('-Gw', result.codegen_params) |
| self.assertIn('/Gy', result.codegen_params) |
| self.assertNotIn('-fdata-sections', result.codegen_params) |
| self.assertNotIn('-ffunction-sections', result.codegen_params) |
| |
| def test_ensure_file_no_dir(self): |
| with named_directory() as d, working_directory(d): |
| self.assertFalse(os.path.exists('test')) |
| remote_link.ensure_file('test') |
| self.assertTrue(os.path.exists('test')) |
| |
| def test_ensure_file_existing(self): |
| with named_directory() as d, working_directory(d): |
| self.assertFalse(os.path.exists('foo/test')) |
| remote_link.ensure_file('foo/test') |
| self.assertTrue(os.path.exists('foo/test')) |
| os.utime('foo/test', (0, 0)) |
| statresult = os.stat('foo/test') |
| remote_link.ensure_file('foo/test') |
| self.assertTrue(os.path.exists('foo/test')) |
| newstatresult = os.stat('foo/test') |
| self.assertEqual(newstatresult.st_mtime, statresult.st_mtime) |
| |
| def test_ensure_file_error(self): |
| with named_directory() as d, working_directory(d): |
| self.assertFalse(os.path.exists('test')) |
| remote_link.ensure_file('test') |
| self.assertTrue(os.path.exists('test')) |
| self.assertRaises(OSError, remote_link.ensure_file, 'test/impossible') |
| |
| def test_transform_codegen_param_on_mllvm(self): |
| # Regression test for crbug.com/1135234 |
| link = remote_ld.RemoteLinkUnix() |
| self.assertEqual( |
| link.transform_codegen_param_common('-mllvm,-import-instr-limit=20'), |
| ['-mllvm', '-import-instr-limit=20']) |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |