1# Copyright 2024 The Bazel Authors. All rights reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://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, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14"""Tests for the cc_args rule.""" 15 16load("//cc:cc_toolchain_config_lib.bzl", "flag_group", "variable_with_value") 17load("//cc/toolchains:cc_toolchain_info.bzl", "VariableInfo") 18load("//cc/toolchains:format.bzl", "format_arg") 19load( 20 "//cc/toolchains/impl:nested_args.bzl", 21 "FORMAT_ARGS_ERR", 22 "REQUIRES_EQUAL_ERR", 23 "REQUIRES_MUTUALLY_EXCLUSIVE_ERR", 24 "REQUIRES_NONE_ERR", 25 "format_string_indexes", 26 "format_variable", 27 "nested_args_provider", 28 "raw_string", 29) 30load("//tests/rule_based_toolchain:subjects.bzl", "result_fn_wrapper", "subjects") 31 32visibility("private") 33 34def _expect_that_nested(env, expr = None, **kwargs): 35 return env.expect.that_value( 36 expr = expr, 37 value = result_fn_wrapper(nested_args_provider)( 38 label = Label("//:args"), 39 **kwargs 40 ), 41 factory = subjects.result(subjects.NestedArgsInfo), 42 ) 43 44def _expect_that_formatted(env, var, iterate_over = None, expr = None): 45 return env.expect.that_value( 46 result_fn_wrapper(format_variable)(var, iterate_over), 47 factory = subjects.result(subjects.str), 48 expr = expr or "format_variable(var=%r, iterate_over=%r" % (var, iterate_over), 49 ) 50 51def _expect_that_format_string_indexes(env, var, expr = None): 52 return env.expect.that_value( 53 result_fn_wrapper(format_string_indexes)(var), 54 factory = subjects.result(subjects.collection), 55 expr = expr or "format_string_indexes(%r)" % var, 56 ) 57 58def _format_string_indexes_test(env, _): 59 _expect_that_format_string_indexes(env, "foo").ok().contains_exactly([]) 60 _expect_that_format_string_indexes(env, "%%").ok().contains_exactly([]) 61 _expect_that_format_string_indexes(env, "%").err().equals( 62 '% should always either of the form %s, or escaped with %%. Instead, got "%"', 63 ) 64 _expect_that_format_string_indexes(env, "%a").err().equals( 65 '% should always either of the form %s, or escaped with %%. Instead, got "%a"', 66 ) 67 _expect_that_format_string_indexes(env, "%s").ok().contains_exactly([0]) 68 _expect_that_format_string_indexes(env, "%%%s%s").ok().contains_exactly([2, 4]) 69 _expect_that_format_string_indexes(env, "%%{").ok().contains_exactly([]) 70 _expect_that_format_string_indexes(env, "%%s").ok().contains_exactly([]) 71 _expect_that_format_string_indexes(env, "%{foo}").err().equals( 72 'Using the old mechanism for variables, %{variable}, but we instead use format_arg("--foo=%s", "//cc/toolchains/variables:<variable>"). Got "%{foo}"', 73 ) 74 75def _formats_raw_strings_test(env, _): 76 _expect_that_formatted( 77 env, 78 raw_string("foo"), 79 ).ok().equals("foo") 80 _expect_that_formatted( 81 env, 82 raw_string("%s"), 83 ).err().contains("Can't use %s with a raw string. Either escape it with %%s or use format_arg") 84 85def _formats_variables_test(env, targets): 86 _expect_that_formatted( 87 env, 88 format_arg("ab %s cd", targets.foo[VariableInfo]), 89 ).ok().equals("ab %{foo} cd") 90 91 _expect_that_formatted( 92 env, 93 format_arg("foo", targets.foo[VariableInfo]), 94 ).err().equals('format_arg requires a "%s" in the format string, but got "foo"') 95 _expect_that_formatted( 96 env, 97 format_arg("%s%s", targets.foo[VariableInfo]), 98 ).err().equals('Only one %s can be used in a format string, but got "%s%s"') 99 100 _expect_that_formatted( 101 env, 102 format_arg("%s"), 103 iterate_over = "foo", 104 ).ok().equals("%{foo}") 105 _expect_that_formatted( 106 env, 107 format_arg("%s"), 108 ).err().contains("format_arg requires either a variable to format, or iterate_over must be provided") 109 110def _iterate_over_test(env, _): 111 inner = _expect_that_nested( 112 env, 113 args = [raw_string("--foo")], 114 ).ok().actual 115 env.expect.that_str(inner.legacy_flag_group).equals(flag_group(flags = ["--foo"])) 116 117 nested = _expect_that_nested( 118 env, 119 nested = [inner], 120 iterate_over = "my_list", 121 ).ok() 122 nested.iterate_over().some().equals("my_list") 123 nested.legacy_flag_group().equals(flag_group( 124 iterate_over = "my_list", 125 flag_groups = [inner.legacy_flag_group], 126 )) 127 nested.requires_types().contains_exactly({}) 128 129def _requires_types_test(env, targets): 130 _expect_that_nested( 131 env, 132 requires_not_none = "abc", 133 requires_none = "def", 134 args = [raw_string("--foo")], 135 expr = "mutually_exclusive", 136 ).err().equals(REQUIRES_MUTUALLY_EXCLUSIVE_ERR) 137 138 _expect_that_nested( 139 env, 140 requires_none = "var", 141 args = [raw_string("--foo")], 142 expr = "requires_none", 143 ).ok().requires_types().contains_exactly( 144 {"var": [struct( 145 msg = REQUIRES_NONE_ERR, 146 valid_types = ["option"], 147 after_option_unwrap = False, 148 )]}, 149 ) 150 151 _expect_that_nested( 152 env, 153 args = [raw_string("foo %s baz")], 154 expr = "no_variable", 155 ).err().contains("Can't use %s with a raw string") 156 157 _expect_that_nested( 158 env, 159 args = [format_arg("foo %s baz", targets.foo[VariableInfo])], 160 expr = "type_validation", 161 ).ok().requires_types().contains_exactly( 162 {"foo": [struct( 163 msg = FORMAT_ARGS_ERR, 164 valid_types = ["string", "file", "directory"], 165 after_option_unwrap = True, 166 )]}, 167 ) 168 169 nested = _expect_that_nested( 170 env, 171 requires_equal = "foo", 172 requires_equal_value = "value", 173 args = [format_arg("--foo=%s", targets.foo[VariableInfo])], 174 expr = "type_and_requires_equal_validation", 175 ).ok() 176 nested.requires_types().contains_exactly( 177 {"foo": [ 178 struct( 179 msg = REQUIRES_EQUAL_ERR, 180 valid_types = ["string"], 181 after_option_unwrap = True, 182 ), 183 struct( 184 msg = FORMAT_ARGS_ERR, 185 valid_types = ["string", "file", "directory"], 186 after_option_unwrap = True, 187 ), 188 ]}, 189 ) 190 nested.legacy_flag_group().equals(flag_group( 191 expand_if_equal = variable_with_value(name = "foo", value = "value"), 192 flags = ["--foo=%{foo}"], 193 )) 194 195TARGETS = [ 196 ":foo", 197] 198 199TESTS = { 200 "format_string_indexes_test": _format_string_indexes_test, 201 "formats_raw_strings_test": _formats_raw_strings_test, 202 "formats_variables_test": _formats_variables_test, 203 "iterate_over_test": _iterate_over_test, 204 "requires_types_test": _requires_types_test, 205} 206