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 15"""Unit tests for the directory_glob rule.""" 16 17load("@bazel_skylib//rules/directory:glob.bzl", "directory_glob") 18load("@bazel_skylib//rules/directory:providers.bzl", "DirectoryInfo") 19 20# buildifier: disable=bzl-visibility 21load("@bazel_skylib//rules/directory/private:glob.bzl", "directory_glob_chunk", "transitive_entries") 22load("@rules_testing//lib:analysis_test.bzl", "analysis_test", "test_suite") 23load("@rules_testing//lib:truth.bzl", "matching") 24load(":utils.bzl", "failure_matching", "failure_test") 25 26def _expect_glob_chunk(env, directory, chunk): 27 return env.expect.that_collection( 28 directory_glob_chunk(directory, chunk), 29 expr = "directory_glob_chunk({}, {})".format(directory.human_readable, repr(chunk)), 30 ) 31 32def _expect_glob(env, directory, include, allow_empty = False): 33 return env.expect.that_collection( 34 directory.glob(include, allow_empty = allow_empty), 35 expr = "directory_glob({}, {}, allow_empty={})".format(directory.human_readable, include, allow_empty), 36 ) 37 38def _with_children(children): 39 return DirectoryInfo( 40 entries = {k: k for k in children}, 41 human_readable = repr(children), 42 ) 43 44def _glob_test(name): 45 simple_name = "_simple_%s" % name 46 exclude_name = "_exclude_%s" % name 47 directory_glob( 48 name = simple_name, 49 srcs = ["testdata/f1"], 50 data = ["testdata/subdir/f2"], 51 directory = ":root", 52 ) 53 54 directory_glob( 55 name = exclude_name, 56 srcs = [ 57 "testdata/f1", 58 "nonexistent", 59 ], 60 allow_empty = True, 61 data = ["**"], 62 directory = ":root", 63 exclude = ["testdata/f1"], 64 ) 65 66 analysis_test( 67 name = name, 68 impl = _glob_test_impl, 69 targets = { 70 "root": ":root", 71 "f1": ":f1_filegroup", 72 "f2": ":f2_filegroup", 73 "simple_glob": simple_name, 74 "glob_with_exclude": exclude_name, 75 }, 76 ) 77 78def _glob_test_impl(env, targets): 79 f1 = targets.f1.files.to_list()[0] 80 f2 = targets.f2.files.to_list()[0] 81 root = targets.root[DirectoryInfo] 82 testdata = root.entries["testdata"] 83 subdir = testdata.entries["subdir"] 84 85 env.expect.that_collection(transitive_entries(root)).contains_exactly([ 86 root, 87 testdata, 88 subdir, 89 f1, 90 f2, 91 ]) 92 93 _expect_glob_chunk(env, testdata, "f1").contains_exactly([f1]) 94 _expect_glob_chunk(env, root, "nonexistent").contains_exactly([]) 95 _expect_glob_chunk(env, testdata, "f2").contains_exactly([]) 96 _expect_glob_chunk(env, root, "testdata").contains_exactly([testdata]) 97 _expect_glob_chunk(env, testdata, "*").contains_exactly( 98 [f1, subdir], 99 ) 100 _expect_glob_chunk( 101 env, 102 _with_children(["a", "d", "abc", "abbc", "ab.bc", ".abbc", "abbc."]), 103 "ab*bc", 104 ).contains_exactly([ 105 "abbc", 106 "ab.bc", 107 ]) 108 _expect_glob_chunk( 109 env, 110 _with_children(["abbc", "abbbc", "ab.b.bc"]), 111 "ab*b*bc", 112 ).contains_exactly([ 113 "abbbc", 114 "ab.b.bc", 115 ]) 116 _expect_glob_chunk( 117 env, 118 _with_children(["a", "ab", "ba"]), 119 "a*", 120 ).contains_exactly([ 121 "a", 122 "ab", 123 ]) 124 _expect_glob_chunk( 125 env, 126 _with_children(["a", "ab", "a.b.", "ba."]), 127 "a*b*", 128 ).contains_exactly([ 129 "ab", 130 "a.b.", 131 ]) 132 133 _expect_glob(env, root, ["testdata/f1"]).contains_exactly([f1]) 134 _expect_glob(env, root, ["testdata/subdir/f2"]).contains_exactly([f2]) 135 _expect_glob(env, root, ["**"]).contains_exactly([f1, f2]) 136 _expect_glob(env, root, ["**/f1"]).contains_exactly([f1]) 137 _expect_glob(env, root, ["**/**/f1"]).contains_exactly([f1]) 138 _expect_glob(env, root, ["testdata/*/f1"], allow_empty = True).contains_exactly([]) 139 140 simple_glob = env.expect.that_target(targets.simple_glob) 141 with_exclude = env.expect.that_target(targets.glob_with_exclude) 142 143 env.expect.that_collection( 144 simple_glob.actual[DefaultInfo].files.to_list(), 145 expr = "simple_glob's files", 146 ).contains_exactly([f1]) 147 env.expect.that_collection( 148 with_exclude.actual[DefaultInfo].files.to_list(), 149 expr = "with_exclude's files", 150 ).contains_exactly([]) 151 152 # target.runfiles().contains_exactly() doesn't do what we want - it converts 153 # it to a string corresponding to the runfiles import path. 154 env.expect.that_collection( 155 simple_glob.runfiles().actual.files.to_list(), 156 expr = "simple_glob's runfiles", 157 ).contains_exactly([f1, f2]) 158 env.expect.that_collection( 159 with_exclude.runfiles().actual.files.to_list(), 160 expr = "with_exclude's runfiles", 161 ).contains_exactly([f2]) 162 163def _glob_with_no_match_test(name): 164 failure_test( 165 name = name, 166 impl = failure_matching(matching.contains('"nonexistent" failed to match any files in')), 167 rule = directory_glob, 168 srcs = [ 169 "testdata/f1", 170 "nonexistent", 171 ], 172 data = ["testdata/f1"], 173 directory = ":root", 174 ) 175 176# buildifier: disable=function-docstring 177def glob_test_suite(name): 178 test_suite( 179 name = name, 180 tests = [ 181 _glob_test, 182 _glob_with_no_match_test, 183 ], 184 ) 185