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"""Test helpers for glob_dirs().""" 15 16load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") 17load("//pw_build:compatibility.bzl", "incompatible_with_mcu") 18 19def return_error(err): 20 """Testing helper to return an error string rather than fail(). 21 22 To use this, the starlark function your testing must support injection 23 of an alternative `fail()` handler. 24 """ 25 return err 26 27# This provider allows the test logic implementation to see the information 28# captured by the build rule. 29_TestExpectationInfo = provider( 30 "A pair of expected and actual values for testing", 31 fields = ["actual", "expected"], 32) 33 34def _comparison_case_rule_impl(ctx): 35 return [ 36 _TestExpectationInfo( 37 expected = ctx.attr.expected, 38 actual = ctx.attr.actual, 39 ), 40 ] 41 42# `rule()` calls have to be at the top of the file, so we can't just make this 43# a lambda of some kind. The best we can do is make it super easy to stamp out 44# more. 45def build_comparison_case_rule(attr_type): 46 return { 47 "attrs": { 48 "actual": attr_type, 49 "expected": attr_type, 50 }, 51 "implementation": _comparison_case_rule_impl, 52 "provides": [_TestExpectationInfo], 53 } 54 55string_comparison = rule( 56 **build_comparison_case_rule(attr.string()) 57) 58 59string_list_comparison = rule( 60 **build_comparison_case_rule(attr.string_list()) 61) 62 63# Collect into a single struct to make it easier to load in a BUILD file. 64PW_LOAD_PHASE_TEST_TYPES = struct( 65 STRING = string_comparison, 66 STRING_LIST = string_list_comparison, 67) 68 69# Implement the actual test logic. In this case, it's pretty trivial: just 70# assert that the `expected` and `actual` attributes of the rule match. 71def _load_phase_test_impl(ctx): 72 env = analysistest.begin(ctx) 73 target_under_test = analysistest.target_under_test(env) 74 asserts.equals( 75 env, 76 target_under_test[_TestExpectationInfo].expected, 77 target_under_test[_TestExpectationInfo].actual, 78 ) 79 80 return analysistest.end(env) 81 82_load_phase_test = analysistest.make(_load_phase_test_impl) 83 84# This is the macro for decaring an individual test case. 85def pw_load_phase_test(comparison_type): 86 def comparison_test(name, expected, actual, tags = [], **rule_kwargs): 87 comparison_type( 88 name = name + ".case", 89 expected = expected, 90 actual = actual, 91 tags = tags + ["manual"], 92 target_compatible_with = incompatible_with_mcu(), 93 testonly = True, 94 **rule_kwargs 95 ) 96 97 _load_phase_test( 98 name = name, 99 target_under_test = name + ".case", 100 tags = tags, 101 target_compatible_with = incompatible_with_mcu(), 102 **rule_kwargs 103 ) 104 105 return comparison_test 106 107# Actual test rule types. 108pw_string_comparison_test = pw_load_phase_test(PW_LOAD_PHASE_TEST_TYPES.STRING) 109pw_string_list_comparison_test = pw_load_phase_test(PW_LOAD_PHASE_TEST_TYPES.STRING_LIST) 110