1# Copyright 2022 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.project_builder_prefs""" 15 16import argparse 17import copy 18from pathlib import Path 19import shutil 20import tempfile 21from typing import Any 22import unittest 23 24from parameterized import parameterized # type: ignore 25 26from pw_build.project_builder_argparse import add_project_builder_arguments 27from pw_build.project_builder_prefs import ( 28 ProjectBuilderPrefs, 29 default_config, 30 load_defaults_from_argparse, 31) 32 33_BAZEL_COMMAND = 'bazel' 34# Prefer bazelisk if available. 35if shutil.which('bazelisk'): 36 _BAZEL_COMMAND = 'bazelisk' 37 38 39def _create_tempfile(content: str) -> Path: 40 with tempfile.NamedTemporaryFile( 41 prefix=f'{__package__}', delete=False 42 ) as output_file: 43 output_file.write(content.encode('UTF-8')) 44 return Path(output_file.name) 45 46 47class TestProjectBuilderPrefs(unittest.TestCase): 48 """Tests for ProjectBuilderPrefs.""" 49 50 maxDiff = None 51 52 def test_load_no_existing_files(self) -> None: 53 # Create a prefs instance with no loaded config. 54 prefs = ProjectBuilderPrefs( 55 load_argparse_arguments=add_project_builder_arguments, 56 project_file=False, 57 project_user_file=False, 58 user_file=False, 59 ) 60 # Construct an expected result config. 61 expected_config: dict[Any, Any] = {} 62 # Apply default build_system_commands and build_directories. 63 expected_config.update(default_config()) 64 expected_config.update( 65 load_defaults_from_argparse(add_project_builder_arguments) 66 ) 67 68 self.assertEqual( 69 prefs._config, expected_config # pylint: disable=protected-access 70 ) 71 72 @parameterized.expand( 73 [ 74 ( 75 'Manual --build-system-commands on the command line', 76 # Argparse input 77 [ 78 '--build-system-command', 79 'out', 80 'bazel build', 81 '--build-system-command', 82 'out', 83 'bazel test', 84 ], 85 # Expected changed config 86 { 87 'default_build_targets': [], 88 'build_directories': [], 89 'build_system_commands': { 90 'out': { 91 'commands': [ 92 { 93 'command': 'bazel', 94 'extra_args': ['build'], 95 }, 96 { 97 'command': 'bazel', 98 'extra_args': ['test'], 99 }, 100 ], 101 }, 102 }, 103 }, 104 ), 105 ( 106 'Empty build directory (no -C) with targets. ' 107 'Ninja manually specified.', 108 # Argparse input 109 '--default-build-system ninja docs python.lint'.split(), 110 # Expected changed config 111 { 112 'default_build_system': 'ninja', 113 'default_build_targets': ['docs', 'python.lint'], 114 'build_directories': [], 115 'build_system_commands': { 116 'default': { 117 'commands': [{'command': 'ninja', 'extra_args': []}] 118 } 119 }, 120 }, 121 ), 122 ( 123 'Empty build directory (no -C) with targets. ' 124 'Ninja not specified.', 125 # Argparse input 126 'docs python.lint'.split(), 127 # Expected changed config 128 { 129 'default_build_targets': ['docs', 'python.lint'], 130 'build_directories': [], 131 'build_system_commands': { 132 'default': { 133 'commands': [{'command': 'ninja', 'extra_args': []}] 134 } 135 }, 136 }, 137 ), 138 ( 139 'Empty build directory (no -C) with targets (bazel).', 140 # Argparse input 141 ( 142 '--default-build-system bazel //pw_watch/... //pw_build/...' 143 ).split(), 144 # Expected changed config 145 { 146 'default_build_system': 'bazel', 147 'default_build_targets': [ 148 '//pw_watch/...', 149 '//pw_build/...', 150 ], 151 'build_directories': [], 152 'build_system_commands': { 153 'default': { 154 'commands': [ 155 { 156 'command': _BAZEL_COMMAND, 157 'extra_args': ['build'], 158 }, 159 { 160 'command': _BAZEL_COMMAND, 161 'extra_args': ['test'], 162 }, 163 ] 164 } 165 }, 166 }, 167 ), 168 ( 169 'Targets with no build directory and an additional build ' 170 'directory with targets.', 171 # Argparse input 172 'docs python.lint -C out2 python.tests'.split(), 173 # Expected changed config 174 { 175 'default_build_targets': ['docs', 'python.lint'], 176 'build_directories': [['out2', 'python.tests']], 177 'build_system_commands': { 178 'default': { 179 'commands': [{'command': 'ninja', 'extra_args': []}] 180 } 181 }, 182 }, 183 ), 184 ( 185 '', 186 # Argparse input 187 'docs python.lint -C out2 python.tests'.split(), 188 # Expected changed config 189 { 190 'default_build_targets': ['docs', 'python.lint'], 191 'build_directories': [['out2', 'python.tests']], 192 'build_system_commands': { 193 'default': { 194 'commands': [{'command': 'ninja', 'extra_args': []}] 195 } 196 }, 197 }, 198 ), 199 ( 200 'Two build directories; one with build system commands the ' 201 'other with none defined.', 202 # Argparse input 203 [ 204 '-C', 205 'out/gn', 206 'python.lint', 207 '-C', 208 'out/bazel', 209 '//...', 210 '--build-system-command', 211 'out/bazel', 212 'bazel build', 213 '--build-system-command', 214 'out/bazel', 215 'bazel test', 216 '--logfile', 217 'out/build.txt', 218 ], 219 # Expected changed config 220 { 221 'default_build_targets': [], 222 'build_directories': [ 223 ['out/gn', 'python.lint'], 224 ['out/bazel', '//...'], 225 ], 226 'build_system_commands': { 227 'default': { 228 'commands': [{'command': 'ninja', 'extra_args': []}] 229 }, 230 'out/bazel': { 231 'commands': [ 232 { 233 'command': 'bazel', 234 'extra_args': ['build'], 235 }, 236 { 237 'command': 'bazel', 238 'extra_args': ['test'], 239 }, 240 ], 241 }, 242 }, 243 'logfile': Path('out/build.txt'), 244 }, 245 ), 246 ] 247 ) 248 def test_apply_command_line_args( 249 self, 250 _name, 251 argparse_args, 252 expected_config_changes, 253 ): 254 """Check command line args are applied to ProjectBuilderPrefs.""" 255 # Load default command line arg values. 256 parser = argparse.ArgumentParser( 257 formatter_class=argparse.RawDescriptionHelpFormatter, 258 ) 259 parser = add_project_builder_arguments(parser) 260 argparse_output = parser.parse_args(argparse_args) 261 262 # Create a prefs instance with the test config file. 263 prefs = ProjectBuilderPrefs( 264 load_argparse_arguments=add_project_builder_arguments, 265 project_file=False, 266 project_user_file=False, 267 user_file=False, 268 ) 269 270 # Save config before apply_command_line_args 271 # pylint: disable=protected-access 272 expected_config = copy.deepcopy(prefs._config) 273 274 # Run apply_command_line_args 275 prefs.apply_command_line_args(argparse_output) 276 277 # Add the expected changes to the base 278 expected_config.update(expected_config_changes) 279 280 # Check equality 281 self.assertEqual(prefs._config, expected_config) 282 # pylint: enable=protected-access 283 284 285if __name__ == '__main__': 286 unittest.main() 287