1# Copyright 2023 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 common to py_test, py_binary, and py_library rules.""" 15 16load("@rules_testing//lib:analysis_test.bzl", "analysis_test") 17load("@rules_testing//lib:truth.bzl", "matching") 18load("@rules_testing//lib:util.bzl", "PREVENT_IMPLICIT_BUILDING_TAGS", rt_util = "util") 19load("//python:defs.bzl", "PyInfo") 20load("//python/private:reexports.bzl", "BuiltinPyInfo") # buildifier: disable=bzl-visibility 21load("//tests/base_rules:util.bzl", pt_util = "util") 22load("//tests/support:py_info_subject.bzl", "py_info_subject") 23 24_tests = [] 25 26_PRODUCES_PY_INFO_ATTRS = { 27 "imports": attr.string_list(), 28 "srcs": attr.label_list(allow_files = True), 29} 30 31def _create_py_info(ctx, provider_type): 32 return [provider_type( 33 transitive_sources = depset(ctx.files.srcs), 34 imports = depset(ctx.attr.imports), 35 )] 36 37def _produces_builtin_py_info_impl(ctx): 38 return _create_py_info(ctx, BuiltinPyInfo) 39 40_produces_builtin_py_info = rule( 41 implementation = _produces_builtin_py_info_impl, 42 attrs = _PRODUCES_PY_INFO_ATTRS, 43) 44 45def _produces_py_info_impl(ctx): 46 return _create_py_info(ctx, BuiltinPyInfo) 47 48_produces_py_info = rule( 49 implementation = _produces_py_info_impl, 50 attrs = _PRODUCES_PY_INFO_ATTRS, 51) 52 53def _not_produces_py_info_impl(ctx): 54 _ = ctx # @unused 55 return [DefaultInfo()] 56 57_not_produces_py_info = rule( 58 implementation = _not_produces_py_info_impl, 59) 60 61def _py_info_propagation_setup(name, config, produce_py_info_rule, test_impl): 62 rt_util.helper_target( 63 config.base_test_rule, 64 name = name + "_subject", 65 deps = [name + "_produces_builtin_py_info"], 66 ) 67 rt_util.helper_target( 68 produce_py_info_rule, 69 name = name + "_produces_builtin_py_info", 70 srcs = [rt_util.empty_file(name + "_produce.py")], 71 imports = ["custom-import"], 72 ) 73 analysis_test( 74 name = name, 75 target = name + "_subject", 76 impl = test_impl, 77 ) 78 79def _py_info_propagation_test_impl(env, target, provider_type): 80 info = env.expect.that_target(target).provider( 81 provider_type, 82 factory = py_info_subject, 83 ) 84 85 info.transitive_sources().contains("{package}/{test_name}_produce.py") 86 info.imports().contains("custom-import") 87 88def _test_py_info_propagation_builtin(name, config): 89 _py_info_propagation_setup( 90 name, 91 config, 92 _produces_builtin_py_info, 93 _test_py_info_propagation_builtin_impl, 94 ) 95 96def _test_py_info_propagation_builtin_impl(env, target): 97 _py_info_propagation_test_impl(env, target, BuiltinPyInfo) 98 99_tests.append(_test_py_info_propagation_builtin) 100 101def _test_py_info_propagation(name, config): 102 _py_info_propagation_setup( 103 name, 104 config, 105 _produces_py_info, 106 _test_py_info_propagation_impl, 107 ) 108 109def _test_py_info_propagation_impl(env, target): 110 _py_info_propagation_test_impl(env, target, PyInfo) 111 112_tests.append(_test_py_info_propagation) 113 114def _test_requires_provider(name, config): 115 rt_util.helper_target( 116 config.base_test_rule, 117 name = name + "_subject", 118 deps = [name + "_nopyinfo"], 119 ) 120 rt_util.helper_target( 121 _not_produces_py_info, 122 name = name + "_nopyinfo", 123 ) 124 analysis_test( 125 name = name, 126 target = name + "_subject", 127 impl = _test_requires_provider_impl, 128 expect_failure = True, 129 ) 130 131def _test_requires_provider_impl(env, target): 132 env.expect.that_target(target).failures().contains_predicate( 133 matching.str_matches("mandatory*PyInfo"), 134 ) 135 136_tests.append(_test_requires_provider) 137 138def _test_data_sets_uses_shared_library(name, config): 139 rt_util.helper_target( 140 config.base_test_rule, 141 name = name + "_subject", 142 data = [rt_util.empty_file(name + "_dso.so")], 143 ) 144 analysis_test( 145 name = name, 146 target = name + "_subject", 147 impl = _test_data_sets_uses_shared_library_impl, 148 ) 149 150def _test_data_sets_uses_shared_library_impl(env, target): 151 env.expect.that_target(target).provider( 152 PyInfo, 153 factory = py_info_subject, 154 ).uses_shared_libraries().equals(True) 155 156_tests.append(_test_data_sets_uses_shared_library) 157 158def _test_tags_can_be_tuple(name, config): 159 # We don't use a helper because we want to ensure that value passed is 160 # a tuple. 161 config.base_test_rule( 162 name = name + "_subject", 163 tags = ("one", "two") + tuple(PREVENT_IMPLICIT_BUILDING_TAGS), 164 ) 165 analysis_test( 166 name = name, 167 target = name + "_subject", 168 impl = _test_tags_can_be_tuple_impl, 169 ) 170 171def _test_tags_can_be_tuple_impl(env, target): 172 env.expect.that_target(target).tags().contains_at_least([ 173 "one", 174 "two", 175 ]) 176 177_tests.append(_test_tags_can_be_tuple) 178 179def create_base_tests(config): 180 return pt_util.create_tests(_tests, config = config) 181