1# Copyright 2024 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"""Utility functions for testing toolchain rules.""" 15 16load( 17 "//cc_toolchain/private:providers.bzl", 18 "PwActionConfigSetInfo", 19 "PwExtraActionFilesSetInfo", 20 "PwFeatureConstraintInfo", 21 "PwFeatureInfo", 22 "PwFeatureSetInfo", 23 "PwFlagGroupInfo", 24 "PwFlagSetInfo", 25) 26load( 27 "//cc_toolchain/private:utils.bzl", 28 _to_untyped_config = "to_untyped_config", 29) 30load("//features:builtin_features.bzl", "BUILTIN_FEATURES") 31 32visibility("//cc_toolchain/tests/...") 33 34# Intentionally add two spaces after got, to ensure the lines are correctly 35# aligned. 36def assert_eq(got, want, msg = "\n\nGot %r\n\nWant %r"): 37 if got != want: 38 fail(msg % (got, want)) 39 40def assert_in(want, container, msg = "\n\nUnable to find %r\n\nIn %r"): 41 if want not in container: 42 fail(msg % (want, container)) 43 44def assert_ne(got, bad, msg = "Got %r, wanted any value other than that"): 45 if got == bad: 46 fail(msg % got) 47 48def assert_not_none(got, msg = "Got None, wanted a non-None value"): 49 if got == None: 50 fail(msg) 51 52def assert_fail(fn, *args, want = None, **kwargs): 53 """Asserts that a function threw an exception 54 55 Args: 56 fn: A function that can take in a parameter "fail" 57 *args: Arguments to be passed to fn 58 **kwargs: Arguments to be papssed to fn 59 want: Option[str]: The expected error message. 60 If not provided, any error message is allowed. 61 """ 62 63 # Use a mutable type so that the inner function can modify the outer scope. 64 fails = [] 65 66 def set_fail_msg(msg): 67 fails.append(msg) 68 69 fn(fail = set_fail_msg, *args, **kwargs) 70 if want == None: 71 assert_ne(fails, [], msg = "Expected %r(**%r) to fail. Unexpectedly passed and got %%r" % (fn, kwargs)) 72 else: 73 assert_ne(fails, [], msg = "Expected %r(**%r) to fail with msg %r. Unexpectedly passed and got %%r" % (fn, want, kwargs)) 74 assert_eq(fails[0], want, msg = "\n\nGot failure message %r\n\nWant failure message %r") 75 76def assert_labels_eq(got, want): 77 """Asserts that two lists of providers with the label attribute are equal 78 79 Args: 80 got: A list or depset of providers containing the attribute "label" 81 want: A list or depset of providers containing the attribute "label" 82 """ 83 if type(got) == "depset": 84 got = got.to_list() 85 if type(want) == "depset": 86 want = want.to_list() 87 got = sorted([entry.label for entry in got]) 88 want = sorted([entry.label for entry in want]) 89 assert_eq(got, want) 90 91_RULES = { 92 PwFlagSetInfo: "flag_sets", 93 PwFlagGroupInfo: "flag_groups", 94 PwFeatureConstraintInfo: "feature_constraints", 95 PwFeatureInfo: "features", 96 PwFeatureSetInfo: "feature_sets", 97 PwActionConfigSetInfo: "action_configs", 98 PwExtraActionFilesSetInfo: "extra_action_files", 99} 100 101_PROVIDERS = { 102 "//cc_toolchain/tests/action_configs:all_c_compile": [PwActionConfigSetInfo], 103 "//cc_toolchain/tests/action_configs:assemble_from_bin": [PwActionConfigSetInfo], 104 "//cc_toolchain/tests/action_configs:c_compile": [PwActionConfigSetInfo], 105 "//cc_toolchain/tests/action_configs:c_compiler_data": [PwExtraActionFilesSetInfo], 106 "//cc_toolchain/tests/action_configs:cpp_compile_from_tool": [PwActionConfigSetInfo], 107 "//cc_toolchain/tests/action_configs:cpp_compiler_data": [PwExtraActionFilesSetInfo], 108 "//cc_toolchain/tests/action_configs:data": [PwExtraActionFilesSetInfo], 109 "//cc_toolchain/tests/action_configs:requires_foo": [PwActionConfigSetInfo], 110 "//cc_toolchain/tests/features:bar": [PwFeatureInfo, PwFeatureSetInfo], 111 "//cc_toolchain/tests/features:baz": [PwFeatureInfo, PwFeatureSetInfo], 112 "//cc_toolchain/tests/features:conflict": [PwFeatureInfo, PwFeatureSetInfo], 113 "//cc_toolchain/tests/features:constrained": [PwFeatureInfo, PwFeatureSetInfo], 114 "//cc_toolchain/tests/features:foo": [PwFeatureInfo, PwFeatureSetInfo], 115 "//cc_toolchain/tests/features:foo_not_baz": [PwFeatureConstraintInfo], 116 "//cc_toolchain/tests/features:foo_only": [PwFeatureConstraintInfo], 117 "//cc_toolchain/tests/features:foobar": [PwFeatureSetInfo], 118 "//cc_toolchain/tests/features:implies": [PwFeatureInfo, PwFeatureSetInfo], 119 "//cc_toolchain/tests/features:implies_supports_pic": [PwFeatureInfo, PwFeatureSetInfo], 120 "//cc_toolchain/tests/features:mutex_label": [PwFeatureInfo], 121 "//cc_toolchain/tests/features:mutex_provider": [PwFeatureInfo], 122 "//cc_toolchain/tests/features:primary_feature": [PwFeatureInfo], 123 "//cc_toolchain/tests/features:requires": [PwFeatureInfo, PwFeatureSetInfo], 124 "//cc_toolchain/tests/features:supports_pic": [PwFeatureInfo, PwFeatureSetInfo], 125 "//cc_toolchain/tests/features:supports_pic_no_override": [PwFeatureInfo, PwFeatureSetInfo], 126 "//cc_toolchain/tests/flag_sets:bar": [PwFlagSetInfo], 127 "//cc_toolchain/tests/flag_sets:baz": [PwFlagSetInfo], 128 "//cc_toolchain/tests/flag_sets:env": [PwFlagSetInfo], 129 "//cc_toolchain/tests/flag_sets:flag_group": [PwFlagGroupInfo], 130 "//cc_toolchain/tests/flag_sets:foo": [PwFlagSetInfo], 131 "//cc_toolchain/tests/flag_sets:multiple_actions": [PwFlagSetInfo], 132 "//cc_toolchain/tests/flag_sets:wraps_flag_group": [PwFlagSetInfo], 133} 134 135# This gives all tests access to all providers. This allows us to write 136# non-brittle tests, since now we can write something like: 137# assert_eq(features.foo.flag_sets, [flag_sets.foo]) 138# Note that this namespaces by provider instead of by label. We do so because 139# this allows you to access both features.foo and feature_sets.foo (since a 140# feature defines both a feature and a feature set). 141def generate_test_rule(implementation): 142 def wrapper(ctx): 143 providers = {v: {} for v in _RULES.values()} 144 for target in ctx.attr.test_cases: 145 pkg = target.label.package 146 name = target.label.name 147 for provider in _PROVIDERS["//%s:%s" % (pkg, name)]: 148 providers[_RULES[provider]][name] = target[provider] 149 150 def to_untyped_config(features = [], feature_sets = [], action_configs = [], flag_sets = [], extra_action_files = [], fail = fail): 151 feature_set = PwFeatureSetInfo(features = depset( 152 features + [ft[PwFeatureInfo] for ft in ctx.attr.builtin_features], 153 transitive = [fs.features for fs in feature_sets], 154 )) 155 action_config_set = PwActionConfigSetInfo( 156 label = ctx.label, 157 action_configs = depset(transitive = [ 158 acs.action_configs 159 for acs in action_configs 160 ]), 161 ) 162 extra_action_files_combined = PwExtraActionFilesSetInfo(srcs = depset(transitive = [ 163 ffa.srcs 164 for ffa in extra_action_files 165 ])) 166 return _to_untyped_config(feature_set, action_config_set, flag_sets, extra_action_files_combined, fail = fail) 167 168 kwargs = {k: struct(**v) for k, v in providers.items()} 169 return implementation(ctx, to_untyped_config = to_untyped_config, **kwargs) 170 171 return rule( 172 implementation = wrapper, 173 attrs = { 174 "builtin_features": attr.label_list(default = BUILTIN_FEATURES), 175 "test_cases": attr.label_list(default = _PROVIDERS.keys()), 176 }, 177 ) 178