blob: d55101cec0687e46a763a68736825d3480cfc1a1 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00002//
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
15package com.google.devtools.build.lib.skylark;
16
17import static com.google.common.truth.Truth.assertThat;
michajlo660d17f2020-03-27 09:01:57 -070018import static org.junit.Assert.assertThrows;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000019
20import com.google.common.base.Joiner;
michajlo0a89cef2020-04-06 12:04:12 -070021import com.google.common.base.Strings;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000022import com.google.common.collect.ImmutableList;
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +000023import com.google.common.collect.ImmutableMap;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000024import com.google.common.collect.Iterables;
gregce7fa23ea2018-01-18 12:46:04 -080025import com.google.devtools.build.lib.analysis.config.transitions.NoTransition;
ulfjack35625252017-08-08 19:45:46 +020026import com.google.devtools.build.lib.analysis.skylark.SkylarkAttr;
27import com.google.devtools.build.lib.analysis.skylark.SkylarkAttr.Descriptor;
tomlu72642a22017-10-18 06:23:14 +020028import com.google.devtools.build.lib.analysis.skylark.SkylarkRuleClassFunctions.SkylarkRuleFunction;
ulfjack35625252017-08-08 19:45:46 +020029import com.google.devtools.build.lib.analysis.skylark.SkylarkRuleContext;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000030import com.google.devtools.build.lib.cmdline.Label;
michajlob839a512020-03-11 10:04:23 -070031import com.google.devtools.build.lib.events.Event;
32import com.google.devtools.build.lib.events.EventKind;
Dmitry Lomovf868b3e2017-01-17 10:25:28 +000033import com.google.devtools.build.lib.packages.AdvertisedProviderSet;
Dmitry Lomov0692c7f2016-09-30 16:43:30 +000034import com.google.devtools.build.lib.packages.AspectParameters;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000035import com.google.devtools.build.lib.packages.Attribute;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000036import com.google.devtools.build.lib.packages.BuildType;
juliexxia44e21432020-03-31 08:21:20 -070037import com.google.devtools.build.lib.packages.ExecGroup;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000038import com.google.devtools.build.lib.packages.ImplicitOutputsFunction;
39import com.google.devtools.build.lib.packages.PredicateWithMessage;
Dmitry Lomovf868b3e2017-01-17 10:25:28 +000040import com.google.devtools.build.lib.packages.RequiredProviders;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000041import com.google.devtools.build.lib.packages.RuleClass;
42import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
Dmitry Lomov950310f2017-03-01 17:45:12 +000043import com.google.devtools.build.lib.packages.SkylarkAspectClass;
cparsons0d55f4c2017-12-20 14:49:13 -080044import com.google.devtools.build.lib.packages.SkylarkDefinedAspect;
dslomov0667b832017-08-25 09:29:50 +020045import com.google.devtools.build.lib.packages.SkylarkInfo;
gregceb6eafee2020-04-20 08:04:51 -070046import com.google.devtools.build.lib.packages.StarlarkProvider;
gregce74d84d42020-04-17 10:02:03 -070047import com.google.devtools.build.lib.packages.StarlarkProviderIdentifier;
cparsons4ebf6c02018-08-17 14:49:36 -070048import com.google.devtools.build.lib.packages.StructImpl;
cparsons0c5c1c62018-05-24 10:37:03 -070049import com.google.devtools.build.lib.packages.StructProvider;
Googlerc5fcc862019-09-06 16:17:47 -070050import com.google.devtools.build.lib.packages.Type;
gregce1cd84ec2020-04-09 15:45:19 -070051import com.google.devtools.build.lib.skyframe.StarlarkImportLookupFunction;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000052import com.google.devtools.build.lib.skylark.util.SkylarkTestCase;
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +000053import com.google.devtools.build.lib.syntax.ClassObject;
Googlerd21a0d12019-11-21 13:52:30 -080054import com.google.devtools.build.lib.syntax.Depset;
Googlera9c93632019-11-13 10:48:07 -080055import com.google.devtools.build.lib.syntax.Dict;
laurentlb707acfe2018-04-13 06:09:30 -070056import com.google.devtools.build.lib.syntax.EvalException;
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +000057import com.google.devtools.build.lib.syntax.EvalUtils;
adonovan034220a2020-03-24 10:11:26 -070058import com.google.devtools.build.lib.syntax.FileOptions;
59import com.google.devtools.build.lib.syntax.Module;
Googler92578702019-11-21 12:19:31 -080060import com.google.devtools.build.lib.syntax.Mutability;
Googler2abde272019-09-17 12:06:08 -070061import com.google.devtools.build.lib.syntax.ParserInput;
Googler66d099e2019-09-26 08:07:06 -070062import com.google.devtools.build.lib.syntax.StarlarkFile;
Googler942e1c42019-11-12 13:11:44 -080063import com.google.devtools.build.lib.syntax.StarlarkList;
adonovan35c67852020-02-12 14:58:03 -080064import com.google.devtools.build.lib.syntax.StarlarkThread;
Googlerf0890f02019-10-01 07:28:48 -070065import com.google.devtools.build.lib.syntax.SyntaxError;
Googlercfd681f2019-11-11 07:24:02 -080066import com.google.devtools.build.lib.syntax.Tuple;
Dmitry Lomov8ff5a872017-03-04 00:58:14 +000067import com.google.devtools.build.lib.testutil.MoreAsserts;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000068import com.google.devtools.build.lib.util.FileTypeSet;
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +000069import java.util.Collection;
Googler92578702019-11-21 12:19:31 -080070import javax.annotation.Nullable;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000071import org.junit.Before;
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +000072import org.junit.Rule;
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +000073import org.junit.Test;
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +000074import org.junit.rules.ExpectedException;
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +000075import org.junit.runner.RunWith;
76import org.junit.runners.JUnit4;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000077
Googlerba868542019-10-09 07:26:27 -070078/** Tests for SkylarkRuleClassFunctions. */
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +000079@RunWith(JUnit4.class)
Googlerba868542019-10-09 07:26:27 -070080public final class SkylarkRuleClassFunctionsTest extends SkylarkTestCase {
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +000081 @Rule public ExpectedException thrown = ExpectedException.none();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000082
83 @Before
Taras Tsugrii7fe70472018-07-25 13:58:02 -070084 public final void createBuildFile() throws Exception {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000085 scratch.file(
86 "foo/BUILD",
87 "genrule(name = 'foo',",
88 " cmd = 'dummy_cmd',",
89 " srcs = ['a.txt', 'b.img'],",
90 " tools = ['t.exe'],",
91 " outs = ['c.txt'])",
92 "genrule(name = 'bar',",
93 " cmd = 'dummy_cmd',",
94 " srcs = [':jl', ':gl'],",
95 " outs = ['d.txt'])",
96 "java_library(name = 'jl',",
97 " srcs = ['a.java'])",
98 "genrule(name = 'gl',",
99 " cmd = 'touch $(OUTS)',",
100 " srcs = ['a.go'],",
101 " outs = [ 'gl.a', 'gl.gcgox', ],",
102 " output_to_bindir = 1,",
103 ")");
104 }
105
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000106 @Test
Florian Weikerte96b0b82015-09-25 11:35:11 +0000107 public void testCannotOverrideBuiltInAttribute() throws Exception {
Dmitry Lomov950310f2017-03-01 17:45:12 +0000108 ev.setFailFast(false);
brandjon8bd20162017-12-28 08:49:54 -0800109 evalAndExport(
110 "def impl(ctx):",
111 " return",
112 "r = rule(impl, attrs = {'tags': attr.string_list()})");
113 ev.assertContainsError(
114 "There is already a built-in attribute 'tags' which cannot be overridden");
Florian Weikerte96b0b82015-09-25 11:35:11 +0000115 }
116
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000117 @Test
Vladimir Moskvada574922016-10-05 16:36:49 +0000118 public void testCannotOverrideBuiltInAttributeName() throws Exception {
Dmitry Lomov950310f2017-03-01 17:45:12 +0000119 ev.setFailFast(false);
brandjon8bd20162017-12-28 08:49:54 -0800120 evalAndExport(
121 "def impl(ctx):",
122 " return",
123 "r = rule(impl, attrs = {'name': attr.string()})");
124 ev.assertContainsError(
125 "There is already a built-in attribute 'name' which cannot be overridden");
Vladimir Moskvada574922016-10-05 16:36:49 +0000126 }
127
128 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000129 public void testImplicitArgsAttribute() throws Exception {
Dmitry Lomov950310f2017-03-01 17:45:12 +0000130 ev.setFailFast(false);
Dmitry Lomov7b599452015-11-26 10:07:32 +0000131 evalAndExport(
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000132 "def _impl(ctx):",
133 " pass",
134 "exec_rule = rule(implementation = _impl, executable = True)",
135 "non_exec_rule = rule(implementation = _impl)");
lberkiaea56b32017-05-30 12:35:33 +0200136 assertThat(getRuleClass("exec_rule").hasAttr("args", Type.STRING_LIST)).isTrue();
137 assertThat(getRuleClass("non_exec_rule").hasAttr("args", Type.STRING_LIST)).isFalse();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000138 }
139
140 private RuleClass getRuleClass(String name) throws Exception {
tomlu72642a22017-10-18 06:23:14 +0200141 return ((SkylarkRuleFunction) lookup(name)).getRuleClass();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000142 }
143
Googler3fcfbe12019-08-28 08:10:11 -0700144 private void registerDummyStarlarkFunction() throws Exception {
Googler1a1fca22019-10-14 09:31:22 -0700145 exec("def impl():", " pass");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000146 }
147
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000148 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000149 public void testAttrWithOnlyType() throws Exception {
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000150 Attribute attr = buildAttribute("a1", "attr.string_list()");
lberkiaea56b32017-05-30 12:35:33 +0200151 assertThat(attr.getType()).isEqualTo(Type.STRING_LIST);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000152 }
153
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000154 private Attribute buildAttribute(String name, String... lines) throws Exception {
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000155 String[] strings = lines.clone();
156 strings[strings.length - 1] = String.format("%s = %s", name, strings[strings.length - 1]);
157 evalAndExport(strings);
Googleree056632019-10-10 13:04:49 -0700158 Descriptor lookup = (Descriptor) lookup(name);
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000159 return lookup != null ? lookup.build(name) : null;
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000160 }
161
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000162 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000163 public void testOutputListAttr() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000164 Attribute attr = buildAttribute("a1", "attr.output_list()");
lberkiaea56b32017-05-30 12:35:33 +0200165 assertThat(attr.getType()).isEqualTo(BuildType.OUTPUT_LIST);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000166 }
167
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000168 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000169 public void testIntListAttr() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000170 Attribute attr = buildAttribute("a1", "attr.int_list()");
lberkiaea56b32017-05-30 12:35:33 +0200171 assertThat(attr.getType()).isEqualTo(Type.INTEGER_LIST);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000172 }
173
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000174 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000175 public void testOutputAttr() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000176 Attribute attr = buildAttribute("a1", "attr.output()");
lberkiaea56b32017-05-30 12:35:33 +0200177 assertThat(attr.getType()).isEqualTo(BuildType.OUTPUT);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000178 }
179
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000180 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000181 public void testStringDictAttr() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000182 Attribute attr = buildAttribute("a1", "attr.string_dict(default = {'a': 'b'})");
lberkiaea56b32017-05-30 12:35:33 +0200183 assertThat(attr.getType()).isEqualTo(Type.STRING_DICT);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000184 }
185
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000186 @Test
Francois-Rene Rideau028e3192015-10-29 14:26:59 +0000187 public void testStringListDictAttr() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000188 Attribute attr = buildAttribute("a1", "attr.string_list_dict(default = {'a': ['b', 'c']})");
lberkiaea56b32017-05-30 12:35:33 +0200189 assertThat(attr.getType()).isEqualTo(Type.STRING_LIST_DICT);
Francois-Rene Rideau028e3192015-10-29 14:26:59 +0000190 }
191
192 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000193 public void testAttrAllowedFileTypesAnyFile() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000194 Attribute attr = buildAttribute("a1", "attr.label_list(allow_files = True)");
lberkiaea56b32017-05-30 12:35:33 +0200195 assertThat(attr.getAllowedFileTypesPredicate()).isEqualTo(FileTypeSet.ANY_FILE);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000196 }
197
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000198 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000199 public void testAttrAllowedFileTypesWrongType() throws Exception {
Googleree056632019-10-10 13:04:49 -0700200 checkEvalErrorContains(
201 "allow_files should be a boolean or a string list", "attr.label_list(allow_files = 18)");
Laurent Le Brun2445df12016-05-11 14:36:40 +0000202 }
203
204 @Test
laurentlbe6ab3c42019-07-01 10:43:57 -0700205 public void testAttrNameSpecialCharactersAreForbidden() throws Exception {
laurentlbe6ab3c42019-07-01 10:43:57 -0700206 ev.setFailFast(false);
207 evalAndExport("def impl(ctx): return", "r = rule(impl, attrs = {'ab$c': attr.int()})");
laurentlb0fddb302019-08-16 14:02:01 -0700208 ev.assertContainsError("attribute name `ab$c` is not a valid identifier");
laurentlbe6ab3c42019-07-01 10:43:57 -0700209 }
210
211 @Test
212 public void testAttrNameCannotStartWithDigit() throws Exception {
laurentlbe6ab3c42019-07-01 10:43:57 -0700213 ev.setFailFast(false);
214 evalAndExport("def impl(ctx): return", "r = rule(impl, attrs = {'2_foo': attr.int()})");
laurentlb0fddb302019-08-16 14:02:01 -0700215 ev.assertContainsError("attribute name `2_foo` is not a valid identifier");
laurentlbe6ab3c42019-07-01 10:43:57 -0700216 }
217
218 @Test
michajlob839a512020-03-11 10:04:23 -0700219 public void testRuleClassTooManyAttributes() throws Exception {
220 ev.setFailFast(false);
221
222 ImmutableList.Builder<String> linesBuilder =
223 ImmutableList.<String>builder()
224 .add("def impl(ctx): return")
225 .add("r = rule(impl, attrs = {");
michajlo7475b772020-03-12 15:10:11 -0700226 for (int i = 0; i < 200; i++) {
michajlob839a512020-03-11 10:04:23 -0700227 linesBuilder.add(" 'attr" + i + "': attr.int(),");
228 }
229 linesBuilder.add("})");
230
231 evalAndExport(linesBuilder.build().toArray(new String[0]));
232
233 assertThat(ev.getEventCollector()).hasSize(1);
234 Event event = ev.getEventCollector().iterator().next();
235 assertThat(event.getKind()).isEqualTo(EventKind.ERROR);
236 assertThat(event.getMessage()).contains("Rule class r declared too many attributes");
237 }
238
239 @Test
michajlo0a89cef2020-04-06 12:04:12 -0700240 public void testRuleClassTooLongAttributeName() throws Exception {
241 ev.setFailFast(false);
242
243 evalAndExport(
244 "def impl(ctx): return;",
245 "r = rule(impl, attrs = { '" + Strings.repeat("x", 150) + "': attr.int() })");
246
247 assertThat(ev.getEventCollector()).hasSize(1);
248 Event event = ev.getEventCollector().iterator().next();
249 assertThat(event.getKind()).isEqualTo(EventKind.ERROR);
250 assertThat(event.getMessage())
251 .matches("Attribute r\\.x{150}'s name is too long \\(150 > 128\\)");
252 }
253
254 @Test
cparsonse5068582018-07-16 13:33:33 -0700255 public void testDisableDeprecatedParams() throws Exception {
Googleree056632019-10-10 13:04:49 -0700256 setSkylarkSemanticsOptions("--incompatible_disable_deprecated_attr_params=true");
cparsonse5068582018-07-16 13:33:33 -0700257
258 // Verify 'single_file' deprecation.
259 EvalException expected =
260 assertThrows(EvalException.class, () -> eval("attr.label(single_file = True)"));
261 assertThat(expected).hasMessageThat().contains(
262 "'single_file' is no longer supported. use allow_single_file instead.");
263 Attribute attr = buildAttribute("a1", "attr.label(allow_single_file = ['.xml'])");
264 assertThat(attr.isSingleArtifact()).isTrue();
265
266 // Verify 'non_empty' deprecation.
267 expected =
268 assertThrows(EvalException.class, () -> eval("attr.string_list(non_empty=True)"));
269 assertThat(expected).hasMessageThat().contains(
270 "'non_empty' is no longer supported. use allow_empty instead.");
271 attr = buildAttribute("a2", "attr.string_list(allow_empty=False)");
272 assertThat(attr.isNonEmpty()).isTrue();
273 }
274
275 @Test
Laurent Le Brun50681c12016-07-05 10:08:54 +0000276 public void testAttrAllowedSingleFileTypesWrongType() throws Exception {
Googleree056632019-10-10 13:04:49 -0700277 checkEvalErrorContains(
Laurent Le Brun50681c12016-07-05 10:08:54 +0000278 "allow_single_file should be a boolean or a string list",
279 "attr.label(allow_single_file = 18)");
280 }
281
282 @Test
Laurent Le Brun2445df12016-05-11 14:36:40 +0000283 public void testAttrWithList() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000284 Attribute attr = buildAttribute("a1", "attr.label_list(allow_files = ['.xml'])");
lberkiaea56b32017-05-30 12:35:33 +0200285 assertThat(attr.getAllowedFileTypesPredicate().apply("a.xml")).isTrue();
286 assertThat(attr.getAllowedFileTypesPredicate().apply("a.txt")).isFalse();
287 assertThat(attr.isSingleArtifact()).isFalse();
Laurent Le Brun50681c12016-07-05 10:08:54 +0000288 }
289
290 @Test
291 public void testAttrSingleFileWithList() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000292 Attribute attr = buildAttribute("a1", "attr.label(allow_single_file = ['.xml'])");
lberkiaea56b32017-05-30 12:35:33 +0200293 assertThat(attr.getAllowedFileTypesPredicate().apply("a.xml")).isTrue();
294 assertThat(attr.getAllowedFileTypesPredicate().apply("a.txt")).isFalse();
295 assertThat(attr.isSingleArtifact()).isTrue();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000296 }
297
gregce74d84d42020-04-17 10:02:03 -0700298 private static StarlarkProviderIdentifier legacy(String legacyId) {
299 return StarlarkProviderIdentifier.forLegacy(legacyId);
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000300 }
301
gregce74d84d42020-04-17 10:02:03 -0700302 private static StarlarkProviderIdentifier declared(String exportedName) {
gregceb6eafee2020-04-20 08:04:51 -0700303 return StarlarkProviderIdentifier.forKey(new StarlarkProvider.Key(FAKE_LABEL, exportedName));
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000304 }
305
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000306 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000307 public void testAttrWithProviders() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000308 Attribute attr =
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000309 buildAttribute("a1",
310 "b = provider()",
311 "attr.label_list(allow_files = True, providers = ['a', b])");
dslomovc13bb392017-08-02 23:29:54 +0200312 assertThat(attr.getRequiredProviders().isSatisfiedBy(set(legacy("a"), declared("b")))).isTrue();
313 assertThat(attr.getRequiredProviders().isSatisfiedBy(set(legacy("a")))).isFalse();
314 }
315
316 @Test
317 public void testAttrWithProvidersOneEmpty() throws Exception {
318 Attribute attr =
319 buildAttribute(
320 "a1",
321 "b = provider()",
322 "attr.label_list(allow_files = True, providers = [['a', b],[]])");
323 assertThat(attr.getRequiredProviders().acceptsAny()).isTrue();
dslomovc32e1b12017-07-31 19:23:52 +0200324 }
325
dslomovc32e1b12017-07-31 19:23:52 +0200326 @Test
Yun Peng83fbb91a2016-02-23 18:37:44 +0000327 public void testAttrWithProvidersList() throws Exception {
328 Attribute attr =
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000329 buildAttribute("a1",
330 "b = provider()",
331 "attr.label_list(allow_files = True, providers = [['a', b], ['c']])");
dslomovc13bb392017-08-02 23:29:54 +0200332 assertThat(attr.getRequiredProviders().isSatisfiedBy(set(legacy("a"), declared("b")))).isTrue();
333 assertThat(attr.getRequiredProviders().isSatisfiedBy(set(legacy("c")))).isTrue();
334 assertThat(attr.getRequiredProviders().isSatisfiedBy(set(legacy("a")))).isFalse();
335 }
336
gregce74d84d42020-04-17 10:02:03 -0700337 private static AdvertisedProviderSet set(StarlarkProviderIdentifier... ids) {
dslomovc13bb392017-08-02 23:29:54 +0200338 AdvertisedProviderSet.Builder builder = AdvertisedProviderSet.builder();
gregce74d84d42020-04-17 10:02:03 -0700339 for (StarlarkProviderIdentifier id : ids) {
dslomovc13bb392017-08-02 23:29:54 +0200340 builder.addSkylark(id);
341 }
342 return builder.build();
Yun Peng83fbb91a2016-02-23 18:37:44 +0000343 }
344
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000345 private void checkAttributeError(String expectedMessage, String... lines) throws Exception {
346 ev.setFailFast(false);
347 buildAttribute("fakeAttribute", lines);
348 MoreAsserts.assertContainsEvent(ev.getEventCollector(), expectedMessage);
349 }
350
Yun Peng83fbb91a2016-02-23 18:37:44 +0000351 @Test
352 public void testAttrWithWrongProvidersList() throws Exception {
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000353 checkAttributeError(
354 "element in 'providers' is of unexpected type. Either all elements should be providers,"
355 + " or all elements should be lists of providers,"
356 + " but got list with an element of type int.",
Yun Pengda9410c2016-03-18 21:14:51 +0000357 "attr.label_list(allow_files = True, providers = [['a', 1], ['c']])");
Yun Peng83fbb91a2016-02-23 18:37:44 +0000358
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000359 checkAttributeError(
360 "element in 'providers' is of unexpected type. Either all elements should be providers,"
361 + " or all elements should be lists of providers,"
362 + " but got an element of type string.",
363 "b = provider()",
364 "attr.label_list(allow_files = True, providers = [['a', b], 'c'])");
365
366 checkAttributeError(
367 "element in 'providers' is of unexpected type. Either all elements should be providers,"
368 + " or all elements should be lists of providers,"
369 + " but got an element of type string.",
370 "c = provider()",
371 "attr.label_list(allow_files = True, providers = [['a', b], c])");
Yun Peng83fbb91a2016-02-23 18:37:44 +0000372 }
373
374 @Test
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000375 public void testLabelListWithAspects() throws Exception {
Dmitry Lomov950310f2017-03-01 17:45:12 +0000376 evalAndExport(
Yun Pengda9410c2016-03-18 21:14:51 +0000377 "def _impl(target, ctx):",
378 " pass",
379 "my_aspect = aspect(implementation = _impl)",
Dmitry Lomov950310f2017-03-01 17:45:12 +0000380 "a = attr.label_list(aspects = [my_aspect])");
Googleree056632019-10-10 13:04:49 -0700381 SkylarkAttr.Descriptor attr = (SkylarkAttr.Descriptor) lookup("a");
382 SkylarkDefinedAspect aspect = (SkylarkDefinedAspect) lookup("my_aspect");
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000383 assertThat(aspect).isNotNull();
Dmitry Lomov950310f2017-03-01 17:45:12 +0000384 assertThat(attr.build("xxx").getAspectClasses()).containsExactly(aspect.getAspectClass());
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000385 }
386
387 @Test
Dmitry Lomov950310f2017-03-01 17:45:12 +0000388 public void testLabelWithAspects() throws Exception {
389 evalAndExport(
390 "def _impl(target, ctx):",
391 " pass",
392 "my_aspect = aspect(implementation = _impl)",
393 "a = attr.label(aspects = [my_aspect])");
Googleree056632019-10-10 13:04:49 -0700394 SkylarkAttr.Descriptor attr = (SkylarkAttr.Descriptor) lookup("a");
395 SkylarkDefinedAspect aspect = (SkylarkDefinedAspect) lookup("my_aspect");
Dmitry Lomov950310f2017-03-01 17:45:12 +0000396 assertThat(aspect).isNotNull();
397 assertThat(attr.build("xxx").getAspectClasses()).containsExactly(aspect.getAspectClass());
398 }
399
Dmitry Lomov950310f2017-03-01 17:45:12 +0000400 @Test
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000401 public void testLabelListWithAspectsError() throws Exception {
Googleree056632019-10-10 13:04:49 -0700402 checkEvalErrorContains(
adonovan85803902020-04-16 14:46:57 -0700403 "at index 0 of aspects, got element of type int, want Aspect",
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000404 "def _impl(target, ctx):",
405 " pass",
406 "my_aspect = aspect(implementation = _impl)",
Laurent Le Brunc31f3512016-12-29 21:41:33 +0000407 "attr.label_list(aspects = [my_aspect, 123])");
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000408 }
409
410 @Test
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000411 public void testAspectExtraDeps() throws Exception {
412 evalAndExport(
413 "def _impl(target, ctx):",
414 " pass",
415 "my_aspect = aspect(_impl,",
Dmitry Lomovace678e2015-12-16 15:10:20 +0000416 " attrs = { '_extra_deps' : attr.label(default = Label('//foo/bar:baz')) }",
417 ")");
Googleree056632019-10-10 13:04:49 -0700418 SkylarkDefinedAspect aspect = (SkylarkDefinedAspect) lookup("my_aspect");
Googler74558fc2016-05-06 21:47:42 +0000419 Attribute attribute = Iterables.getOnlyElement(aspect.getAttributes());
420 assertThat(attribute.getName()).isEqualTo("$extra_deps");
421 assertThat(attribute.getDefaultValue(null))
dannarkda327bb2018-06-22 11:44:27 -0700422 .isEqualTo(
423 Label.parseAbsolute(
424 "//foo/bar:baz",
425 /* defaultToMain= */ false,
426 /* repositoryMapping= */ ImmutableMap.of()));
Dmitry Lomovace678e2015-12-16 15:10:20 +0000427 }
428
429 @Test
Googler74558fc2016-05-06 21:47:42 +0000430 public void testAspectParameter() throws Exception {
431 evalAndExport(
Dmitry Lomovace678e2015-12-16 15:10:20 +0000432 "def _impl(target, ctx):",
433 " pass",
434 "my_aspect = aspect(_impl,",
Googler74558fc2016-05-06 21:47:42 +0000435 " attrs = { 'param' : attr.string(values=['a', 'b']) }",
Dmitry Lomovace678e2015-12-16 15:10:20 +0000436 ")");
Googleree056632019-10-10 13:04:49 -0700437 SkylarkDefinedAspect aspect = (SkylarkDefinedAspect) lookup("my_aspect");
Googler74558fc2016-05-06 21:47:42 +0000438 Attribute attribute = Iterables.getOnlyElement(aspect.getAttributes());
439 assertThat(attribute.getName()).isEqualTo("param");
440 }
Michajlo Matijkiw6d471412016-08-09 20:35:45 +0000441
Googler74558fc2016-05-06 21:47:42 +0000442 @Test
443 public void testAspectParameterRequiresValues() throws Exception {
Googleree056632019-10-10 13:04:49 -0700444 checkEvalErrorContains(
Googler74558fc2016-05-06 21:47:42 +0000445 "Aspect parameter attribute 'param' must have type 'string' and use the 'values' "
Googleree056632019-10-10 13:04:49 -0700446 + "restriction.",
Googler74558fc2016-05-06 21:47:42 +0000447 "def _impl(target, ctx):",
448 " pass",
449 "my_aspect = aspect(_impl,",
450 " attrs = { 'param' : attr.string(default = 'c') }",
451 ")");
452 }
453
454 @Test
455 public void testAspectParameterBadType() throws Exception {
Googleree056632019-10-10 13:04:49 -0700456 checkEvalErrorContains(
Googler74558fc2016-05-06 21:47:42 +0000457 "Aspect parameter attribute 'param' must have type 'string' and use the 'values' "
Googleree056632019-10-10 13:04:49 -0700458 + "restriction.",
Googler74558fc2016-05-06 21:47:42 +0000459 "def _impl(target, ctx):",
460 " pass",
461 "my_aspect = aspect(_impl,",
462 " attrs = { 'param' : attr.label(default = Label('//foo/bar:baz')) }",
463 ")");
464 }
465
466 @Test
467 public void testAspectParameterAndExtraDeps() throws Exception {
468 evalAndExport(
469 "def _impl(target, ctx):",
470 " pass",
471 "my_aspect = aspect(_impl,",
472 " attrs = { 'param' : attr.string(values=['a', 'b']),",
473 " '_extra' : attr.label(default = Label('//foo/bar:baz')) }",
474 ")");
Googleree056632019-10-10 13:04:49 -0700475 SkylarkDefinedAspect aspect = (SkylarkDefinedAspect) lookup("my_aspect");
Googler74558fc2016-05-06 21:47:42 +0000476 assertThat(aspect.getAttributes()).hasSize(2);
477 assertThat(aspect.getParamAttributes()).containsExactly("param");
Dmitry Lomovace678e2015-12-16 15:10:20 +0000478 }
479
480 @Test
481 public void testAspectNoDefaultValueAttribute() throws Exception {
Googleree056632019-10-10 13:04:49 -0700482 checkEvalErrorContains(
Dmitry Lomovace678e2015-12-16 15:10:20 +0000483 "Aspect attribute '_extra_deps' has no default value",
484 "def _impl(target, ctx):",
485 " pass",
486 "my_aspect = aspect(_impl,",
487 " attrs = { '_extra_deps' : attr.label() }",
488 ")");
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000489 }
490
491 @Test
John Cater9a8d16e2017-07-05 16:12:07 -0400492 public void testAspectAddToolchain() throws Exception {
493 scratch.file("test/BUILD", "toolchain_type(name = 'my_toolchain_type')");
494 evalAndExport(
495 "def _impl(ctx): pass", "a1 = aspect(_impl, toolchains=['//test:my_toolchain_type'])");
cparsons0d55f4c2017-12-20 14:49:13 -0800496 SkylarkDefinedAspect a = (SkylarkDefinedAspect) lookup("a1");
John Cater9a8d16e2017-07-05 16:12:07 -0400497 assertThat(a.getRequiredToolchains()).containsExactly(makeLabel("//test:my_toolchain_type"));
498 }
499
500 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000501 public void testNonLabelAttrWithProviders() throws Exception {
Googleree056632019-10-10 13:04:49 -0700502 checkEvalErrorContains(
adonovan3f602f22020-01-08 10:28:10 -0800503 "unexpected keyword argument 'providers'", "attr.string(providers = ['a'])");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000504 }
505
cparsonse2d200f2018-03-06 16:15:11 -0800506 private static final RuleClass.ConfiguredTargetFactory<Object, Object, Exception>
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000507 DUMMY_CONFIGURED_TARGET_FACTORY =
dslomovc13bb392017-08-02 23:29:54 +0200508 ruleContext -> {
509 throw new IllegalStateException();
510 };
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000511
512 private RuleClass ruleClass(String name) {
513 return new RuleClass.Builder(name, RuleClassType.NORMAL, false)
514 .factory(DUMMY_CONFIGURED_TARGET_FACTORY)
515 .add(Attribute.attr("tags", Type.STRING_LIST))
516 .build();
517 }
vladmos9c787fa2017-07-04 11:45:22 -0400518
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000519 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000520 public void testAttrAllowedRuleClassesSpecificRuleClasses() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000521 Attribute attr = buildAttribute("a",
522 "attr.label_list(allow_rules = ['java_binary'], allow_files = True)");
lberkiaea56b32017-05-30 12:35:33 +0200523 assertThat(attr.getAllowedRuleClassesPredicate().apply(ruleClass("java_binary"))).isTrue();
524 assertThat(attr.getAllowedRuleClassesPredicate().apply(ruleClass("genrule"))).isFalse();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000525 }
vladmos9c787fa2017-07-04 11:45:22 -0400526
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000527 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000528 public void testAttrDefaultValue() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000529 Attribute attr = buildAttribute("a1", "attr.string(default = 'some value')");
juliexxia84d1a662018-12-26 14:07:04 -0800530 assertThat(attr.getDefaultValueUnchecked()).isEqualTo("some value");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000531 }
532
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000533 @Test
vladmos9c787fa2017-07-04 11:45:22 -0400534 public void testLabelAttrDefaultValueAsString() throws Exception {
535 Attribute sligleAttr = buildAttribute("a1", "attr.label(default = '//foo:bar')");
juliexxia84d1a662018-12-26 14:07:04 -0800536 assertThat(sligleAttr.getDefaultValueUnchecked())
dannarkda327bb2018-06-22 11:44:27 -0700537 .isEqualTo(
538 Label.parseAbsolute(
539 "//foo:bar",
540 /* defaultToMain= */ false,
541 /* repositoryMapping= */ ImmutableMap.of()));
vladmos9c787fa2017-07-04 11:45:22 -0400542
543 Attribute listAttr =
544 buildAttribute("a2", "attr.label_list(default = ['//foo:bar', '//bar:foo'])");
juliexxia84d1a662018-12-26 14:07:04 -0800545 assertThat(listAttr.getDefaultValueUnchecked())
vladmos9c787fa2017-07-04 11:45:22 -0400546 .isEqualTo(
547 ImmutableList.of(
dannarkda327bb2018-06-22 11:44:27 -0700548 Label.parseAbsolute(
549 "//foo:bar",
550 /* defaultToMain= */ false,
551 /* repositoryMapping= */ ImmutableMap.of()),
552 Label.parseAbsolute(
553 "//bar:foo",
554 /* defaultToMain= */ false,
555 /*repositoryMapping= */ ImmutableMap.of())));
vladmos9c787fa2017-07-04 11:45:22 -0400556
557 Attribute dictAttr =
558 buildAttribute("a3", "attr.label_keyed_string_dict(default = {'//foo:bar': 'my value'})");
juliexxia84d1a662018-12-26 14:07:04 -0800559 assertThat(dictAttr.getDefaultValueUnchecked())
dannarkda327bb2018-06-22 11:44:27 -0700560 .isEqualTo(
561 ImmutableMap.of(
562 Label.parseAbsolute(
563 "//foo:bar",
564 /* defaultToMain= */ false,
565 /* repositoryMapping= */ ImmutableMap.of()),
566 "my value"));
vladmos9c787fa2017-07-04 11:45:22 -0400567 }
568
569 @Test
570 public void testLabelAttrDefaultValueAsStringBadValue() throws Exception {
Googleree056632019-10-10 13:04:49 -0700571 checkEvalErrorContains(
vladmos9c787fa2017-07-04 11:45:22 -0400572 "invalid label '/foo:bar' in parameter 'default' of attribute 'label': "
janakrd3fe5e72018-03-30 12:49:12 -0700573 + "invalid target name '/foo:bar'",
vladmos9c787fa2017-07-04 11:45:22 -0400574 "attr.label(default = '/foo:bar')");
575
Googleree056632019-10-10 13:04:49 -0700576 checkEvalErrorContains(
vladmos9c787fa2017-07-04 11:45:22 -0400577 "invalid label '/bar:foo' in element 1 of parameter 'default' of attribute "
janakrd3fe5e72018-03-30 12:49:12 -0700578 + "'label_list': invalid target name '/bar:foo'",
vladmos9c787fa2017-07-04 11:45:22 -0400579 "attr.label_list(default = ['//foo:bar', '/bar:foo'])");
580
Googleree056632019-10-10 13:04:49 -0700581 checkEvalErrorContains(
janakrd3fe5e72018-03-30 12:49:12 -0700582 "invalid label '/bar:foo' in dict key element: invalid target name '/bar:foo'",
vladmos9c787fa2017-07-04 11:45:22 -0400583 "attr.label_keyed_string_dict(default = {'//foo:bar': 'a', '/bar:foo': 'b'})");
584 }
585
586 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000587 public void testAttrDefaultValueBadType() throws Exception {
adonovan3f602f22020-01-08 10:28:10 -0800588 checkEvalErrorContains("got value of type 'int', want 'string'", "attr.string(default = 1)");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000589 }
590
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000591 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000592 public void testAttrMandatory() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000593 Attribute attr = buildAttribute("a1", "attr.string(mandatory=True)");
lberkiaea56b32017-05-30 12:35:33 +0200594 assertThat(attr.isMandatory()).isTrue();
595 assertThat(attr.isNonEmpty()).isFalse();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000596 }
597
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000598 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000599 public void testAttrNonEmpty() throws Exception {
Googleree056632019-10-10 13:04:49 -0700600 setSkylarkSemanticsOptions("--incompatible_disable_deprecated_attr_params=false");
601 reset();
laurentlb140c04b2019-05-21 06:56:28 -0700602
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000603 Attribute attr = buildAttribute("a1", "attr.string_list(non_empty=True)");
lberkiaea56b32017-05-30 12:35:33 +0200604 assertThat(attr.isNonEmpty()).isTrue();
605 assertThat(attr.isMandatory()).isFalse();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000606 }
607
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000608 @Test
Laurent Le Brunbc16f722016-07-06 13:40:24 +0000609 public void testAttrAllowEmpty() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000610 Attribute attr = buildAttribute("a1", "attr.string_list(allow_empty=False)");
lberkiaea56b32017-05-30 12:35:33 +0200611 assertThat(attr.isNonEmpty()).isTrue();
612 assertThat(attr.isMandatory()).isFalse();
Laurent Le Brunbc16f722016-07-06 13:40:24 +0000613 }
614
615 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000616 public void testAttrBadKeywordArguments() throws Exception {
Googleree056632019-10-10 13:04:49 -0700617 checkEvalErrorContains(
adonovan3f602f22020-01-08 10:28:10 -0800618 "string() got unexpected keyword argument 'bad_keyword'", "attr.string(bad_keyword = '')");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000619 }
620
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000621 @Test
Laurent Le Brunc4ddf6f2016-07-04 13:38:38 +0000622 public void testAttrCfg() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000623 Attribute attr = buildAttribute("a1", "attr.label(cfg = 'host', allow_files = True)");
jcaterb44167f2019-04-02 12:06:26 -0700624 assertThat(attr.getTransitionFactory().isHost()).isTrue();
Laurent Le Brunc4ddf6f2016-07-04 13:38:38 +0000625 }
626
627 @Test
Vladimir Moskva5a510772016-11-23 19:03:38 +0000628 public void testAttrCfgTarget() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000629 Attribute attr = buildAttribute("a1", "attr.label(cfg = 'target', allow_files = True)");
John Cater5adcd3e2019-03-28 10:14:32 -0700630 assertThat(NoTransition.isInstance(attr.getTransitionFactory())).isTrue();
Vladimir Moskva5a510772016-11-23 19:03:38 +0000631 }
632
633 @Test
gregcebceecab2018-06-27 17:44:45 -0700634 public void incompatibleDataTransition() throws Exception {
gregcebceecab2018-06-27 17:44:45 -0700635 EvalException expected =
636 assertThrows(EvalException.class, () -> eval("attr.label(cfg = 'data')"));
laurentlbd576ed02019-03-26 15:35:06 -0700637 assertThat(expected).hasMessageThat().contains("cfg must be either 'host' or 'target'");
gregcebceecab2018-06-27 17:44:45 -0700638 }
639
640 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000641 public void testAttrValues() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000642 Attribute attr = buildAttribute("a1", "attr.string(values = ['ab', 'cd'])");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000643 PredicateWithMessage<Object> predicate = attr.getAllowedValues();
644 assertThat(predicate.apply("ab")).isTrue();
645 assertThat(predicate.apply("xy")).isFalse();
646 }
647
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000648 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000649 public void testAttrIntValues() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000650 Attribute attr = buildAttribute("a1", "attr.int(values = [1, 2])");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000651 PredicateWithMessage<Object> predicate = attr.getAllowedValues();
652 assertThat(predicate.apply(2)).isTrue();
653 assertThat(predicate.apply(3)).isFalse();
654 }
655
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000656 @Test
allevato45b79e52017-07-07 21:40:50 +0200657 public void testAttrDoc() throws Exception {
658 // We don't actually store the doc in the attr definition; right now it's just meant to be
659 // extracted by documentation generating tools. So we don't have anything to assert and we just
660 // verify that no exceptions were thrown from building them.
661 buildAttribute("a1", "attr.bool(doc='foo')");
662 buildAttribute("a2", "attr.int(doc='foo')");
663 buildAttribute("a3", "attr.int_list(doc='foo')");
664 buildAttribute("a4", "attr.label(doc='foo')");
665 buildAttribute("a5", "attr.label_keyed_string_dict(doc='foo')");
666 buildAttribute("a6", "attr.label_list(doc='foo')");
allevato45b79e52017-07-07 21:40:50 +0200667 buildAttribute("a8", "attr.output(doc='foo')");
668 buildAttribute("a9", "attr.output_list(doc='foo')");
669 buildAttribute("a10", "attr.string(doc='foo')");
670 buildAttribute("a11", "attr.string_dict(doc='foo')");
671 buildAttribute("a12", "attr.string_list(doc='foo')");
672 buildAttribute("a13", "attr.string_list_dict(doc='foo')");
673 }
674
675 @Test
laurentlbd8d37762018-10-26 14:08:33 -0700676 public void testNoAttrLicense() throws Exception {
laurentlbd8d37762018-10-26 14:08:33 -0700677 EvalException expected = assertThrows(EvalException.class, () -> eval("attr.license()"));
678 assertThat(expected)
679 .hasMessageThat()
adonovan4e2b4952019-12-10 12:19:20 -0800680 .contains("'attr (a language module)' value has no field or method 'license'");
laurentlbd8d37762018-10-26 14:08:33 -0700681 }
682
683 @Test
allevato45b79e52017-07-07 21:40:50 +0200684 public void testAttrDocValueBadType() throws Exception {
adonovan3f602f22020-01-08 10:28:10 -0800685 checkEvalErrorContains("got value of type 'int', want 'string'", "attr.string(doc = 1)");
allevato45b79e52017-07-07 21:40:50 +0200686 }
687
688 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000689 public void testRuleImplementation() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000690 evalAndExport("def impl(ctx): return None", "rule1 = rule(impl)");
tomlu72642a22017-10-18 06:23:14 +0200691 RuleClass c = ((SkylarkRuleFunction) lookup("rule1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200692 assertThat(c.getConfiguredTargetFunction().getName()).isEqualTo("impl");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000693 }
694
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000695 @Test
allevato45b79e52017-07-07 21:40:50 +0200696 public void testRuleDoc() throws Exception {
697 evalAndExport("def impl(ctx): return None", "rule1 = rule(impl, doc='foo')");
698 }
699
700 @Test
adonovan3f602f22020-01-08 10:28:10 -0800701 public void testFunctionAsAttrDefault() throws Exception {
702 exec("def f(): pass");
703
adonovanaf226dd2019-12-17 12:43:00 -0800704 // Late-bound attributes, which are computed during analysis as a function
705 // of the configuration, are only available for attributes involving labels:
706 // attr.label
707 // attr.label_list
708 // attr.label_keyed_string_dict
709 // attr.output,
710 // attr.output_list
adonovan3f602f22020-01-08 10:28:10 -0800711 // (See testRuleClassImplicitOutputFunctionDependingOnComputedAttribute
712 // for a more detailed positive test.)
713 evalAndExport(
714 "attr.label(default=f)",
715 "attr.label_list(default=f)",
716 "attr.label_keyed_string_dict(default=f)");
717 // Note: the default parameter of attr.output{,_list} is deprecated
718 // (see --incompatible_no_output_attr_default)
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000719
adonovan3f602f22020-01-08 10:28:10 -0800720 // For all other attribute types, the default value may not be a function.
adonovanaf226dd2019-12-17 12:43:00 -0800721 //
adonovan3f602f22020-01-08 10:28:10 -0800722 // (This is a regression test for github.com/bazelbuild/bazel/issues/9463.
723 // The loading-phase feature of "computed attribute defaults" is not exposed
724 // to Starlark; the bug was that the @SkylarkCallable
adonovanaf226dd2019-12-17 12:43:00 -0800725 // annotation was more permissive than the method declaration.)
adonovan3f602f22020-01-08 10:28:10 -0800726 checkEvalErrorContains("got value of type 'function', want 'string'", "attr.string(default=f)");
adonovanaf226dd2019-12-17 12:43:00 -0800727 checkEvalErrorContains(
adonovan7f266402020-04-20 10:15:47 -0700728 "got value of type 'function', want 'sequence'", "attr.string_list(default=f)");
adonovan3f602f22020-01-08 10:28:10 -0800729 checkEvalErrorContains("got value of type 'function', want 'int'", "attr.int(default=f)");
adonovanaf226dd2019-12-17 12:43:00 -0800730 checkEvalErrorContains(
adonovan7f266402020-04-20 10:15:47 -0700731 "got value of type 'function', want 'sequence'", "attr.int_list(default=f)");
adonovan3f602f22020-01-08 10:28:10 -0800732 checkEvalErrorContains("got value of type 'function', want 'bool'", "attr.bool(default=f)");
adonovanaf226dd2019-12-17 12:43:00 -0800733 checkEvalErrorContains(
adonovan3f602f22020-01-08 10:28:10 -0800734 "got value of type 'function', want 'dict'", "attr.string_dict(default=f)");
adonovanaf226dd2019-12-17 12:43:00 -0800735 checkEvalErrorContains(
adonovan3f602f22020-01-08 10:28:10 -0800736 "got value of type 'function', want 'dict'", "attr.string_list_dict(default=f)");
737 // Note: attr.license appears to be disabled already.
738 // (see --incompatible_no_attr_license)
adonovanaf226dd2019-12-17 12:43:00 -0800739 }
740
Dmitry Lomov7b599452015-11-26 10:07:32 +0000741 private static final Label FAKE_LABEL = Label.parseAbsoluteUnchecked("//fake/label.bzl");
742
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000743 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000744 public void testRuleAddAttribute() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000745 evalAndExport("def impl(ctx): return None", "r1 = rule(impl, attrs={'a1': attr.string()})");
tomlu72642a22017-10-18 06:23:14 +0200746 RuleClass c = ((SkylarkRuleFunction) lookup("r1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200747 assertThat(c.hasAttr("a1", Type.STRING)).isTrue();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000748 }
749
Googlerba868542019-10-09 07:26:27 -0700750 private void evalAndExport(String... lines) throws Exception {
Googler2abde272019-09-17 12:06:08 -0700751 ParserInput input = ParserInput.fromLines(lines);
adonovan35c67852020-02-12 14:58:03 -0800752 StarlarkThread thread = ev.getStarlarkThread();
adonovan034220a2020-03-24 10:11:26 -0700753 Module module = thread.getGlobals();
754 StarlarkFile file = EvalUtils.parseAndValidate(input, FileOptions.DEFAULT, module);
Googlerba868542019-10-09 07:26:27 -0700755 if (!file.ok()) {
adonovanac1c41e2020-04-01 14:28:49 -0700756 throw new SyntaxError.Exception(file.errors());
Googlerf0890f02019-10-01 07:28:48 -0700757 }
gregce1cd84ec2020-04-09 15:45:19 -0700758 StarlarkImportLookupFunction.execAndExport(file, FAKE_LABEL, ev.getEventHandler(), thread);
Dmitry Lomov7b599452015-11-26 10:07:32 +0000759 }
760
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000761 @Test
Jon Brandveinded4fbb2017-01-18 22:21:04 +0000762 public void testExportAliasedName() throws Exception {
763 // When there are multiple names aliasing the same SkylarkExportable, the first one to be
764 // declared should be used. Make sure we're not using lexicographical order, hash order,
765 // non-deterministic order, or anything else.
766 evalAndExport(
767 "def _impl(ctx): pass",
768 "d = rule(implementation = _impl)",
769 "a = d",
770 // Having more names improves the chance that non-determinism will be caught.
771 "b = d",
772 "c = d",
773 "e = d",
774 "f = d",
775 "foo = d",
776 "bar = d",
777 "baz = d",
778 "x = d",
779 "y = d",
780 "z = d");
tomlu72642a22017-10-18 06:23:14 +0200781 String dName = ((SkylarkRuleFunction) lookup("d")).getRuleClass().getName();
782 String fooName = ((SkylarkRuleFunction) lookup("foo")).getRuleClass().getName();
Jon Brandveinded4fbb2017-01-18 22:21:04 +0000783 assertThat(dName).isEqualTo("d");
784 assertThat(fooName).isEqualTo("d");
785 }
786
787 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000788 public void testOutputToGenfiles() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000789 evalAndExport("def impl(ctx): pass", "r1 = rule(impl, output_to_genfiles=True)");
tomlu72642a22017-10-18 06:23:14 +0200790 RuleClass c = ((SkylarkRuleFunction) lookup("r1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200791 assertThat(c.hasBinaryOutput()).isFalse();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000792 }
793
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000794 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000795 public void testRuleAddMultipleAttributes() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000796 evalAndExport(
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000797 "def impl(ctx): return None",
798 "r1 = rule(impl,",
799 " attrs = {",
800 " 'a1': attr.label_list(allow_files=True),",
801 " 'a2': attr.int()",
802 "})");
tomlu72642a22017-10-18 06:23:14 +0200803 RuleClass c = ((SkylarkRuleFunction) lookup("r1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200804 assertThat(c.hasAttr("a1", BuildType.LABEL_LIST)).isTrue();
805 assertThat(c.hasAttr("a2", Type.INTEGER)).isTrue();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000806 }
Taras Tsugrii7fe70472018-07-25 13:58:02 -0700807
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000808 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000809 public void testRuleAttributeFlag() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000810 evalAndExport(
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000811 "def impl(ctx): return None",
812 "r1 = rule(impl, attrs = {'a1': attr.string(mandatory=True)})");
tomlu72642a22017-10-18 06:23:14 +0200813 RuleClass c = ((SkylarkRuleFunction) lookup("r1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200814 assertThat(c.getAttributeByName("a1").isMandatory()).isTrue();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000815 }
816
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000817 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000818 public void testRuleOutputs() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000819 evalAndExport(
820 "def impl(ctx): return None",
821 "r1 = rule(impl, outputs = {'a': 'a.txt'})");
tomlu72642a22017-10-18 06:23:14 +0200822 RuleClass c = ((SkylarkRuleFunction) lookup("r1")).getRuleClass();
Michajlo Matijkiw6d471412016-08-09 20:35:45 +0000823 ImplicitOutputsFunction function = c.getDefaultImplicitOutputsFunction();
vladmos076977e2017-12-02 14:15:32 -0800824 assertThat(function.getImplicitOutputs(ev.getEventHandler(), null)).containsExactly("a.txt");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000825 }
826
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000827 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000828 public void testRuleUnknownKeyword() throws Exception {
Googler3fcfbe12019-08-28 08:10:11 -0700829 registerDummyStarlarkFunction();
Googleree056632019-10-10 13:04:49 -0700830 checkEvalErrorContains(
adonovan3f602f22020-01-08 10:28:10 -0800831 "unexpected keyword argument 'bad_keyword'", "rule(impl, bad_keyword = 'some text')");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000832 }
833
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000834 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000835 public void testRuleImplementationMissing() throws Exception {
Googleree056632019-10-10 13:04:49 -0700836 checkEvalErrorContains(
adonovan3f602f22020-01-08 10:28:10 -0800837 "rule() missing 1 required positional argument: implementation", "rule(attrs = {})");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000838 }
839
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000840 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000841 public void testRuleBadTypeForAdd() throws Exception {
Googler3fcfbe12019-08-28 08:10:11 -0700842 registerDummyStarlarkFunction();
Googleree056632019-10-10 13:04:49 -0700843 checkEvalErrorContains(
adonovan3f602f22020-01-08 10:28:10 -0800844 "in call to rule(), parameter 'attrs' got value of type 'string', want 'dict or NoneType'",
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000845 "rule(impl, attrs = 'some text')");
846 }
847
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000848 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000849 public void testRuleBadTypeInAdd() throws Exception {
Googler3fcfbe12019-08-28 08:10:11 -0700850 registerDummyStarlarkFunction();
Googleree056632019-10-10 13:04:49 -0700851 checkEvalErrorContains(
adonovan07fb6a52020-03-20 14:36:28 -0700852 "got dict<string, string> for 'attrs', want dict<string, Attribute>",
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000853 "rule(impl, attrs = {'a1': 'some text'})");
854 }
855
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000856 @Test
allevato45b79e52017-07-07 21:40:50 +0200857 public void testRuleBadTypeForDoc() throws Exception {
Googler3fcfbe12019-08-28 08:10:11 -0700858 registerDummyStarlarkFunction();
adonovan3f602f22020-01-08 10:28:10 -0800859 checkEvalErrorContains("got value of type 'int', want 'string'", "rule(impl, doc = 1)");
allevato45b79e52017-07-07 21:40:50 +0200860 }
861
862 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000863 public void testLabel() throws Exception {
Googleree056632019-10-10 13:04:49 -0700864 Object result = eval("Label('//foo/foo:foo')");
Dmitry Lomov7b599452015-11-26 10:07:32 +0000865 assertThat(result).isInstanceOf(Label.class);
lberkiaea56b32017-05-30 12:35:33 +0200866 assertThat(result.toString()).isEqualTo("//foo/foo:foo");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000867 }
868
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000869 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000870 public void testLabelSameInstance() throws Exception {
Googleree056632019-10-10 13:04:49 -0700871 Object l1 = eval("Label('//foo/foo:foo')");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000872 // Implicitly creates a new pkgContext and environment, yet labels should be the same.
Googleree056632019-10-10 13:04:49 -0700873 Object l2 = eval("Label('//foo/foo:foo')");
cpovirka4d3da62019-05-02 14:27:33 -0700874 assertThat(l1).isSameInstanceAs(l2);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000875 }
876
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000877 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000878 public void testLabelNameAndPackage() throws Exception {
Googleree056632019-10-10 13:04:49 -0700879 Object result = eval("Label('//foo/bar:baz').name");
lberkiaea56b32017-05-30 12:35:33 +0200880 assertThat(result).isEqualTo("baz");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000881 // NB: implicitly creates a new pkgContext and environments, yet labels should be the same.
Googleree056632019-10-10 13:04:49 -0700882 result = eval("Label('//foo/bar:baz').package");
lberkiaea56b32017-05-30 12:35:33 +0200883 assertThat(result).isEqualTo("foo/bar");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000884 }
885
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000886 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000887 public void testRuleLabelDefaultValue() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000888 evalAndExport(
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000889 "def impl(ctx): return None\n"
890 + "r1 = rule(impl, attrs = {'a1': "
891 + "attr.label(default = Label('//foo:foo'), allow_files=True)})");
tomlu72642a22017-10-18 06:23:14 +0200892 RuleClass c = ((SkylarkRuleFunction) lookup("r1")).getRuleClass();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000893 Attribute a = c.getAttributeByName("a1");
juliexxia84d1a662018-12-26 14:07:04 -0800894 assertThat(a.getDefaultValueUnchecked()).isInstanceOf(Label.class);
895 assertThat(a.getDefaultValueUnchecked().toString()).isEqualTo("//foo:foo");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000896 }
897
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000898 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000899 public void testIntDefaultValue() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000900 evalAndExport(
901 "def impl(ctx): return None",
902 "r1 = rule(impl, attrs = {'a1': attr.int(default = 40+2)})");
tomlu72642a22017-10-18 06:23:14 +0200903 RuleClass c = ((SkylarkRuleFunction) lookup("r1")).getRuleClass();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000904 Attribute a = c.getAttributeByName("a1");
juliexxia84d1a662018-12-26 14:07:04 -0800905 assertThat(a.getDefaultValueUnchecked()).isEqualTo(42);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000906 }
907
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000908 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000909 public void testRuleInheritsBaseRuleAttributes() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000910 evalAndExport("def impl(ctx): return None", "r1 = rule(impl)");
tomlu72642a22017-10-18 06:23:14 +0200911 RuleClass c = ((SkylarkRuleFunction) lookup("r1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200912 assertThat(c.hasAttr("tags", Type.STRING_LIST)).isTrue();
913 assertThat(c.hasAttr("visibility", BuildType.NODEP_LABEL_LIST)).isTrue();
914 assertThat(c.hasAttr("deprecation", Type.STRING)).isTrue();
915 assertThat(c.hasAttr(":action_listener", BuildType.LABEL_LIST))
916 .isTrue(); // required for extra actions
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000917 }
918
919 private void checkTextMessage(String from, String... lines) throws Exception {
fangismed97d842019-04-12 14:19:46 -0700920 String[] strings = lines.clone();
Googleree056632019-10-10 13:04:49 -0700921 Object result = eval(from);
fangismed97d842019-04-12 14:19:46 -0700922 String expect = "";
923 if (strings.length > 0) {
924 expect = Joiner.on("\n").join(lines) + "\n";
925 }
926 assertThat(result).isEqualTo(expect);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000927 }
928
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000929 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000930 public void testSimpleTextMessagesBooleanFields() throws Exception {
931 checkTextMessage("struct(name=True).to_proto()", "name: true");
932 checkTextMessage("struct(name=False).to_proto()", "name: false");
933 }
934
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000935 @Test
cparsons07460fc2018-06-20 10:41:48 -0700936 public void testStructRestrictedOverrides() throws Exception {
Googleree056632019-10-10 13:04:49 -0700937 checkEvalErrorContains(
938 "cannot override built-in struct function 'to_json'", "struct(to_json='foo')");
cparsons07460fc2018-06-20 10:41:48 -0700939
Googleree056632019-10-10 13:04:49 -0700940 checkEvalErrorContains(
941 "cannot override built-in struct function 'to_proto'", "struct(to_proto='foo')");
cparsons07460fc2018-06-20 10:41:48 -0700942 }
943
944 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000945 public void testSimpleTextMessages() throws Exception {
946 checkTextMessage("struct(name='value').to_proto()", "name: \"value\"");
fangismed97d842019-04-12 14:19:46 -0700947 checkTextMessage("struct(name=[]).to_proto()"); // empty lines
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000948 checkTextMessage("struct(name=['a', 'b']).to_proto()", "name: \"a\"", "name: \"b\"");
949 checkTextMessage("struct(name=123).to_proto()", "name: 123");
950 checkTextMessage("struct(name=[1, 2, 3]).to_proto()", "name: 1", "name: 2", "name: 3");
951 checkTextMessage("struct(a=struct(b='b')).to_proto()", "a {", " b: \"b\"", "}");
952 checkTextMessage(
953 "struct(a=[struct(b='x'), struct(b='y')]).to_proto()",
954 "a {",
955 " b: \"x\"",
956 "}",
957 "a {",
958 " b: \"y\"",
959 "}");
960 checkTextMessage(
961 "struct(a=struct(b=struct(c='c'))).to_proto()", "a {", " b {", " c: \"c\"", " }", "}");
fangismed97d842019-04-12 14:19:46 -0700962 // dict to_proto tests
963 checkTextMessage("struct(name={}).to_proto()"); // empty lines
964 checkTextMessage(
965 "struct(name={'a': 'b'}).to_proto()", "name {", " key: \"a\"", " value: \"b\"", "}");
966 checkTextMessage(
967 "struct(name={'c': 'd', 'a': 'b'}).to_proto()",
968 "name {",
969 " key: \"c\"",
970 " value: \"d\"",
971 "}",
972 "name {",
973 " key: \"a\"",
974 " value: \"b\"",
975 "}");
976 checkTextMessage(
977 "struct(x=struct(y={'a': 1})).to_proto()",
978 "x {",
979 " y {",
980 " key: \"a\"",
981 " value: 1",
982 " }",
983 "}");
984 checkTextMessage(
985 "struct(name={'a': struct(b=1, c=2)}).to_proto()",
986 "name {",
987 " key: \"a\"",
988 " value {",
989 " b: 1",
990 " c: 2",
991 " }",
992 "}");
993 checkTextMessage(
994 "struct(name={'a': struct(b={4: 'z', 3: 'y'}, c=2)}).to_proto()",
995 "name {",
996 " key: \"a\"",
997 " value {",
998 " b {",
999 " key: 4",
1000 " value: \"z\"",
1001 " }",
1002 " b {",
1003 " key: 3",
1004 " value: \"y\"",
1005 " }",
1006 " c: 2",
1007 " }",
1008 "}");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001009 }
1010
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +00001011 @Test
Vladimir Moskva76e31d12016-12-05 16:28:37 +00001012 public void testProtoFieldsOrder() throws Exception {
1013 checkTextMessage("struct(d=4, b=2, c=3, a=1).to_proto()", "a: 1", "b: 2", "c: 3", "d: 4");
1014 }
1015
1016 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001017 public void testTextMessageEscapes() throws Exception {
1018 checkTextMessage("struct(name='a\"b').to_proto()", "name: \"a\\\"b\"");
1019 checkTextMessage("struct(name='a\\'b').to_proto()", "name: \"a'b\"");
1020 checkTextMessage("struct(name='a\\nb').to_proto()", "name: \"a\\nb\"");
Googler4489aaf2016-06-17 15:17:37 +00001021
1022 // struct(name="a\\\"b") -> name: "a\\\"b"
1023 checkTextMessage("struct(name='a\\\\\\\"b').to_proto()", "name: \"a\\\\\\\"b\"");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001024 }
1025
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +00001026 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001027 public void testTextMessageInvalidElementInListStructure() throws Exception {
Googleree056632019-10-10 13:04:49 -07001028 checkEvalErrorContains(
fangismed97d842019-04-12 14:19:46 -07001029 "Invalid text format, expected a struct, a dict, a string, a bool, or "
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001030 + "an int but got a list for list element in struct field 'a'",
1031 "struct(a=[['b']]).to_proto()");
1032 }
1033
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +00001034 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001035 public void testTextMessageInvalidStructure() throws Exception {
Googleree056632019-10-10 13:04:49 -07001036 checkEvalErrorContains(
fangismed97d842019-04-12 14:19:46 -07001037 "Invalid text format, expected a struct, a dict, a string, a bool, or an int "
Vladimir Moskvacd12f772017-01-10 12:47:06 +00001038 + "but got a function for struct field 'a'",
1039 "struct(a=rule).to_proto()");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001040 }
1041
Erik Abair927f4592016-02-29 18:57:22 +00001042 private void checkJson(String from, String expected) throws Exception {
Googleree056632019-10-10 13:04:49 -07001043 Object result = eval(from);
lberkiaea56b32017-05-30 12:35:33 +02001044 assertThat(result).isEqualTo(expected);
Erik Abair927f4592016-02-29 18:57:22 +00001045 }
1046
1047 @Test
1048 public void testJsonBooleanFields() throws Exception {
1049 checkJson("struct(name=True).to_json()", "{\"name\":true}");
1050 checkJson("struct(name=False).to_json()", "{\"name\":false}");
1051 }
1052
1053 @Test
Taras Tsugrii7fe70472018-07-25 13:58:02 -07001054 public void testJsonDictFields() throws Exception {
1055 checkJson("struct(config={}).to_json()", "{\"config\":{}}");
1056 checkJson("struct(config={'key': 'value'}).to_json()", "{\"config\":{\"key\":\"value\"}}");
Googleree056632019-10-10 13:04:49 -07001057 checkEvalErrorContains(
Taras Tsugrii7fe70472018-07-25 13:58:02 -07001058 "Keys must be a string but got a int for struct field 'config'",
1059 "struct(config={1:2}).to_json()");
Googleree056632019-10-10 13:04:49 -07001060 checkEvalErrorContains(
Taras Tsugrii7fe70472018-07-25 13:58:02 -07001061 "Keys must be a string but got a int for dict value 'foo'",
1062 "struct(config={'foo':{1:2}}).to_json()");
Googleree056632019-10-10 13:04:49 -07001063 checkEvalErrorContains(
Taras Tsugrii7fe70472018-07-25 13:58:02 -07001064 "Keys must be a string but got a bool for struct field 'config'",
1065 "struct(config={True: False}).to_json()");
1066 }
1067
1068 @Test
Erik Abair927f4592016-02-29 18:57:22 +00001069 public void testJsonEncoding() throws Exception {
1070 checkJson("struct(name='value').to_json()", "{\"name\":\"value\"}");
1071 checkJson("struct(name=['a', 'b']).to_json()", "{\"name\":[\"a\",\"b\"]}");
1072 checkJson("struct(name=123).to_json()", "{\"name\":123}");
1073 checkJson("struct(name=[1, 2, 3]).to_json()", "{\"name\":[1,2,3]}");
1074 checkJson("struct(a=struct(b='b')).to_json()", "{\"a\":{\"b\":\"b\"}}");
1075 checkJson("struct(a=[struct(b='x'), struct(b='y')]).to_json()",
1076 "{\"a\":[{\"b\":\"x\"},{\"b\":\"y\"}]}");
1077 checkJson("struct(a=struct(b=struct(c='c'))).to_json()",
1078 "{\"a\":{\"b\":{\"c\":\"c\"}}}");
1079 }
1080
1081 @Test
1082 public void testJsonEscapes() throws Exception {
1083 checkJson("struct(name='a\"b').to_json()", "{\"name\":\"a\\\"b\"}");
1084 checkJson("struct(name='a\\'b').to_json()", "{\"name\":\"a'b\"}");
Erik Abairec1f2b92016-03-01 00:45:33 +00001085 checkJson("struct(name='a\\\\b').to_json()", "{\"name\":\"a\\\\b\"}");
Erik Abair927f4592016-02-29 18:57:22 +00001086 checkJson("struct(name='a\\nb').to_json()", "{\"name\":\"a\\nb\"}");
Erik Abairec1f2b92016-03-01 00:45:33 +00001087 checkJson("struct(name='a\\rb').to_json()", "{\"name\":\"a\\rb\"}");
1088 checkJson("struct(name='a\\tb').to_json()", "{\"name\":\"a\\tb\"}");
Erik Abair927f4592016-02-29 18:57:22 +00001089 }
1090
1091 @Test
1092 public void testJsonNestedListStructure() throws Exception {
1093 checkJson("struct(a=[['b']]).to_json()", "{\"a\":[[\"b\"]]}");
1094 }
1095
1096 @Test
1097 public void testJsonInvalidStructure() throws Exception {
Googleree056632019-10-10 13:04:49 -07001098 checkEvalErrorContains(
Erik Abair927f4592016-02-29 18:57:22 +00001099 "Invalid text format, expected a struct, a string, a bool, or an int but got a "
Vladimir Moskvacd12f772017-01-10 12:47:06 +00001100 + "function for struct field 'a'",
1101 "struct(a=rule).to_json()");
Erik Abair927f4592016-02-29 18:57:22 +00001102 }
1103
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +00001104 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001105 public void testLabelAttrWrongDefault() throws Exception {
Googleree056632019-10-10 13:04:49 -07001106 checkEvalErrorContains(
adonovan3f602f22020-01-08 10:28:10 -08001107 "got value of type 'int', want 'Label or string or LateBoundDefault or function or"
1108 + " NoneType'",
vladmos9c787fa2017-07-04 11:45:22 -04001109 "attr.label(default = 123)");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001110 }
1111
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +00001112 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001113 public void testLabelGetRelative() throws Exception {
lberkiaea56b32017-05-30 12:35:33 +02001114 assertThat(eval("Label('//foo:bar').relative('baz')").toString()).isEqualTo("//foo:baz");
1115 assertThat(eval("Label('//foo:bar').relative('//baz:qux')").toString()).isEqualTo("//baz:qux");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001116 }
1117
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +00001118 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001119 public void testLabelGetRelativeSyntaxError() throws Exception {
Googleree056632019-10-10 13:04:49 -07001120 checkEvalErrorContains(
Damien Martin-Guillerez934c1d52017-03-03 14:44:56 +00001121 "invalid target name 'bad//syntax': target names may not contain '//' path separators",
1122 "Label('//foo:bar').relative('bad//syntax')");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001123 }
Greg Estren223976c2016-02-04 22:40:56 +00001124
1125 @Test
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001126 public void testStructCreation() throws Exception {
1127 // TODO(fwe): cannot be handled by current testing suite
Googler1a1fca22019-10-14 09:31:22 -07001128 exec("x = struct(a = 1, b = 2)");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001129 assertThat(lookup("x")).isInstanceOf(ClassObject.class);
1130 }
1131
1132 @Test
1133 public void testStructFields() throws Exception {
1134 // TODO(fwe): cannot be handled by current testing suite
Googler1a1fca22019-10-14 09:31:22 -07001135 exec("x = struct(a = 1, b = 2)");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001136 ClassObject x = (ClassObject) lookup("x");
lberkiaea56b32017-05-30 12:35:33 +02001137 assertThat(x.getValue("a")).isEqualTo(1);
1138 assertThat(x.getValue("b")).isEqualTo(2);
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001139 }
1140
1141 @Test
Vladimir Moskvafdfa9882016-11-18 16:24:20 +00001142 public void testStructEquality() throws Exception {
lberkiaea56b32017-05-30 12:35:33 +02001143 assertThat((Boolean) eval("struct(a = 1, b = 2) == struct(b = 2, a = 1)")).isTrue();
1144 assertThat((Boolean) eval("struct(a = 1) == struct(a = 1, b = 2)")).isFalse();
1145 assertThat((Boolean) eval("struct(a = 1, b = 2) == struct(a = 1)")).isFalse();
Vladimir Moskvafdfa9882016-11-18 16:24:20 +00001146 // Compare a recursive object to itself to make sure reference equality is checked
Googler1a1fca22019-10-14 09:31:22 -07001147 exec("s = struct(a = 1, b = []); s.b.append(s)");
1148 assertThat((Boolean) eval("s == s")).isTrue();
lberkiaea56b32017-05-30 12:35:33 +02001149 assertThat((Boolean) eval("struct(a = 1, b = 2) == struct(a = 1, b = 3)")).isFalse();
1150 assertThat((Boolean) eval("struct(a = 1) == [1]")).isFalse();
1151 assertThat((Boolean) eval("[1] == struct(a = 1)")).isFalse();
1152 assertThat((Boolean) eval("struct() == struct()")).isTrue();
1153 assertThat((Boolean) eval("struct() == struct(a = 1)")).isFalse();
Vladimir Moskvafdfa9882016-11-18 16:24:20 +00001154
Googler1a1fca22019-10-14 09:31:22 -07001155 exec("foo = provider(); bar = provider()");
lberkiaea56b32017-05-30 12:35:33 +02001156 assertThat((Boolean) eval("struct(a = 1) == foo(a = 1)")).isFalse();
1157 assertThat((Boolean) eval("foo(a = 1) == struct(a = 1)")).isFalse();
1158 assertThat((Boolean) eval("foo(a = 1) == bar(a = 1)")).isFalse();
1159 assertThat((Boolean) eval("foo(a = 1) == foo(a = 1)")).isTrue();
Vladimir Moskvafdfa9882016-11-18 16:24:20 +00001160 }
1161
1162 @Test
Vladimir Moskva76e31d12016-12-05 16:28:37 +00001163 public void testStructIncomparability() throws Exception {
Googleree056632019-10-10 13:04:49 -07001164 checkEvalErrorContains("Cannot compare structs", "struct(a = 1) < struct(a = 2)");
1165 checkEvalErrorContains("Cannot compare structs", "struct(a = 1) > struct(a = 2)");
1166 checkEvalErrorContains("Cannot compare structs", "struct(a = 1) <= struct(a = 2)");
1167 checkEvalErrorContains("Cannot compare structs", "struct(a = 1) >= struct(a = 2)");
Vladimir Moskva76e31d12016-12-05 16:28:37 +00001168 }
1169
1170 @Test
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001171 public void testStructAccessingFieldsFromSkylark() throws Exception {
Googler1a1fca22019-10-14 09:31:22 -07001172 exec("x = struct(a = 1, b = 2)", "x1 = x.a", "x2 = x.b");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001173 assertThat(lookup("x1")).isEqualTo(1);
1174 assertThat(lookup("x2")).isEqualTo(2);
1175 }
1176
1177 @Test
1178 public void testStructAccessingUnknownField() throws Exception {
Googleree056632019-10-10 13:04:49 -07001179 checkEvalErrorContains(
adonovan4e2b4952019-12-10 12:19:20 -08001180 "'struct' value has no field or method 'c'\n" + "Available attributes: a, b",
Googleree056632019-10-10 13:04:49 -07001181 "x = struct(a = 1, b = 2)",
1182 "y = x.c");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001183 }
1184
1185 @Test
1186 public void testStructAccessingUnknownFieldWithArgs() throws Exception {
Googleree056632019-10-10 13:04:49 -07001187 checkEvalErrorContains(
adonovan4e2b4952019-12-10 12:19:20 -08001188 "'struct' value has no field or method 'c'", "x = struct(a = 1, b = 2)", "y = x.c()");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001189 }
1190
1191 @Test
1192 public void testStructAccessingNonFunctionFieldWithArgs() throws Exception {
Googleree056632019-10-10 13:04:49 -07001193 checkEvalErrorContains(
1194 "'int' object is not callable", "x = struct(a = 1, b = 2)", "x1 = x.a(1)");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001195 }
1196
1197 @Test
1198 public void testStructAccessingFunctionFieldWithArgs() throws Exception {
Googler1a1fca22019-10-14 09:31:22 -07001199 exec("def f(x): return x+5", "x = struct(a = f, b = 2)", "x1 = x.a(1)");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001200 assertThat(lookup("x1")).isEqualTo(6);
1201 }
1202
1203 @Test
1204 public void testStructPosArgs() throws Exception {
adonovan3f602f22020-01-08 10:28:10 -08001205 checkEvalErrorContains("struct() got unexpected positional argument", "x = struct(1, b = 2)");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001206 }
1207
1208 @Test
1209 public void testStructConcatenationFieldNames() throws Exception {
1210 // TODO(fwe): cannot be handled by current testing suite
Googler1a1fca22019-10-14 09:31:22 -07001211 exec(
1212 "x = struct(a = 1, b = 2)", //
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001213 "y = struct(c = 1, d = 2)",
1214 "z = x + y\n");
cparsons4ebf6c02018-08-17 14:49:36 -07001215 StructImpl z = (StructImpl) lookup("z");
jcater91ed0f32019-11-07 10:07:16 -08001216 assertThat(z.getFieldNames()).containsExactly("a", "b", "c", "d");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001217 }
1218
1219 @Test
1220 public void testStructConcatenationFieldValues() throws Exception {
1221 // TODO(fwe): cannot be handled by current testing suite
Googler1a1fca22019-10-14 09:31:22 -07001222 exec(
1223 "x = struct(a = 1, b = 2)", //
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001224 "y = struct(c = 1, d = 2)",
1225 "z = x + y\n");
cparsons4ebf6c02018-08-17 14:49:36 -07001226 StructImpl z = (StructImpl) lookup("z");
lberkiaea56b32017-05-30 12:35:33 +02001227 assertThat(z.getValue("a")).isEqualTo(1);
1228 assertThat(z.getValue("b")).isEqualTo(2);
1229 assertThat(z.getValue("c")).isEqualTo(1);
1230 assertThat(z.getValue("d")).isEqualTo(2);
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001231 }
1232
1233 @Test
1234 public void testStructConcatenationCommonFields() throws Exception {
Googleree056632019-10-10 13:04:49 -07001235 checkEvalErrorContains(
adonovan553dc6c2019-12-10 11:22:48 -08001236 "cannot add struct instances with common field 'a'",
Googleree056632019-10-10 13:04:49 -07001237 "x = struct(a = 1, b = 2)",
1238 "y = struct(c = 1, a = 2)",
1239 "z = x + y\n");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001240 }
1241
1242 @Test
1243 public void testConditionalStructConcatenation() throws Exception {
1244 // TODO(fwe): cannot be handled by current testing suite
Googler1a1fca22019-10-14 09:31:22 -07001245 exec(
1246 "def func():",
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001247 " x = struct(a = 1, b = 2)",
1248 " if True:",
1249 " x += struct(c = 1, d = 2)",
1250 " return x",
1251 "x = func()");
cparsons4ebf6c02018-08-17 14:49:36 -07001252 StructImpl x = (StructImpl) lookup("x");
lberkiaea56b32017-05-30 12:35:33 +02001253 assertThat(x.getValue("a")).isEqualTo(1);
1254 assertThat(x.getValue("b")).isEqualTo(2);
1255 assertThat(x.getValue("c")).isEqualTo(1);
1256 assertThat(x.getValue("d")).isEqualTo(2);
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001257 }
1258
1259 @Test
1260 public void testGetattrNoAttr() throws Exception {
Googleree056632019-10-10 13:04:49 -07001261 checkEvalErrorContains(
adonovan4e2b4952019-12-10 12:19:20 -08001262 "'struct' value has no field or method 'b'\nAvailable attributes: a",
Googler055d6c62018-05-22 13:21:04 -07001263 "s = struct(a='val')",
1264 "getattr(s, 'b')");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001265 }
1266
1267 @Test
1268 public void testGetattr() throws Exception {
Googler1a1fca22019-10-14 09:31:22 -07001269 exec("s = struct(a='val')", "x = getattr(s, 'a')", "y = getattr(s, 'b', 'def')");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001270 assertThat(lookup("x")).isEqualTo("val");
1271 assertThat(lookup("y")).isEqualTo("def");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001272 }
1273
1274 @Test
1275 public void testHasattr() throws Exception {
Googler1a1fca22019-10-14 09:31:22 -07001276 exec(
1277 "s = struct(a=1)", //
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001278 "x = hasattr(s, 'a')",
1279 "y = hasattr(s, 'b')\n");
1280 assertThat(lookup("x")).isEqualTo(true);
1281 assertThat(lookup("y")).isEqualTo(false);
1282 }
1283
1284 @Test
1285 public void testStructStr() throws Exception {
1286 assertThat(eval("str(struct(x = 2, y = 3, z = 4))"))
1287 .isEqualTo("struct(x = 2, y = 3, z = 4)");
1288 }
1289
1290 @Test
1291 public void testStructsInSets() throws Exception {
Googler1a1fca22019-10-14 09:31:22 -07001292 exec("depset([struct(a='a')])");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001293 }
1294
1295 @Test
Vladimir Moskva76e31d12016-12-05 16:28:37 +00001296 public void testStructsInDicts() throws Exception {
Googler1a1fca22019-10-14 09:31:22 -07001297 exec("d = {struct(a = 1): 'aa', struct(b = 2): 'bb'}");
Vladimir Moskva76e31d12016-12-05 16:28:37 +00001298 assertThat(eval("d[struct(a = 1)]")).isEqualTo("aa");
1299 assertThat(eval("d[struct(b = 2)]")).isEqualTo("bb");
1300 assertThat(eval("str([d[k] for k in d])")).isEqualTo("[\"aa\", \"bb\"]");
1301
Googleree056632019-10-10 13:04:49 -07001302 checkEvalErrorContains("unhashable type: 'struct'", "{struct(a = []): 'foo'}");
Vladimir Moskva76e31d12016-12-05 16:28:37 +00001303 }
1304
1305 @Test
Vladimir Moskva10770382016-08-23 15:04:54 +00001306 public void testStructDictMembersAreMutable() throws Exception {
Googler1a1fca22019-10-14 09:31:22 -07001307 exec(
1308 "s = struct(x = {'a' : 1})", //
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001309 "s.x['b'] = 2\n");
cparsons4ebf6c02018-08-17 14:49:36 -07001310 assertThat(((StructImpl) lookup("s")).getValue("x")).isEqualTo(ImmutableMap.of("a", 1, "b", 2));
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001311 }
1312
1313 @Test
1314 public void testNsetGoodCompositeItem() throws Exception {
Googler1a1fca22019-10-14 09:31:22 -07001315 exec("def func():", " return depset([struct(a='a')])", "s = func()");
Googlerd21a0d12019-11-21 13:52:30 -08001316 Collection<?> result = ((Depset) lookup("s")).toCollection();
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001317 assertThat(result).hasSize(1);
cparsons4ebf6c02018-08-17 14:49:36 -07001318 assertThat(result.iterator().next()).isInstanceOf(StructImpl.class);
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001319 }
1320
cparsons4ebf6c02018-08-17 14:49:36 -07001321 private static StructImpl makeStruct(String field, Object value) {
cparsons0c5c1c62018-05-24 10:37:03 -07001322 return StructProvider.STRUCT.create(ImmutableMap.of(field, value), "no field '%'");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001323 }
1324
Googler92578702019-11-21 12:19:31 -08001325 private static StructImpl makeBigStruct(@Nullable Mutability mu) {
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001326 // struct(a=[struct(x={1:1}), ()], b=(), c={2:2})
cparsons0c5c1c62018-05-24 10:37:03 -07001327 return StructProvider.STRUCT.create(
Dmitry Lomovea9de072016-08-09 09:35:40 +00001328 ImmutableMap.<String, Object>of(
dslomovde965ac2017-07-31 21:07:51 +02001329 "a",
Googler942e1c42019-11-12 13:11:44 -08001330 StarlarkList.<Object>of(
Googler92578702019-11-21 12:19:31 -08001331 mu,
cparsons0c5c1c62018-05-24 10:37:03 -07001332 StructProvider.STRUCT.create(
Googler92578702019-11-21 12:19:31 -08001333 ImmutableMap.<String, Object>of("x", Dict.<Object, Object>of(mu, 1, 1)),
dslomovde965ac2017-07-31 21:07:51 +02001334 "no field '%s'"),
1335 Tuple.of()),
Dmitry Lomovea9de072016-08-09 09:35:40 +00001336 "b", Tuple.of(),
Googler92578702019-11-21 12:19:31 -08001337 "c", Dict.<Object, Object>of(mu, 2, 2)),
Dmitry Lomovea9de072016-08-09 09:35:40 +00001338 "no field '%s'");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001339 }
1340
1341 @Test
1342 public void testStructMutabilityShallow() throws Exception {
lberkiaea56b32017-05-30 12:35:33 +02001343 assertThat(EvalUtils.isImmutable(makeStruct("a", 1))).isTrue();
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001344 }
1345
Googler92578702019-11-21 12:19:31 -08001346 private static StarlarkList<Object> makeList(@Nullable Mutability mu) {
1347 return StarlarkList.<Object>of(mu, 1, 2, 3);
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001348 }
1349
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001350 @Test
1351 public void testStructMutabilityDeep() throws Exception {
lberkiaea56b32017-05-30 12:35:33 +02001352 assertThat(EvalUtils.isImmutable(Tuple.<Object>of(makeList(null)))).isTrue();
1353 assertThat(EvalUtils.isImmutable(makeStruct("a", makeList(null)))).isTrue();
1354 assertThat(EvalUtils.isImmutable(makeBigStruct(null))).isTrue();
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001355
Googler92578702019-11-21 12:19:31 -08001356 Mutability mu = ev.getStarlarkThread().mutability();
1357 assertThat(EvalUtils.isImmutable(Tuple.<Object>of(makeList(mu)))).isFalse();
1358 assertThat(EvalUtils.isImmutable(makeStruct("a", makeList(mu)))).isFalse();
1359 assertThat(EvalUtils.isImmutable(makeBigStruct(mu))).isFalse();
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001360 }
1361
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001362 @Test
1363 public void declaredProviders() throws Exception {
1364 evalAndExport(
1365 "data = provider()",
1366 "d = data(x = 1, y ='abc')",
1367 "d_x = d.x",
1368 "d_y = d.y"
1369 );
1370 assertThat(lookup("d_x")).isEqualTo(1);
1371 assertThat(lookup("d_y")).isEqualTo("abc");
gregceb6eafee2020-04-20 08:04:51 -07001372 StarlarkProvider dataConstructor = (StarlarkProvider) lookup("data");
cparsons4ebf6c02018-08-17 14:49:36 -07001373 StructImpl data = (StructImpl) lookup("d");
dslomovde965ac2017-07-31 21:07:51 +02001374 assertThat(data.getProvider()).isEqualTo(dataConstructor);
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001375 assertThat(dataConstructor.isExported()).isTrue();
1376 assertThat(dataConstructor.getPrintableName()).isEqualTo("data");
gregceb6eafee2020-04-20 08:04:51 -07001377 assertThat(dataConstructor.getKey()).isEqualTo(new StarlarkProvider.Key(FAKE_LABEL, "data"));
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001378 }
1379
1380 @Test
1381 public void declaredProvidersConcatSuccess() throws Exception {
1382 evalAndExport(
1383 "data = provider()",
1384 "dx = data(x = 1)",
1385 "dy = data(y = 'abc')",
1386 "dxy = dx + dy",
1387 "x = dxy.x",
1388 "y = dxy.y"
1389 );
1390 assertThat(lookup("x")).isEqualTo(1);
1391 assertThat(lookup("y")).isEqualTo("abc");
gregceb6eafee2020-04-20 08:04:51 -07001392 StarlarkProvider dataConstructor = (StarlarkProvider) lookup("data");
cparsons4ebf6c02018-08-17 14:49:36 -07001393 StructImpl dx = (StructImpl) lookup("dx");
dslomovde965ac2017-07-31 21:07:51 +02001394 assertThat(dx.getProvider()).isEqualTo(dataConstructor);
cparsons4ebf6c02018-08-17 14:49:36 -07001395 StructImpl dy = (StructImpl) lookup("dy");
dslomovde965ac2017-07-31 21:07:51 +02001396 assertThat(dy.getProvider()).isEqualTo(dataConstructor);
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001397 }
1398
1399 @Test
1400 public void declaredProvidersConcatError() throws Exception {
1401 evalAndExport(
1402 "data1 = provider()",
1403 "data2 = provider()"
1404 );
1405
Googleree056632019-10-10 13:04:49 -07001406 checkEvalErrorContains(
1407 "Cannot use '+' operator on instances of different providers (data1 and data2)",
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001408 "d1 = data1(x = 1)",
1409 "d2 = data2(y = 2)",
Googleree056632019-10-10 13:04:49 -07001410 "d = d1 + d2");
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001411 }
Dmitry Lomov94b2c882016-09-25 20:24:24 +00001412
1413 @Test
Benjamin Peterson8610d972017-11-27 03:14:43 -08001414 public void declaredProvidersWithFieldsConcatSuccess() throws Exception {
1415 evalAndExport(
1416 "data = provider(fields=['f1', 'f2'])",
1417 "d1 = data(f1 = 4)",
1418 "d2 = data(f2 = 5)",
1419 "d3 = d1 + d2",
1420 "f1 = d3.f1",
1421 "f2 = d3.f2");
1422 assertThat(lookup("f1")).isEqualTo(4);
1423 assertThat(lookup("f2")).isEqualTo(5);
1424 }
1425
1426 @Test
1427 public void declaredProvidersWithFieldsConcatError() throws Exception {
1428 evalAndExport("data1 = provider(fields=['f1', 'f2'])", "data2 = provider(fields=['f3'])");
Googleree056632019-10-10 13:04:49 -07001429 checkEvalErrorContains(
brandjona29648a2018-01-10 08:38:43 -08001430 "Cannot use '+' operator on instances of different providers (data1 and data2)",
Benjamin Peterson8610d972017-11-27 03:14:43 -08001431 "d1 = data1(f1=1, f2=2)",
1432 "d2 = data2(f3=3)",
1433 "d = d1 + d2");
1434 }
1435
1436 @Test
1437 public void declaredProvidersWithOverlappingFieldsConcatError() throws Exception {
1438 evalAndExport("data = provider(fields=['f1', 'f2'])");
Googleree056632019-10-10 13:04:49 -07001439 checkEvalErrorContains(
adonovan553dc6c2019-12-10 11:22:48 -08001440 "cannot add struct instances with common field 'f1'",
Benjamin Peterson8610d972017-11-27 03:14:43 -08001441 "d1 = data(f1 = 4)",
1442 "d2 = data(f1 = 5)",
1443 "d1 + d2");
1444 }
1445
1446 @Test
Dmitry Lomov94b2c882016-09-25 20:24:24 +00001447 public void structsAsDeclaredProvidersTest() throws Exception {
1448 evalAndExport(
1449 "data = struct(x = 1)"
1450 );
cparsons4ebf6c02018-08-17 14:49:36 -07001451 StructImpl data = (StructImpl) lookup("data");
cparsons0c5c1c62018-05-24 10:37:03 -07001452 assertThat(StructProvider.STRUCT.isExported()).isTrue();
1453 assertThat(data.getProvider()).isEqualTo(StructProvider.STRUCT);
1454 assertThat(data.getProvider().getKey()).isEqualTo(StructProvider.STRUCT.getKey());
Dmitry Lomov94b2c882016-09-25 20:24:24 +00001455 }
Dmitry Lomov0692c7f2016-09-30 16:43:30 +00001456
1457 @Test
allevato45b79e52017-07-07 21:40:50 +02001458 public void declaredProvidersDoc() throws Exception {
1459 evalAndExport("data1 = provider(doc='foo')");
1460 }
1461
1462 @Test
1463 public void declaredProvidersBadTypeForDoc() throws Exception {
adonovan3f602f22020-01-08 10:28:10 -08001464 checkEvalErrorContains("got value of type 'int', want 'string'", "provider(doc = 1)");
allevato45b79e52017-07-07 21:40:50 +02001465 }
1466
1467 @Test
Dmitry Lomov0692c7f2016-09-30 16:43:30 +00001468 public void aspectAllAttrs() throws Exception {
1469 evalAndExport(
1470 "def _impl(target, ctx):",
1471 " pass",
1472 "my_aspect = aspect(_impl, attr_aspects=['*'])");
1473
cparsons0d55f4c2017-12-20 14:49:13 -08001474 SkylarkDefinedAspect myAspect = (SkylarkDefinedAspect) lookup("my_aspect");
lberki3bd28392019-02-05 01:01:02 -08001475 assertThat(myAspect.getDefinition(AspectParameters.EMPTY).propagateAlong("foo")).isTrue();
Dmitry Lomov0692c7f2016-09-30 16:43:30 +00001476 }
1477
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001478 @Test
1479 public void aspectRequiredAspectProvidersSingle() throws Exception {
1480 evalAndExport(
1481 "def _impl(target, ctx):",
1482 " pass",
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001483 "cc = provider()",
1484 "my_aspect = aspect(_impl, required_aspect_providers=['java', cc])"
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001485 );
cparsons0d55f4c2017-12-20 14:49:13 -08001486 SkylarkDefinedAspect myAspect = (SkylarkDefinedAspect) lookup("my_aspect");
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001487 RequiredProviders requiredProviders = myAspect.getDefinition(AspectParameters.EMPTY)
1488 .getRequiredProvidersForAspects();
1489 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.ANY)).isTrue();
1490 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.EMPTY)).isFalse();
1491 assertThat(requiredProviders.isSatisfiedBy(
1492 AdvertisedProviderSet.builder()
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001493 .addSkylark(declared("cc"))
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001494 .addSkylark("java")
1495 .build()))
1496 .isTrue();
1497 assertThat(requiredProviders.isSatisfiedBy(
1498 AdvertisedProviderSet.builder()
1499 .addSkylark("cc")
1500 .build()))
1501 .isFalse();
1502 }
1503
1504 @Test
1505 public void aspectRequiredAspectProvidersAlternatives() throws Exception {
1506 evalAndExport(
1507 "def _impl(target, ctx):",
1508 " pass",
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001509 "cc = provider()",
1510 "my_aspect = aspect(_impl, required_aspect_providers=[['java'], [cc]])"
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001511 );
cparsons0d55f4c2017-12-20 14:49:13 -08001512 SkylarkDefinedAspect myAspect = (SkylarkDefinedAspect) lookup("my_aspect");
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001513 RequiredProviders requiredProviders = myAspect.getDefinition(AspectParameters.EMPTY)
1514 .getRequiredProvidersForAspects();
1515 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.ANY)).isTrue();
1516 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.EMPTY)).isFalse();
1517 assertThat(requiredProviders.isSatisfiedBy(
1518 AdvertisedProviderSet.builder()
1519 .addSkylark("java")
1520 .build()))
1521 .isTrue();
1522 assertThat(requiredProviders.isSatisfiedBy(
1523 AdvertisedProviderSet.builder()
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001524 .addSkylark(declared("cc"))
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001525 .build()))
1526 .isTrue();
1527 assertThat(requiredProviders.isSatisfiedBy(
1528 AdvertisedProviderSet.builder()
1529 .addSkylark("prolog")
1530 .build()))
1531 .isFalse();
1532 }
1533
1534 @Test
1535 public void aspectRequiredAspectProvidersEmpty() throws Exception {
1536 evalAndExport(
1537 "def _impl(target, ctx):",
1538 " pass",
1539 "my_aspect = aspect(_impl, required_aspect_providers=[])"
1540 );
cparsons0d55f4c2017-12-20 14:49:13 -08001541 SkylarkDefinedAspect myAspect = (SkylarkDefinedAspect) lookup("my_aspect");
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001542 RequiredProviders requiredProviders = myAspect.getDefinition(AspectParameters.EMPTY)
1543 .getRequiredProvidersForAspects();
1544 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.ANY)).isFalse();
1545 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.EMPTY)).isFalse();
1546 }
1547
1548 @Test
1549 public void aspectRequiredAspectProvidersDefault() throws Exception {
1550 evalAndExport(
1551 "def _impl(target, ctx):",
1552 " pass",
1553 "my_aspect = aspect(_impl)"
1554 );
cparsons0d55f4c2017-12-20 14:49:13 -08001555 SkylarkDefinedAspect myAspect = (SkylarkDefinedAspect) lookup("my_aspect");
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001556 RequiredProviders requiredProviders = myAspect.getDefinition(AspectParameters.EMPTY)
1557 .getRequiredProvidersForAspects();
1558 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.ANY)).isFalse();
1559 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.EMPTY)).isFalse();
1560 }
1561
Dmitry Lomov950310f2017-03-01 17:45:12 +00001562 @Test
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001563 public void aspectProvides() throws Exception {
1564 evalAndExport(
1565 "def _impl(target, ctx):",
1566 " pass",
1567 "y = provider()",
1568 "my_aspect = aspect(_impl, provides = ['x', y])"
1569 );
cparsons0d55f4c2017-12-20 14:49:13 -08001570 SkylarkDefinedAspect myAspect = (SkylarkDefinedAspect) lookup("my_aspect");
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001571 AdvertisedProviderSet advertisedProviders = myAspect.getDefinition(AspectParameters.EMPTY)
1572 .getAdvertisedProviders();
1573 assertThat(advertisedProviders.canHaveAnyProvider()).isFalse();
1574 assertThat(advertisedProviders.getSkylarkProviders())
1575 .containsExactly(legacy("x"), declared("y"));
1576 }
1577
1578 @Test
1579 public void aspectProvidesError() throws Exception {
1580 ev.setFailFast(false);
1581 evalAndExport(
1582 "def _impl(target, ctx):",
1583 " pass",
1584 "y = provider()",
1585 "my_aspect = aspect(_impl, provides = ['x', 1])"
1586 );
1587 MoreAsserts.assertContainsEvent(ev.getEventCollector(),
1588 " Illegal argument: element in 'provides' is of unexpected type."
cparsonsaab98682018-04-27 13:04:59 -07001589 + " Should be list of providers, but got item of type int. ");
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001590 }
1591
allevato45b79e52017-07-07 21:40:50 +02001592 @Test
1593 public void aspectDoc() throws Exception {
1594 evalAndExport(
1595 "def _impl(target, ctx):",
1596 " pass",
1597 "my_aspect = aspect(_impl, doc='foo')");
1598 }
1599
1600 @Test
1601 public void aspectBadTypeForDoc() throws Exception {
Googler3fcfbe12019-08-28 08:10:11 -07001602 registerDummyStarlarkFunction();
adonovan3f602f22020-01-08 10:28:10 -08001603 checkEvalErrorContains("got value of type 'int', want 'string'", "aspect(impl, doc = 1)");
allevato45b79e52017-07-07 21:40:50 +02001604 }
1605
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001606 @Test
Dmitry Lomov950310f2017-03-01 17:45:12 +00001607 public void fancyExports() throws Exception {
1608 evalAndExport(
1609 "def _impla(target, ctx): pass",
1610 "p, (a, p1) = [",
1611 " provider(),",
1612 " [ aspect(_impla),",
1613 " provider() ]",
1614 "]"
1615 );
gregceb6eafee2020-04-20 08:04:51 -07001616 StarlarkProvider p = (StarlarkProvider) lookup("p");
cparsons0d55f4c2017-12-20 14:49:13 -08001617 SkylarkDefinedAspect a = (SkylarkDefinedAspect) lookup("a");
gregceb6eafee2020-04-20 08:04:51 -07001618 StarlarkProvider p1 = (StarlarkProvider) lookup("p1");
Dmitry Lomov950310f2017-03-01 17:45:12 +00001619 assertThat(p.getPrintableName()).isEqualTo("p");
gregceb6eafee2020-04-20 08:04:51 -07001620 assertThat(p.getKey()).isEqualTo(new StarlarkProvider.Key(FAKE_LABEL, "p"));
Dmitry Lomov950310f2017-03-01 17:45:12 +00001621 assertThat(p1.getPrintableName()).isEqualTo("p1");
gregceb6eafee2020-04-20 08:04:51 -07001622 assertThat(p1.getKey()).isEqualTo(new StarlarkProvider.Key(FAKE_LABEL, "p1"));
Dmitry Lomov950310f2017-03-01 17:45:12 +00001623 assertThat(a.getAspectClass()).isEqualTo(
1624 new SkylarkAspectClass(FAKE_LABEL, "a")
1625 );
1626 }
1627
1628 @Test
1629 public void multipleTopLevels() throws Exception {
1630 evalAndExport(
1631 "p = provider()",
1632 "p1 = p"
1633 );
gregceb6eafee2020-04-20 08:04:51 -07001634 StarlarkProvider p = (StarlarkProvider) lookup("p");
1635 StarlarkProvider p1 = (StarlarkProvider) lookup("p1");
Dmitry Lomov950310f2017-03-01 17:45:12 +00001636 assertThat(p).isEqualTo(p1);
gregceb6eafee2020-04-20 08:04:51 -07001637 assertThat(p.getKey()).isEqualTo(new StarlarkProvider.Key(FAKE_LABEL, "p"));
1638 assertThat(p1.getKey()).isEqualTo(new StarlarkProvider.Key(FAKE_LABEL, "p"));
Dmitry Lomov950310f2017-03-01 17:45:12 +00001639 }
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001640
dslomov0667b832017-08-25 09:29:50 +02001641 @Test
1642 public void providerWithFields() throws Exception {
1643 evalAndExport(
1644 "p = provider(fields = ['x', 'y'])",
1645 "p1 = p(x = 1, y = 2)",
1646 "x = p1.x",
1647 "y = p1.y"
1648 );
gregceb6eafee2020-04-20 08:04:51 -07001649 StarlarkProvider p = (StarlarkProvider) lookup("p");
dslomov0667b832017-08-25 09:29:50 +02001650 SkylarkInfo p1 = (SkylarkInfo) lookup("p1");
1651
dslomov0667b832017-08-25 09:29:50 +02001652 assertThat(p1.getProvider()).isEqualTo(p);
1653 assertThat(lookup("x")).isEqualTo(1);
1654 assertThat(lookup("y")).isEqualTo(2);
1655 }
1656
1657 @Test
1658 public void providerWithFieldsDict() throws Exception {
1659 evalAndExport(
1660 "p = provider(fields = { 'x' : 'I am x', 'y' : 'I am y'})",
1661 "p1 = p(x = 1, y = 2)",
1662 "x = p1.x",
1663 "y = p1.y"
1664 );
gregceb6eafee2020-04-20 08:04:51 -07001665 StarlarkProvider p = (StarlarkProvider) lookup("p");
dslomov0667b832017-08-25 09:29:50 +02001666 SkylarkInfo p1 = (SkylarkInfo) lookup("p1");
1667
dslomov0667b832017-08-25 09:29:50 +02001668 assertThat(p1.getProvider()).isEqualTo(p);
1669 assertThat(lookup("x")).isEqualTo(1);
1670 assertThat(lookup("y")).isEqualTo(2);
1671 }
1672
1673 @Test
1674 public void providerWithFieldsOptional() throws Exception {
1675 evalAndExport(
1676 "p = provider(fields = ['x', 'y'])",
1677 "p1 = p(y = 2)",
1678 "y = p1.y"
1679 );
gregceb6eafee2020-04-20 08:04:51 -07001680 StarlarkProvider p = (StarlarkProvider) lookup("p");
dslomov0667b832017-08-25 09:29:50 +02001681 SkylarkInfo p1 = (SkylarkInfo) lookup("p1");
1682
dslomov0667b832017-08-25 09:29:50 +02001683 assertThat(p1.getProvider()).isEqualTo(p);
1684 assertThat(lookup("y")).isEqualTo(2);
1685 }
1686
1687 @Test
1688 public void providerWithFieldsOptionalError() throws Exception {
1689 ev.setFailFast(false);
1690 evalAndExport(
1691 "p = provider(fields = ['x', 'y'])",
1692 "p1 = p(y = 2)",
1693 "x = p1.x"
1694 );
adonovan4e2b4952019-12-10 12:19:20 -08001695 MoreAsserts.assertContainsEvent(
1696 ev.getEventCollector(), " 'p' value has no field or method 'x'");
dslomov0667b832017-08-25 09:29:50 +02001697 }
1698
1699 @Test
1700 public void providerWithExtraFieldsError() throws Exception {
1701 ev.setFailFast(false);
1702 evalAndExport(
1703 "p = provider(fields = ['x', 'y'])",
1704 "p1 = p(x = 1, y = 2, z = 3)"
1705 );
adonovan7f266402020-04-20 10:15:47 -07001706 MoreAsserts.assertContainsEvent(
1707 ev.getEventCollector(), "unexpected keyword z in call to instantiate provider p");
dslomov0667b832017-08-25 09:29:50 +02001708 }
1709
1710 @Test
1711 public void providerWithEmptyFieldsError() throws Exception {
1712 ev.setFailFast(false);
1713 evalAndExport(
1714 "p = provider(fields = [])",
1715 "p1 = p(x = 1, y = 2, z = 3)"
1716 );
adonovan7f266402020-04-20 10:15:47 -07001717 MoreAsserts.assertContainsEvent(
1718 ev.getEventCollector(), "unexpected keywords x, y, z in call to instantiate provider p");
1719 }
1720
1721 @Test
1722 public void providerWithDuplicateFieldsError() throws Exception {
1723 ev.setFailFast(false);
1724 evalAndExport("p = provider(fields = ['a', 'b'])", "p(a = 1, b = 2, b = 3)");
1725 MoreAsserts.assertContainsEvent(
1726 ev.getEventCollector(),
1727 "got multiple values for parameter b in call to instantiate provider p");
dslomov0667b832017-08-25 09:29:50 +02001728 }
1729
Dmitry Lomov0692c7f2016-09-30 16:43:30 +00001730 @Test
1731 public void starTheOnlyAspectArg() throws Exception {
Googleree056632019-10-10 13:04:49 -07001732 checkEvalErrorContains(
1733 "'*' must be the only string in 'attr_aspects' list",
Dmitry Lomov0692c7f2016-09-30 16:43:30 +00001734 "def _impl(target, ctx):",
1735 " pass",
1736 "aspect(_impl, attr_aspects=['*', 'foo'])");
1737 }
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +00001738
1739 @Test
1740 public void testMandatoryConfigParameterForExecutableLabels() throws Exception {
1741 scratch.file("third_party/foo/extension.bzl",
1742 "def _main_rule_impl(ctx):",
1743 " pass",
1744 "my_rule = rule(_main_rule_impl,",
1745 " attrs = { ",
1746 " 'exe' : attr.label(executable = True, allow_files = True),",
1747 " },",
1748 ")"
1749 );
1750 scratch.file("third_party/foo/BUILD",
laurentlb5ddd8042017-11-30 12:03:31 -08001751 "load(':extension.bzl', 'my_rule')",
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +00001752 "my_rule(name = 'main', exe = ':tool.sh')"
1753 );
1754
cushon978cb002018-02-24 14:05:37 -08001755 AssertionError expected =
1756 assertThrows(AssertionError.class, () -> createRuleContext("//third_party/foo:main"));
brandjon8bd20162017-12-28 08:49:54 -08001757 assertThat(expected).hasMessageThat()
1758 .contains("cfg parameter is mandatory when executable=True is provided.");
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +00001759 }
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +00001760
John Catereca28402017-05-17 21:44:12 +02001761 @Test
1762 public void testRuleAddToolchain() throws Exception {
John Cater9a8d16e2017-07-05 16:12:07 -04001763 scratch.file("test/BUILD", "toolchain_type(name = 'my_toolchain_type')");
John Catereca28402017-05-17 21:44:12 +02001764 evalAndExport(
John Cater9a8d16e2017-07-05 16:12:07 -04001765 "def impl(ctx): return None", "r1 = rule(impl, toolchains=['//test:my_toolchain_type'])");
tomlu72642a22017-10-18 06:23:14 +02001766 RuleClass c = ((SkylarkRuleFunction) lookup("r1")).getRuleClass();
John Cater9a8d16e2017-07-05 16:12:07 -04001767 assertThat(c.getRequiredToolchains()).containsExactly(makeLabel("//test:my_toolchain_type"));
John Catereca28402017-05-17 21:44:12 +02001768 }
vladmos2f32e382017-06-19 12:44:21 -04001769
1770 @Test
John Caterd79e9772018-06-14 02:24:57 -07001771 public void testRuleAddExecutionConstraints() throws Exception {
Googler3fcfbe12019-08-28 08:10:11 -07001772 registerDummyStarlarkFunction();
John Caterd79e9772018-06-14 02:24:57 -07001773 scratch.file("test/BUILD", "toolchain_type(name = 'my_toolchain_type')");
1774 evalAndExport(
1775 "r1 = rule(",
1776 " implementation = impl,",
1777 " toolchains=['//test:my_toolchain_type'],",
1778 " exec_compatible_with=['//constraint:cv1', '//constraint:cv2'],",
1779 ")");
1780 RuleClass c = ((SkylarkRuleFunction) lookup("r1")).getRuleClass();
1781 assertThat(c.getExecutionPlatformConstraints())
1782 .containsExactly(makeLabel("//constraint:cv1"), makeLabel("//constraint:cv2"));
1783 }
1784
1785 @Test
juliexxia693048d2020-04-01 06:41:42 -07001786 public void testRuleAddExecGroup() throws Exception {
1787 setSkylarkSemanticsOptions("--experimental_exec_groups=true");
1788 reset();
1789
1790 registerDummyStarlarkFunction();
1791 scratch.file("test/BUILD", "toolchain_type(name = 'my_toolchain_type')");
1792 evalAndExport(
1793 "plum = rule(",
1794 " implementation = impl,",
1795 " exec_groups = {",
1796 " 'group': exec_group(",
1797 " toolchains=['//test:my_toolchain_type'],",
1798 " exec_compatible_with=['//constraint:cv1', '//constraint:cv2'],",
1799 " ),",
1800 " },",
1801 ")");
1802 RuleClass plum = ((SkylarkRuleFunction) lookup("plum")).getRuleClass();
1803 assertThat(plum.getRequiredToolchains()).isEmpty();
1804 assertThat(plum.getExecGroups().get("group").getRequiredToolchains())
1805 .containsExactly(makeLabel("//test:my_toolchain_type"));
1806 assertThat(plum.getExecutionPlatformConstraints()).isEmpty();
1807 assertThat(plum.getExecGroups().get("group").getExecutionPlatformConstraints())
1808 .containsExactly(makeLabel("//constraint:cv1"), makeLabel("//constraint:cv2"));
1809 }
1810
1811 @Test
vladmos2f32e382017-06-19 12:44:21 -04001812 public void testRuleFunctionReturnsNone() throws Exception {
1813 scratch.file("test/rule.bzl",
1814 "def _impl(ctx):",
1815 " pass",
1816 "foo_rule = rule(",
1817 " implementation = _impl,",
1818 " attrs = {'params': attr.string_list()},",
1819 ")");
1820 scratch.file("test/BUILD",
1821 "load(':rule.bzl', 'foo_rule')",
1822 "r = foo_rule(name='foo')", // Custom rule should return None
1823 "c = cc_library(name='cc')", // Native rule should return None
1824 "",
1825 "foo_rule(",
1826 " name='check',",
1827 " params = [type(r), type(c)]",
1828 ")");
1829 invalidatePackages();
1830 SkylarkRuleContext context = createRuleContext("//test:check");
1831 @SuppressWarnings("unchecked")
Googler942e1c42019-11-12 13:11:44 -08001832 StarlarkList<Object> params = (StarlarkList<Object>) context.getAttr().getValue("params");
vladmos2f32e382017-06-19 12:44:21 -04001833 assertThat(params.get(0)).isEqualTo("NoneType");
1834 assertThat(params.get(1)).isEqualTo("NoneType");
1835 }
cparsons0c5c1c62018-05-24 10:37:03 -07001836
1837 @Test
1838 public void testTypeOfStruct() throws Exception {
Googler1a1fca22019-10-14 09:31:22 -07001839 exec("p = type(struct)", "s = type(struct())");
cparsons0c5c1c62018-05-24 10:37:03 -07001840
1841 assertThat(lookup("p")).isEqualTo("Provider");
1842 assertThat(lookup("s")).isEqualTo("struct");
1843 }
juliexxia44e21432020-03-31 08:21:20 -07001844
1845 @Test
1846 public void testCreateExecGroup() throws Exception {
1847 setSkylarkSemanticsOptions("--experimental_exec_groups=true");
1848 reset();
1849
1850 scratch.file("test/BUILD", "toolchain_type(name = 'my_toolchain_type')");
1851 evalAndExport(
1852 "group = exec_group(",
1853 " toolchains=['//test:my_toolchain_type'],",
1854 " exec_compatible_with=['//constraint:cv1', '//constraint:cv2'],",
1855 ")");
1856 ExecGroup group = ((ExecGroup) lookup("group"));
1857 assertThat(group.getRequiredToolchains())
1858 .containsExactly(makeLabel("//test:my_toolchain_type"));
1859 assertThat(group.getExecutionPlatformConstraints())
1860 .containsExactly(makeLabel("//constraint:cv1"), makeLabel("//constraint:cv2"));
1861 }
John Catereca28402017-05-17 21:44:12 +02001862}