blob: dadd537a5e7b81d11dfaa1a0429e619e34a1c8f3 [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;
gregceeefc91c2020-06-19 13:33:43 -070026import com.google.devtools.build.lib.analysis.starlark.StarlarkAttrModule;
27import com.google.devtools.build.lib.analysis.starlark.StarlarkRuleClassFunctions.StarlarkRuleFunction;
28import com.google.devtools.build.lib.analysis.starlark.StarlarkRuleContext;
adonovan4444f1a2020-05-12 20:03:11 -070029import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000030import com.google.devtools.build.lib.cmdline.Label;
adonovanbc74d1d2020-04-30 12:08:31 -070031import com.google.devtools.build.lib.collect.nestedset.Depset;
michajlob839a512020-03-11 10:04:23 -070032import com.google.devtools.build.lib.events.Event;
33import com.google.devtools.build.lib.events.EventKind;
Dmitry Lomovf868b3e2017-01-17 10:25:28 +000034import com.google.devtools.build.lib.packages.AdvertisedProviderSet;
Dmitry Lomov0692c7f2016-09-30 16:43:30 +000035import com.google.devtools.build.lib.packages.AspectParameters;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000036import com.google.devtools.build.lib.packages.Attribute;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000037import com.google.devtools.build.lib.packages.BuildType;
juliexxia44e21432020-03-31 08:21:20 -070038import com.google.devtools.build.lib.packages.ExecGroup;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000039import com.google.devtools.build.lib.packages.ImplicitOutputsFunction;
40import com.google.devtools.build.lib.packages.PredicateWithMessage;
Dmitry Lomovf868b3e2017-01-17 10:25:28 +000041import com.google.devtools.build.lib.packages.RequiredProviders;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000042import com.google.devtools.build.lib.packages.RuleClass;
43import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
gregce18694cd2020-05-12 15:40:05 -070044import com.google.devtools.build.lib.packages.StarlarkAspectClass;
gregced281df72020-05-11 12:27:06 -070045import com.google.devtools.build.lib.packages.StarlarkDefinedAspect;
gregced7c1cef2020-05-12 07:51:48 -070046import com.google.devtools.build.lib.packages.StarlarkInfo;
gregceb6eafee2020-04-20 08:04:51 -070047import com.google.devtools.build.lib.packages.StarlarkProvider;
gregce74d84d42020-04-17 10:02:03 -070048import com.google.devtools.build.lib.packages.StarlarkProviderIdentifier;
cparsons4ebf6c02018-08-17 14:49:36 -070049import com.google.devtools.build.lib.packages.StructImpl;
cparsons0c5c1c62018-05-24 10:37:03 -070050import com.google.devtools.build.lib.packages.StructProvider;
Googlerc5fcc862019-09-06 16:17:47 -070051import com.google.devtools.build.lib.packages.Type;
brandjon771a0292020-05-26 12:04:16 -070052import com.google.devtools.build.lib.skyframe.BzlLoadFunction;
adonovan4444f1a2020-05-12 20:03:11 -070053import com.google.devtools.build.lib.skylark.util.BazelEvaluationTestCase;
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +000054import com.google.devtools.build.lib.syntax.ClassObject;
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;
Googlerf0890f02019-10-01 07:28:48 -070064import com.google.devtools.build.lib.syntax.SyntaxError;
Googlercfd681f2019-11-11 07:24:02 -080065import com.google.devtools.build.lib.syntax.Tuple;
adonovan4444f1a2020-05-12 20:03:11 -070066import com.google.devtools.build.lib.syntax.util.EvaluationTestCase;
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;
Googler92578702019-11-21 12:19:31 -080069import javax.annotation.Nullable;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000070import org.junit.Before;
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +000071import org.junit.Rule;
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +000072import org.junit.Test;
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +000073import org.junit.rules.ExpectedException;
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +000074import org.junit.runner.RunWith;
75import org.junit.runners.JUnit4;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000076
gregceb100b1d2020-05-20 10:22:17 -070077/** Tests for StarlarkRuleClassFunctions. */
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +000078@RunWith(JUnit4.class)
adonovan4444f1a2020-05-12 20:03:11 -070079public final class StarlarkRuleClassFunctionsTest extends BuildViewTestCase {
80
81 private final EvaluationTestCase ev = new BazelEvaluationTestCase();
82
83 private StarlarkRuleContext createRuleContext(String label) throws Exception {
84 return new StarlarkRuleContext(
85 getRuleContextForStarlark(getConfiguredTarget(label)), null, getStarlarkSemantics());
86 }
87
88 @Override
89 protected void setStarlarkSemanticsOptions(String... options) throws Exception {
90 super.setStarlarkSemanticsOptions(options); // for BuildViewTestCase
91 ev.setSemantics(options); // for StarlarkThread
92 }
93
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +000094 @Rule public ExpectedException thrown = ExpectedException.none();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000095
96 @Before
Taras Tsugrii7fe70472018-07-25 13:58:02 -070097 public final void createBuildFile() throws Exception {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000098 scratch.file(
99 "foo/BUILD",
100 "genrule(name = 'foo',",
101 " cmd = 'dummy_cmd',",
102 " srcs = ['a.txt', 'b.img'],",
103 " tools = ['t.exe'],",
104 " outs = ['c.txt'])",
105 "genrule(name = 'bar',",
106 " cmd = 'dummy_cmd',",
107 " srcs = [':jl', ':gl'],",
108 " outs = ['d.txt'])",
109 "java_library(name = 'jl',",
110 " srcs = ['a.java'])",
111 "genrule(name = 'gl',",
112 " cmd = 'touch $(OUTS)',",
113 " srcs = ['a.go'],",
114 " outs = [ 'gl.a', 'gl.gcgox', ],",
115 " output_to_bindir = 1,",
116 ")");
117 }
118
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000119 @Test
Florian Weikerte96b0b82015-09-25 11:35:11 +0000120 public void testCannotOverrideBuiltInAttribute() throws Exception {
Dmitry Lomov950310f2017-03-01 17:45:12 +0000121 ev.setFailFast(false);
brandjon8bd20162017-12-28 08:49:54 -0800122 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -0700123 ev,
124 "def impl(ctx):", //
brandjon8bd20162017-12-28 08:49:54 -0800125 " return",
126 "r = rule(impl, attrs = {'tags': attr.string_list()})");
127 ev.assertContainsError(
128 "There is already a built-in attribute 'tags' which cannot be overridden");
Florian Weikerte96b0b82015-09-25 11:35:11 +0000129 }
130
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000131 @Test
Vladimir Moskvada574922016-10-05 16:36:49 +0000132 public void testCannotOverrideBuiltInAttributeName() throws Exception {
Dmitry Lomov950310f2017-03-01 17:45:12 +0000133 ev.setFailFast(false);
brandjon8bd20162017-12-28 08:49:54 -0800134 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -0700135 ev,
136 "def impl(ctx):", //
brandjon8bd20162017-12-28 08:49:54 -0800137 " return",
138 "r = rule(impl, attrs = {'name': attr.string()})");
139 ev.assertContainsError(
140 "There is already a built-in attribute 'name' which cannot be overridden");
Vladimir Moskvada574922016-10-05 16:36:49 +0000141 }
142
143 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000144 public void testImplicitArgsAttribute() throws Exception {
Dmitry Lomov950310f2017-03-01 17:45:12 +0000145 ev.setFailFast(false);
Dmitry Lomov7b599452015-11-26 10:07:32 +0000146 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -0700147 ev,
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000148 "def _impl(ctx):",
149 " pass",
150 "exec_rule = rule(implementation = _impl, executable = True)",
151 "non_exec_rule = rule(implementation = _impl)");
lberkiaea56b32017-05-30 12:35:33 +0200152 assertThat(getRuleClass("exec_rule").hasAttr("args", Type.STRING_LIST)).isTrue();
153 assertThat(getRuleClass("non_exec_rule").hasAttr("args", Type.STRING_LIST)).isFalse();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000154 }
155
156 private RuleClass getRuleClass(String name) throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -0700157 return ((StarlarkRuleFunction) ev.lookup(name)).getRuleClass();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000158 }
159
Googler3fcfbe12019-08-28 08:10:11 -0700160 private void registerDummyStarlarkFunction() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -0700161 ev.exec("def impl():", " pass");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000162 }
163
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000164 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000165 public void testAttrWithOnlyType() throws Exception {
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000166 Attribute attr = buildAttribute("a1", "attr.string_list()");
lberkiaea56b32017-05-30 12:35:33 +0200167 assertThat(attr.getType()).isEqualTo(Type.STRING_LIST);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000168 }
169
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000170 private Attribute buildAttribute(String name, String... lines) throws Exception {
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000171 String[] strings = lines.clone();
172 strings[strings.length - 1] = String.format("%s = %s", name, strings[strings.length - 1]);
adonovan4444f1a2020-05-12 20:03:11 -0700173 evalAndExport(ev, strings);
174 StarlarkAttrModule.Descriptor lookup = (StarlarkAttrModule.Descriptor) ev.lookup(name);
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000175 return lookup != null ? lookup.build(name) : null;
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000176 }
177
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000178 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000179 public void testOutputListAttr() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000180 Attribute attr = buildAttribute("a1", "attr.output_list()");
lberkiaea56b32017-05-30 12:35:33 +0200181 assertThat(attr.getType()).isEqualTo(BuildType.OUTPUT_LIST);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000182 }
183
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000184 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000185 public void testIntListAttr() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000186 Attribute attr = buildAttribute("a1", "attr.int_list()");
lberkiaea56b32017-05-30 12:35:33 +0200187 assertThat(attr.getType()).isEqualTo(Type.INTEGER_LIST);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000188 }
189
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000190 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000191 public void testOutputAttr() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000192 Attribute attr = buildAttribute("a1", "attr.output()");
lberkiaea56b32017-05-30 12:35:33 +0200193 assertThat(attr.getType()).isEqualTo(BuildType.OUTPUT);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000194 }
195
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000196 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000197 public void testStringDictAttr() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000198 Attribute attr = buildAttribute("a1", "attr.string_dict(default = {'a': 'b'})");
lberkiaea56b32017-05-30 12:35:33 +0200199 assertThat(attr.getType()).isEqualTo(Type.STRING_DICT);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000200 }
201
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000202 @Test
Francois-Rene Rideau028e3192015-10-29 14:26:59 +0000203 public void testStringListDictAttr() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000204 Attribute attr = buildAttribute("a1", "attr.string_list_dict(default = {'a': ['b', 'c']})");
lberkiaea56b32017-05-30 12:35:33 +0200205 assertThat(attr.getType()).isEqualTo(Type.STRING_LIST_DICT);
Francois-Rene Rideau028e3192015-10-29 14:26:59 +0000206 }
207
208 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000209 public void testAttrAllowedFileTypesAnyFile() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000210 Attribute attr = buildAttribute("a1", "attr.label_list(allow_files = True)");
lberkiaea56b32017-05-30 12:35:33 +0200211 assertThat(attr.getAllowedFileTypesPredicate()).isEqualTo(FileTypeSet.ANY_FILE);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000212 }
213
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000214 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000215 public void testAttrAllowedFileTypesWrongType() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -0700216 ev.checkEvalErrorContains(
Googleree056632019-10-10 13:04:49 -0700217 "allow_files should be a boolean or a string list", "attr.label_list(allow_files = 18)");
Laurent Le Brun2445df12016-05-11 14:36:40 +0000218 }
219
220 @Test
laurentlbe6ab3c42019-07-01 10:43:57 -0700221 public void testAttrNameSpecialCharactersAreForbidden() throws Exception {
laurentlbe6ab3c42019-07-01 10:43:57 -0700222 ev.setFailFast(false);
adonovan4444f1a2020-05-12 20:03:11 -0700223 evalAndExport(ev, "def impl(ctx): return", "r = rule(impl, attrs = {'ab$c': attr.int()})");
laurentlb0fddb302019-08-16 14:02:01 -0700224 ev.assertContainsError("attribute name `ab$c` is not a valid identifier");
laurentlbe6ab3c42019-07-01 10:43:57 -0700225 }
226
227 @Test
228 public void testAttrNameCannotStartWithDigit() throws Exception {
laurentlbe6ab3c42019-07-01 10:43:57 -0700229 ev.setFailFast(false);
adonovan4444f1a2020-05-12 20:03:11 -0700230 evalAndExport(ev, "def impl(ctx): return", "r = rule(impl, attrs = {'2_foo': attr.int()})");
laurentlb0fddb302019-08-16 14:02:01 -0700231 ev.assertContainsError("attribute name `2_foo` is not a valid identifier");
laurentlbe6ab3c42019-07-01 10:43:57 -0700232 }
233
234 @Test
michajlob839a512020-03-11 10:04:23 -0700235 public void testRuleClassTooManyAttributes() throws Exception {
236 ev.setFailFast(false);
237
238 ImmutableList.Builder<String> linesBuilder =
239 ImmutableList.<String>builder()
240 .add("def impl(ctx): return")
241 .add("r = rule(impl, attrs = {");
michajlo7475b772020-03-12 15:10:11 -0700242 for (int i = 0; i < 200; i++) {
michajlob839a512020-03-11 10:04:23 -0700243 linesBuilder.add(" 'attr" + i + "': attr.int(),");
244 }
245 linesBuilder.add("})");
246
adonovan4444f1a2020-05-12 20:03:11 -0700247 evalAndExport(ev, linesBuilder.build().toArray(new String[0]));
michajlob839a512020-03-11 10:04:23 -0700248
249 assertThat(ev.getEventCollector()).hasSize(1);
250 Event event = ev.getEventCollector().iterator().next();
251 assertThat(event.getKind()).isEqualTo(EventKind.ERROR);
252 assertThat(event.getMessage()).contains("Rule class r declared too many attributes");
253 }
254
255 @Test
michajlo0a89cef2020-04-06 12:04:12 -0700256 public void testRuleClassTooLongAttributeName() throws Exception {
257 ev.setFailFast(false);
258
259 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -0700260 ev,
michajlo0a89cef2020-04-06 12:04:12 -0700261 "def impl(ctx): return;",
262 "r = rule(impl, attrs = { '" + Strings.repeat("x", 150) + "': attr.int() })");
263
264 assertThat(ev.getEventCollector()).hasSize(1);
265 Event event = ev.getEventCollector().iterator().next();
266 assertThat(event.getKind()).isEqualTo(EventKind.ERROR);
267 assertThat(event.getMessage())
268 .matches("Attribute r\\.x{150}'s name is too long \\(150 > 128\\)");
269 }
270
271 @Test
Laurent Le Brun50681c12016-07-05 10:08:54 +0000272 public void testAttrAllowedSingleFileTypesWrongType() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -0700273 ev.checkEvalErrorContains(
Laurent Le Brun50681c12016-07-05 10:08:54 +0000274 "allow_single_file should be a boolean or a string list",
275 "attr.label(allow_single_file = 18)");
276 }
277
278 @Test
Laurent Le Brun2445df12016-05-11 14:36:40 +0000279 public void testAttrWithList() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000280 Attribute attr = buildAttribute("a1", "attr.label_list(allow_files = ['.xml'])");
lberkiaea56b32017-05-30 12:35:33 +0200281 assertThat(attr.getAllowedFileTypesPredicate().apply("a.xml")).isTrue();
282 assertThat(attr.getAllowedFileTypesPredicate().apply("a.txt")).isFalse();
283 assertThat(attr.isSingleArtifact()).isFalse();
Laurent Le Brun50681c12016-07-05 10:08:54 +0000284 }
285
286 @Test
287 public void testAttrSingleFileWithList() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000288 Attribute attr = buildAttribute("a1", "attr.label(allow_single_file = ['.xml'])");
lberkiaea56b32017-05-30 12:35:33 +0200289 assertThat(attr.getAllowedFileTypesPredicate().apply("a.xml")).isTrue();
290 assertThat(attr.getAllowedFileTypesPredicate().apply("a.txt")).isFalse();
291 assertThat(attr.isSingleArtifact()).isTrue();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000292 }
293
gregce74d84d42020-04-17 10:02:03 -0700294 private static StarlarkProviderIdentifier legacy(String legacyId) {
295 return StarlarkProviderIdentifier.forLegacy(legacyId);
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000296 }
297
gregce74d84d42020-04-17 10:02:03 -0700298 private static StarlarkProviderIdentifier declared(String exportedName) {
gregceb6eafee2020-04-20 08:04:51 -0700299 return StarlarkProviderIdentifier.forKey(new StarlarkProvider.Key(FAKE_LABEL, exportedName));
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000300 }
301
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000302 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000303 public void testAttrWithProviders() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000304 Attribute attr =
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000305 buildAttribute("a1",
306 "b = provider()",
307 "attr.label_list(allow_files = True, providers = ['a', b])");
dslomovc13bb392017-08-02 23:29:54 +0200308 assertThat(attr.getRequiredProviders().isSatisfiedBy(set(legacy("a"), declared("b")))).isTrue();
309 assertThat(attr.getRequiredProviders().isSatisfiedBy(set(legacy("a")))).isFalse();
310 }
311
312 @Test
313 public void testAttrWithProvidersOneEmpty() throws Exception {
314 Attribute attr =
315 buildAttribute(
316 "a1",
317 "b = provider()",
318 "attr.label_list(allow_files = True, providers = [['a', b],[]])");
319 assertThat(attr.getRequiredProviders().acceptsAny()).isTrue();
dslomovc32e1b12017-07-31 19:23:52 +0200320 }
321
dslomovc32e1b12017-07-31 19:23:52 +0200322 @Test
Yun Peng83fbb91a2016-02-23 18:37:44 +0000323 public void testAttrWithProvidersList() throws Exception {
324 Attribute attr =
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000325 buildAttribute("a1",
326 "b = provider()",
327 "attr.label_list(allow_files = True, providers = [['a', b], ['c']])");
dslomovc13bb392017-08-02 23:29:54 +0200328 assertThat(attr.getRequiredProviders().isSatisfiedBy(set(legacy("a"), declared("b")))).isTrue();
329 assertThat(attr.getRequiredProviders().isSatisfiedBy(set(legacy("c")))).isTrue();
330 assertThat(attr.getRequiredProviders().isSatisfiedBy(set(legacy("a")))).isFalse();
331 }
332
gregce74d84d42020-04-17 10:02:03 -0700333 private static AdvertisedProviderSet set(StarlarkProviderIdentifier... ids) {
dslomovc13bb392017-08-02 23:29:54 +0200334 AdvertisedProviderSet.Builder builder = AdvertisedProviderSet.builder();
gregce74d84d42020-04-17 10:02:03 -0700335 for (StarlarkProviderIdentifier id : ids) {
gregced7c1cef2020-05-12 07:51:48 -0700336 builder.addStarlark(id);
dslomovc13bb392017-08-02 23:29:54 +0200337 }
338 return builder.build();
Yun Peng83fbb91a2016-02-23 18:37:44 +0000339 }
340
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000341 private void checkAttributeError(String expectedMessage, String... lines) throws Exception {
342 ev.setFailFast(false);
343 buildAttribute("fakeAttribute", lines);
344 MoreAsserts.assertContainsEvent(ev.getEventCollector(), expectedMessage);
345 }
346
Yun Peng83fbb91a2016-02-23 18:37:44 +0000347 @Test
348 public void testAttrWithWrongProvidersList() throws Exception {
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000349 checkAttributeError(
350 "element in 'providers' is of unexpected type. Either all elements should be providers,"
351 + " or all elements should be lists of providers,"
352 + " but got list with an element of type int.",
Yun Pengda9410c2016-03-18 21:14:51 +0000353 "attr.label_list(allow_files = True, providers = [['a', 1], ['c']])");
Yun Peng83fbb91a2016-02-23 18:37:44 +0000354
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000355 checkAttributeError(
356 "element in 'providers' is of unexpected type. Either all elements should be providers,"
357 + " or all elements should be lists of providers,"
358 + " but got an element of type string.",
359 "b = provider()",
360 "attr.label_list(allow_files = True, providers = [['a', b], 'c'])");
361
362 checkAttributeError(
363 "element in 'providers' is of unexpected type. Either all elements should be providers,"
364 + " or all elements should be lists of providers,"
365 + " but got an element of type string.",
366 "c = provider()",
367 "attr.label_list(allow_files = True, providers = [['a', b], c])");
Yun Peng83fbb91a2016-02-23 18:37:44 +0000368 }
369
370 @Test
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000371 public void testLabelListWithAspects() throws Exception {
Dmitry Lomov950310f2017-03-01 17:45:12 +0000372 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -0700373 ev,
374 "def _impl(target, ctx):",
375 " pass",
376 "my_aspect = aspect(implementation = _impl)",
377 "a = attr.label_list(aspects = [my_aspect])");
378 StarlarkAttrModule.Descriptor attr = (StarlarkAttrModule.Descriptor) ev.lookup("a");
379 StarlarkDefinedAspect aspect = (StarlarkDefinedAspect) ev.lookup("my_aspect");
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000380 assertThat(aspect).isNotNull();
Dmitry Lomov950310f2017-03-01 17:45:12 +0000381 assertThat(attr.build("xxx").getAspectClasses()).containsExactly(aspect.getAspectClass());
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000382 }
383
384 @Test
Dmitry Lomov950310f2017-03-01 17:45:12 +0000385 public void testLabelWithAspects() throws Exception {
386 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -0700387 ev,
Dmitry Lomov950310f2017-03-01 17:45:12 +0000388 "def _impl(target, ctx):",
389 " pass",
390 "my_aspect = aspect(implementation = _impl)",
391 "a = attr.label(aspects = [my_aspect])");
adonovan4444f1a2020-05-12 20:03:11 -0700392 StarlarkAttrModule.Descriptor attr = (StarlarkAttrModule.Descriptor) ev.lookup("a");
393 StarlarkDefinedAspect aspect = (StarlarkDefinedAspect) ev.lookup("my_aspect");
Dmitry Lomov950310f2017-03-01 17:45:12 +0000394 assertThat(aspect).isNotNull();
395 assertThat(attr.build("xxx").getAspectClasses()).containsExactly(aspect.getAspectClass());
396 }
397
Dmitry Lomov950310f2017-03-01 17:45:12 +0000398 @Test
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000399 public void testLabelListWithAspectsError() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -0700400 ev.checkEvalErrorContains(
adonovan85803902020-04-16 14:46:57 -0700401 "at index 0 of aspects, got element of type int, want Aspect",
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000402 "def _impl(target, ctx):",
403 " pass",
404 "my_aspect = aspect(implementation = _impl)",
Laurent Le Brunc31f3512016-12-29 21:41:33 +0000405 "attr.label_list(aspects = [my_aspect, 123])");
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000406 }
407
408 @Test
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000409 public void testAspectExtraDeps() throws Exception {
410 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -0700411 ev,
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000412 "def _impl(target, ctx):",
413 " pass",
414 "my_aspect = aspect(_impl,",
Dmitry Lomovace678e2015-12-16 15:10:20 +0000415 " attrs = { '_extra_deps' : attr.label(default = Label('//foo/bar:baz')) }",
416 ")");
adonovan4444f1a2020-05-12 20:03:11 -0700417 StarlarkDefinedAspect aspect = (StarlarkDefinedAspect) ev.lookup("my_aspect");
Googler74558fc2016-05-06 21:47:42 +0000418 Attribute attribute = Iterables.getOnlyElement(aspect.getAttributes());
419 assertThat(attribute.getName()).isEqualTo("$extra_deps");
420 assertThat(attribute.getDefaultValue(null))
dannarkda327bb2018-06-22 11:44:27 -0700421 .isEqualTo(
422 Label.parseAbsolute(
423 "//foo/bar:baz",
424 /* defaultToMain= */ false,
425 /* repositoryMapping= */ ImmutableMap.of()));
Dmitry Lomovace678e2015-12-16 15:10:20 +0000426 }
427
428 @Test
Googler74558fc2016-05-06 21:47:42 +0000429 public void testAspectParameter() throws Exception {
430 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -0700431 ev,
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 ")");
adonovan4444f1a2020-05-12 20:03:11 -0700437 StarlarkDefinedAspect aspect = (StarlarkDefinedAspect) ev.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 {
adonovan4444f1a2020-05-12 20:03:11 -0700444 ev.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 {
adonovan4444f1a2020-05-12 20:03:11 -0700456 ev.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(
adonovan4444f1a2020-05-12 20:03:11 -0700469 ev,
Googler74558fc2016-05-06 21:47:42 +0000470 "def _impl(target, ctx):",
471 " pass",
472 "my_aspect = aspect(_impl,",
473 " attrs = { 'param' : attr.string(values=['a', 'b']),",
474 " '_extra' : attr.label(default = Label('//foo/bar:baz')) }",
475 ")");
adonovan4444f1a2020-05-12 20:03:11 -0700476 StarlarkDefinedAspect aspect = (StarlarkDefinedAspect) ev.lookup("my_aspect");
Googler74558fc2016-05-06 21:47:42 +0000477 assertThat(aspect.getAttributes()).hasSize(2);
478 assertThat(aspect.getParamAttributes()).containsExactly("param");
Dmitry Lomovace678e2015-12-16 15:10:20 +0000479 }
480
481 @Test
482 public void testAspectNoDefaultValueAttribute() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -0700483 ev.checkEvalErrorContains(
Dmitry Lomovace678e2015-12-16 15:10:20 +0000484 "Aspect attribute '_extra_deps' has no default value",
485 "def _impl(target, ctx):",
486 " pass",
487 "my_aspect = aspect(_impl,",
488 " attrs = { '_extra_deps' : attr.label() }",
489 ")");
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000490 }
491
492 @Test
John Cater9a8d16e2017-07-05 16:12:07 -0400493 public void testAspectAddToolchain() throws Exception {
494 scratch.file("test/BUILD", "toolchain_type(name = 'my_toolchain_type')");
495 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -0700496 ev, "def _impl(ctx): pass", "a1 = aspect(_impl, toolchains=['//test:my_toolchain_type'])");
497 StarlarkDefinedAspect a = (StarlarkDefinedAspect) ev.lookup("a1");
John Cater9a8d16e2017-07-05 16:12:07 -0400498 assertThat(a.getRequiredToolchains()).containsExactly(makeLabel("//test:my_toolchain_type"));
499 }
500
501 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000502 public void testNonLabelAttrWithProviders() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -0700503 ev.checkEvalErrorContains(
adonovan3f602f22020-01-08 10:28:10 -0800504 "unexpected keyword argument 'providers'", "attr.string(providers = ['a'])");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000505 }
506
cparsonse2d200f2018-03-06 16:15:11 -0800507 private static final RuleClass.ConfiguredTargetFactory<Object, Object, Exception>
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000508 DUMMY_CONFIGURED_TARGET_FACTORY =
dslomovc13bb392017-08-02 23:29:54 +0200509 ruleContext -> {
510 throw new IllegalStateException();
511 };
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000512
513 private RuleClass ruleClass(String name) {
514 return new RuleClass.Builder(name, RuleClassType.NORMAL, false)
515 .factory(DUMMY_CONFIGURED_TARGET_FACTORY)
516 .add(Attribute.attr("tags", Type.STRING_LIST))
517 .build();
518 }
vladmos9c787fa2017-07-04 11:45:22 -0400519
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000520 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000521 public void testAttrAllowedRuleClassesSpecificRuleClasses() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000522 Attribute attr = buildAttribute("a",
523 "attr.label_list(allow_rules = ['java_binary'], allow_files = True)");
lberkiaea56b32017-05-30 12:35:33 +0200524 assertThat(attr.getAllowedRuleClassesPredicate().apply(ruleClass("java_binary"))).isTrue();
525 assertThat(attr.getAllowedRuleClassesPredicate().apply(ruleClass("genrule"))).isFalse();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000526 }
vladmos9c787fa2017-07-04 11:45:22 -0400527
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000528 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000529 public void testAttrDefaultValue() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000530 Attribute attr = buildAttribute("a1", "attr.string(default = 'some value')");
juliexxia84d1a662018-12-26 14:07:04 -0800531 assertThat(attr.getDefaultValueUnchecked()).isEqualTo("some value");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000532 }
533
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000534 @Test
vladmos9c787fa2017-07-04 11:45:22 -0400535 public void testLabelAttrDefaultValueAsString() throws Exception {
536 Attribute sligleAttr = buildAttribute("a1", "attr.label(default = '//foo:bar')");
juliexxia84d1a662018-12-26 14:07:04 -0800537 assertThat(sligleAttr.getDefaultValueUnchecked())
dannarkda327bb2018-06-22 11:44:27 -0700538 .isEqualTo(
539 Label.parseAbsolute(
540 "//foo:bar",
541 /* defaultToMain= */ false,
542 /* repositoryMapping= */ ImmutableMap.of()));
vladmos9c787fa2017-07-04 11:45:22 -0400543
544 Attribute listAttr =
545 buildAttribute("a2", "attr.label_list(default = ['//foo:bar', '//bar:foo'])");
juliexxia84d1a662018-12-26 14:07:04 -0800546 assertThat(listAttr.getDefaultValueUnchecked())
vladmos9c787fa2017-07-04 11:45:22 -0400547 .isEqualTo(
548 ImmutableList.of(
dannarkda327bb2018-06-22 11:44:27 -0700549 Label.parseAbsolute(
550 "//foo:bar",
551 /* defaultToMain= */ false,
552 /* repositoryMapping= */ ImmutableMap.of()),
553 Label.parseAbsolute(
554 "//bar:foo",
555 /* defaultToMain= */ false,
556 /*repositoryMapping= */ ImmutableMap.of())));
vladmos9c787fa2017-07-04 11:45:22 -0400557
558 Attribute dictAttr =
559 buildAttribute("a3", "attr.label_keyed_string_dict(default = {'//foo:bar': 'my value'})");
juliexxia84d1a662018-12-26 14:07:04 -0800560 assertThat(dictAttr.getDefaultValueUnchecked())
dannarkda327bb2018-06-22 11:44:27 -0700561 .isEqualTo(
562 ImmutableMap.of(
563 Label.parseAbsolute(
564 "//foo:bar",
565 /* defaultToMain= */ false,
566 /* repositoryMapping= */ ImmutableMap.of()),
567 "my value"));
vladmos9c787fa2017-07-04 11:45:22 -0400568 }
569
570 @Test
571 public void testLabelAttrDefaultValueAsStringBadValue() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -0700572 ev.checkEvalErrorContains(
vladmos9c787fa2017-07-04 11:45:22 -0400573 "invalid label '/foo:bar' in parameter 'default' of attribute 'label': "
janakrd3fe5e72018-03-30 12:49:12 -0700574 + "invalid target name '/foo:bar'",
vladmos9c787fa2017-07-04 11:45:22 -0400575 "attr.label(default = '/foo:bar')");
576
adonovan4444f1a2020-05-12 20:03:11 -0700577 ev.checkEvalErrorContains(
vladmos9c787fa2017-07-04 11:45:22 -0400578 "invalid label '/bar:foo' in element 1 of parameter 'default' of attribute "
janakrd3fe5e72018-03-30 12:49:12 -0700579 + "'label_list': invalid target name '/bar:foo'",
vladmos9c787fa2017-07-04 11:45:22 -0400580 "attr.label_list(default = ['//foo:bar', '/bar:foo'])");
581
adonovan4444f1a2020-05-12 20:03:11 -0700582 ev.checkEvalErrorContains(
janakrd3fe5e72018-03-30 12:49:12 -0700583 "invalid label '/bar:foo' in dict key element: invalid target name '/bar:foo'",
vladmos9c787fa2017-07-04 11:45:22 -0400584 "attr.label_keyed_string_dict(default = {'//foo:bar': 'a', '/bar:foo': 'b'})");
585 }
586
587 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000588 public void testAttrDefaultValueBadType() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -0700589 ev.checkEvalErrorContains("got value of type 'int', want 'string'", "attr.string(default = 1)");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000590 }
591
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000592 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000593 public void testAttrMandatory() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000594 Attribute attr = buildAttribute("a1", "attr.string(mandatory=True)");
lberkiaea56b32017-05-30 12:35:33 +0200595 assertThat(attr.isMandatory()).isTrue();
596 assertThat(attr.isNonEmpty()).isFalse();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000597 }
598
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000599 @Test
Laurent Le Brunbc16f722016-07-06 13:40:24 +0000600 public void testAttrAllowEmpty() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000601 Attribute attr = buildAttribute("a1", "attr.string_list(allow_empty=False)");
lberkiaea56b32017-05-30 12:35:33 +0200602 assertThat(attr.isNonEmpty()).isTrue();
603 assertThat(attr.isMandatory()).isFalse();
Laurent Le Brunbc16f722016-07-06 13:40:24 +0000604 }
605
606 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000607 public void testAttrBadKeywordArguments() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -0700608 ev.checkEvalErrorContains(
adonovan3f602f22020-01-08 10:28:10 -0800609 "string() got unexpected keyword argument 'bad_keyword'", "attr.string(bad_keyword = '')");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000610 }
611
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000612 @Test
Laurent Le Brunc4ddf6f2016-07-04 13:38:38 +0000613 public void testAttrCfg() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000614 Attribute attr = buildAttribute("a1", "attr.label(cfg = 'host', allow_files = True)");
jcaterb44167f2019-04-02 12:06:26 -0700615 assertThat(attr.getTransitionFactory().isHost()).isTrue();
Laurent Le Brunc4ddf6f2016-07-04 13:38:38 +0000616 }
617
618 @Test
Vladimir Moskva5a510772016-11-23 19:03:38 +0000619 public void testAttrCfgTarget() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000620 Attribute attr = buildAttribute("a1", "attr.label(cfg = 'target', allow_files = True)");
John Cater5adcd3e2019-03-28 10:14:32 -0700621 assertThat(NoTransition.isInstance(attr.getTransitionFactory())).isTrue();
Vladimir Moskva5a510772016-11-23 19:03:38 +0000622 }
623
624 @Test
gregcebceecab2018-06-27 17:44:45 -0700625 public void incompatibleDataTransition() throws Exception {
gregcebceecab2018-06-27 17:44:45 -0700626 EvalException expected =
adonovan4444f1a2020-05-12 20:03:11 -0700627 assertThrows(EvalException.class, () -> ev.eval("attr.label(cfg = 'data')"));
laurentlbd576ed02019-03-26 15:35:06 -0700628 assertThat(expected).hasMessageThat().contains("cfg must be either 'host' or 'target'");
gregcebceecab2018-06-27 17:44:45 -0700629 }
630
631 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000632 public void testAttrValues() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000633 Attribute attr = buildAttribute("a1", "attr.string(values = ['ab', 'cd'])");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000634 PredicateWithMessage<Object> predicate = attr.getAllowedValues();
635 assertThat(predicate.apply("ab")).isTrue();
636 assertThat(predicate.apply("xy")).isFalse();
637 }
638
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000639 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000640 public void testAttrIntValues() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000641 Attribute attr = buildAttribute("a1", "attr.int(values = [1, 2])");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000642 PredicateWithMessage<Object> predicate = attr.getAllowedValues();
643 assertThat(predicate.apply(2)).isTrue();
644 assertThat(predicate.apply(3)).isFalse();
645 }
646
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000647 @Test
allevato45b79e52017-07-07 21:40:50 +0200648 public void testAttrDoc() throws Exception {
649 // We don't actually store the doc in the attr definition; right now it's just meant to be
650 // extracted by documentation generating tools. So we don't have anything to assert and we just
651 // verify that no exceptions were thrown from building them.
652 buildAttribute("a1", "attr.bool(doc='foo')");
653 buildAttribute("a2", "attr.int(doc='foo')");
654 buildAttribute("a3", "attr.int_list(doc='foo')");
655 buildAttribute("a4", "attr.label(doc='foo')");
656 buildAttribute("a5", "attr.label_keyed_string_dict(doc='foo')");
657 buildAttribute("a6", "attr.label_list(doc='foo')");
allevato45b79e52017-07-07 21:40:50 +0200658 buildAttribute("a8", "attr.output(doc='foo')");
659 buildAttribute("a9", "attr.output_list(doc='foo')");
660 buildAttribute("a10", "attr.string(doc='foo')");
661 buildAttribute("a11", "attr.string_dict(doc='foo')");
662 buildAttribute("a12", "attr.string_list(doc='foo')");
663 buildAttribute("a13", "attr.string_list_dict(doc='foo')");
664 }
665
666 @Test
laurentlbd8d37762018-10-26 14:08:33 -0700667 public void testNoAttrLicense() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -0700668 EvalException expected = assertThrows(EvalException.class, () -> ev.eval("attr.license()"));
adonovan54cdd582020-06-09 18:58:42 -0700669 assertThat(expected).hasMessageThat().contains("'attr' value has no field or method 'license'");
laurentlbd8d37762018-10-26 14:08:33 -0700670 }
671
672 @Test
allevato45b79e52017-07-07 21:40:50 +0200673 public void testAttrDocValueBadType() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -0700674 ev.checkEvalErrorContains("got value of type 'int', want 'string'", "attr.string(doc = 1)");
allevato45b79e52017-07-07 21:40:50 +0200675 }
676
677 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000678 public void testRuleImplementation() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -0700679 evalAndExport(ev, "def impl(ctx): return None", "rule1 = rule(impl)");
680 RuleClass c = ((StarlarkRuleFunction) ev.lookup("rule1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200681 assertThat(c.getConfiguredTargetFunction().getName()).isEqualTo("impl");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000682 }
683
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000684 @Test
allevato45b79e52017-07-07 21:40:50 +0200685 public void testRuleDoc() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -0700686 evalAndExport(ev, "def impl(ctx): return None", "rule1 = rule(impl, doc='foo')");
allevato45b79e52017-07-07 21:40:50 +0200687 }
688
689 @Test
adonovan3f602f22020-01-08 10:28:10 -0800690 public void testFunctionAsAttrDefault() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -0700691 ev.exec("def f(): pass");
adonovan3f602f22020-01-08 10:28:10 -0800692
adonovanaf226dd2019-12-17 12:43:00 -0800693 // Late-bound attributes, which are computed during analysis as a function
694 // of the configuration, are only available for attributes involving labels:
695 // attr.label
696 // attr.label_list
697 // attr.label_keyed_string_dict
698 // attr.output,
699 // attr.output_list
adonovan3f602f22020-01-08 10:28:10 -0800700 // (See testRuleClassImplicitOutputFunctionDependingOnComputedAttribute
701 // for a more detailed positive test.)
702 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -0700703 ev,
adonovan3f602f22020-01-08 10:28:10 -0800704 "attr.label(default=f)",
705 "attr.label_list(default=f)",
706 "attr.label_keyed_string_dict(default=f)");
adonovan3f602f22020-01-08 10:28:10 -0800707 // For all other attribute types, the default value may not be a function.
adonovanaf226dd2019-12-17 12:43:00 -0800708 //
adonovan3f602f22020-01-08 10:28:10 -0800709 // (This is a regression test for github.com/bazelbuild/bazel/issues/9463.
710 // The loading-phase feature of "computed attribute defaults" is not exposed
gregce8345b792020-05-15 13:23:29 -0700711 // to Starlark; the bug was that the @StarlarkMethod
adonovanaf226dd2019-12-17 12:43:00 -0800712 // annotation was more permissive than the method declaration.)
adonovan4444f1a2020-05-12 20:03:11 -0700713 ev.checkEvalErrorContains(
714 "got value of type 'function', want 'string'", "attr.string(default=f)");
715 ev.checkEvalErrorContains(
adonovan7f266402020-04-20 10:15:47 -0700716 "got value of type 'function', want 'sequence'", "attr.string_list(default=f)");
adonovan4444f1a2020-05-12 20:03:11 -0700717 ev.checkEvalErrorContains("got value of type 'function', want 'int'", "attr.int(default=f)");
718 ev.checkEvalErrorContains(
adonovan7f266402020-04-20 10:15:47 -0700719 "got value of type 'function', want 'sequence'", "attr.int_list(default=f)");
adonovan4444f1a2020-05-12 20:03:11 -0700720 ev.checkEvalErrorContains("got value of type 'function', want 'bool'", "attr.bool(default=f)");
721 ev.checkEvalErrorContains(
adonovan3f602f22020-01-08 10:28:10 -0800722 "got value of type 'function', want 'dict'", "attr.string_dict(default=f)");
adonovan4444f1a2020-05-12 20:03:11 -0700723 ev.checkEvalErrorContains(
adonovan3f602f22020-01-08 10:28:10 -0800724 "got value of type 'function', want 'dict'", "attr.string_list_dict(default=f)");
725 // Note: attr.license appears to be disabled already.
726 // (see --incompatible_no_attr_license)
adonovanaf226dd2019-12-17 12:43:00 -0800727 }
728
Dmitry Lomov7b599452015-11-26 10:07:32 +0000729 private static final Label FAKE_LABEL = Label.parseAbsoluteUnchecked("//fake/label.bzl");
730
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000731 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000732 public void testRuleAddAttribute() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -0700733 evalAndExport(ev, "def impl(ctx): return None", "r1 = rule(impl, attrs={'a1': attr.string()})");
734 RuleClass c = ((StarlarkRuleFunction) ev.lookup("r1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200735 assertThat(c.hasAttr("a1", Type.STRING)).isTrue();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000736 }
737
adonovan4444f1a2020-05-12 20:03:11 -0700738 private static void evalAndExport(EvaluationTestCase ev, String... lines) throws Exception {
Googler2abde272019-09-17 12:06:08 -0700739 ParserInput input = ParserInput.fromLines(lines);
adonovan09d13702020-05-19 08:26:55 -0700740 Module module = ev.getModule();
adonovan034220a2020-03-24 10:11:26 -0700741 StarlarkFile file = EvalUtils.parseAndValidate(input, FileOptions.DEFAULT, module);
Googlerba868542019-10-09 07:26:27 -0700742 if (!file.ok()) {
adonovanac1c41e2020-04-01 14:28:49 -0700743 throw new SyntaxError.Exception(file.errors());
Googlerf0890f02019-10-01 07:28:48 -0700744 }
brandjon771a0292020-05-26 12:04:16 -0700745 BzlLoadFunction.execAndExport(
adonovan09d13702020-05-19 08:26:55 -0700746 file, FAKE_LABEL, ev.getEventHandler(), module, ev.getStarlarkThread());
Dmitry Lomov7b599452015-11-26 10:07:32 +0000747 }
748
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000749 @Test
Jon Brandveinded4fbb2017-01-18 22:21:04 +0000750 public void testExportAliasedName() throws Exception {
gregceb100b1d2020-05-20 10:22:17 -0700751 // When there are multiple names aliasing the same StarlarkExportable, the first one to be
Jon Brandveinded4fbb2017-01-18 22:21:04 +0000752 // declared should be used. Make sure we're not using lexicographical order, hash order,
753 // non-deterministic order, or anything else.
754 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -0700755 ev,
Jon Brandveinded4fbb2017-01-18 22:21:04 +0000756 "def _impl(ctx): pass",
757 "d = rule(implementation = _impl)",
758 "a = d",
759 // Having more names improves the chance that non-determinism will be caught.
760 "b = d",
761 "c = d",
762 "e = d",
763 "f = d",
764 "foo = d",
765 "bar = d",
766 "baz = d",
767 "x = d",
768 "y = d",
769 "z = d");
adonovan4444f1a2020-05-12 20:03:11 -0700770 String dName = ((StarlarkRuleFunction) ev.lookup("d")).getRuleClass().getName();
771 String fooName = ((StarlarkRuleFunction) ev.lookup("foo")).getRuleClass().getName();
Jon Brandveinded4fbb2017-01-18 22:21:04 +0000772 assertThat(dName).isEqualTo("d");
773 assertThat(fooName).isEqualTo("d");
774 }
775
776 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000777 public void testOutputToGenfiles() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -0700778 evalAndExport(ev, "def impl(ctx): pass", "r1 = rule(impl, output_to_genfiles=True)");
779 RuleClass c = ((StarlarkRuleFunction) ev.lookup("r1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200780 assertThat(c.hasBinaryOutput()).isFalse();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000781 }
782
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000783 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000784 public void testRuleAddMultipleAttributes() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000785 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -0700786 ev,
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000787 "def impl(ctx): return None",
788 "r1 = rule(impl,",
789 " attrs = {",
790 " 'a1': attr.label_list(allow_files=True),",
791 " 'a2': attr.int()",
792 "})");
adonovan4444f1a2020-05-12 20:03:11 -0700793 RuleClass c = ((StarlarkRuleFunction) ev.lookup("r1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200794 assertThat(c.hasAttr("a1", BuildType.LABEL_LIST)).isTrue();
795 assertThat(c.hasAttr("a2", Type.INTEGER)).isTrue();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000796 }
Taras Tsugrii7fe70472018-07-25 13:58:02 -0700797
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000798 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000799 public void testRuleAttributeFlag() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000800 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -0700801 ev,
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000802 "def impl(ctx): return None",
803 "r1 = rule(impl, attrs = {'a1': attr.string(mandatory=True)})");
adonovan4444f1a2020-05-12 20:03:11 -0700804 RuleClass c = ((StarlarkRuleFunction) ev.lookup("r1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200805 assertThat(c.getAttributeByName("a1").isMandatory()).isTrue();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000806 }
807
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000808 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000809 public void testRuleOutputs() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000810 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -0700811 ev,
812 "def impl(ctx): return None", //
Dmitry Lomov7b599452015-11-26 10:07:32 +0000813 "r1 = rule(impl, outputs = {'a': 'a.txt'})");
adonovan4444f1a2020-05-12 20:03:11 -0700814 RuleClass c = ((StarlarkRuleFunction) ev.lookup("r1")).getRuleClass();
Michajlo Matijkiw6d471412016-08-09 20:35:45 +0000815 ImplicitOutputsFunction function = c.getDefaultImplicitOutputsFunction();
vladmos076977e2017-12-02 14:15:32 -0800816 assertThat(function.getImplicitOutputs(ev.getEventHandler(), null)).containsExactly("a.txt");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000817 }
818
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000819 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000820 public void testRuleUnknownKeyword() throws Exception {
Googler3fcfbe12019-08-28 08:10:11 -0700821 registerDummyStarlarkFunction();
adonovan4444f1a2020-05-12 20:03:11 -0700822 ev.checkEvalErrorContains(
adonovan3f602f22020-01-08 10:28:10 -0800823 "unexpected keyword argument 'bad_keyword'", "rule(impl, bad_keyword = 'some text')");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000824 }
825
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000826 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000827 public void testRuleImplementationMissing() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -0700828 ev.checkEvalErrorContains(
adonovan3f602f22020-01-08 10:28:10 -0800829 "rule() missing 1 required positional argument: implementation", "rule(attrs = {})");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000830 }
831
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000832 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000833 public void testRuleBadTypeForAdd() throws Exception {
Googler3fcfbe12019-08-28 08:10:11 -0700834 registerDummyStarlarkFunction();
adonovan4444f1a2020-05-12 20:03:11 -0700835 ev.checkEvalErrorContains(
adonovan3f602f22020-01-08 10:28:10 -0800836 "in call to rule(), parameter 'attrs' got value of type 'string', want 'dict or NoneType'",
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000837 "rule(impl, attrs = 'some text')");
838 }
839
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000840 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000841 public void testRuleBadTypeInAdd() throws Exception {
Googler3fcfbe12019-08-28 08:10:11 -0700842 registerDummyStarlarkFunction();
adonovan4444f1a2020-05-12 20:03:11 -0700843 ev.checkEvalErrorContains(
adonovan07fb6a52020-03-20 14:36:28 -0700844 "got dict<string, string> for 'attrs', want dict<string, Attribute>",
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000845 "rule(impl, attrs = {'a1': 'some text'})");
846 }
847
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000848 @Test
allevato45b79e52017-07-07 21:40:50 +0200849 public void testRuleBadTypeForDoc() throws Exception {
Googler3fcfbe12019-08-28 08:10:11 -0700850 registerDummyStarlarkFunction();
adonovan4444f1a2020-05-12 20:03:11 -0700851 ev.checkEvalErrorContains("got value of type 'int', want 'string'", "rule(impl, doc = 1)");
allevato45b79e52017-07-07 21:40:50 +0200852 }
853
854 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000855 public void testLabel() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -0700856 Object result = ev.eval("Label('//foo/foo:foo')");
Dmitry Lomov7b599452015-11-26 10:07:32 +0000857 assertThat(result).isInstanceOf(Label.class);
lberkiaea56b32017-05-30 12:35:33 +0200858 assertThat(result.toString()).isEqualTo("//foo/foo:foo");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000859 }
860
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000861 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000862 public void testLabelSameInstance() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -0700863 Object l1 = ev.eval("Label('//foo/foo:foo')");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000864 // Implicitly creates a new pkgContext and environment, yet labels should be the same.
adonovan4444f1a2020-05-12 20:03:11 -0700865 Object l2 = ev.eval("Label('//foo/foo:foo')");
cpovirka4d3da62019-05-02 14:27:33 -0700866 assertThat(l1).isSameInstanceAs(l2);
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 testLabelNameAndPackage() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -0700871 Object result = ev.eval("Label('//foo/bar:baz').name");
lberkiaea56b32017-05-30 12:35:33 +0200872 assertThat(result).isEqualTo("baz");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000873 // NB: implicitly creates a new pkgContext and environments, yet labels should be the same.
adonovan4444f1a2020-05-12 20:03:11 -0700874 result = ev.eval("Label('//foo/bar:baz').package");
lberkiaea56b32017-05-30 12:35:33 +0200875 assertThat(result).isEqualTo("foo/bar");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000876 }
877
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000878 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000879 public void testRuleLabelDefaultValue() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000880 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -0700881 ev,
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000882 "def impl(ctx): return None\n"
883 + "r1 = rule(impl, attrs = {'a1': "
884 + "attr.label(default = Label('//foo:foo'), allow_files=True)})");
adonovan4444f1a2020-05-12 20:03:11 -0700885 RuleClass c = ((StarlarkRuleFunction) ev.lookup("r1")).getRuleClass();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000886 Attribute a = c.getAttributeByName("a1");
juliexxia84d1a662018-12-26 14:07:04 -0800887 assertThat(a.getDefaultValueUnchecked()).isInstanceOf(Label.class);
888 assertThat(a.getDefaultValueUnchecked().toString()).isEqualTo("//foo:foo");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000889 }
890
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000891 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000892 public void testIntDefaultValue() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000893 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -0700894 ev,
Dmitry Lomov7b599452015-11-26 10:07:32 +0000895 "def impl(ctx): return None",
896 "r1 = rule(impl, attrs = {'a1': attr.int(default = 40+2)})");
adonovan4444f1a2020-05-12 20:03:11 -0700897 RuleClass c = ((StarlarkRuleFunction) ev.lookup("r1")).getRuleClass();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000898 Attribute a = c.getAttributeByName("a1");
juliexxia84d1a662018-12-26 14:07:04 -0800899 assertThat(a.getDefaultValueUnchecked()).isEqualTo(42);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000900 }
901
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000902 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000903 public void testRuleInheritsBaseRuleAttributes() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -0700904 evalAndExport(ev, "def impl(ctx): return None", "r1 = rule(impl)");
905 RuleClass c = ((StarlarkRuleFunction) ev.lookup("r1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200906 assertThat(c.hasAttr("tags", Type.STRING_LIST)).isTrue();
907 assertThat(c.hasAttr("visibility", BuildType.NODEP_LABEL_LIST)).isTrue();
908 assertThat(c.hasAttr("deprecation", Type.STRING)).isTrue();
909 assertThat(c.hasAttr(":action_listener", BuildType.LABEL_LIST))
910 .isTrue(); // required for extra actions
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000911 }
912
913 private void checkTextMessage(String from, String... lines) throws Exception {
fangismed97d842019-04-12 14:19:46 -0700914 String[] strings = lines.clone();
adonovan4444f1a2020-05-12 20:03:11 -0700915 Object result = ev.eval(from);
fangismed97d842019-04-12 14:19:46 -0700916 String expect = "";
917 if (strings.length > 0) {
918 expect = Joiner.on("\n").join(lines) + "\n";
919 }
920 assertThat(result).isEqualTo(expect);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000921 }
922
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000923 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000924 public void testSimpleTextMessagesBooleanFields() throws Exception {
925 checkTextMessage("struct(name=True).to_proto()", "name: true");
926 checkTextMessage("struct(name=False).to_proto()", "name: false");
927 }
928
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000929 @Test
cparsons07460fc2018-06-20 10:41:48 -0700930 public void testStructRestrictedOverrides() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -0700931 ev.checkEvalErrorContains(
Googleree056632019-10-10 13:04:49 -0700932 "cannot override built-in struct function 'to_json'", "struct(to_json='foo')");
cparsons07460fc2018-06-20 10:41:48 -0700933
adonovan4444f1a2020-05-12 20:03:11 -0700934 ev.checkEvalErrorContains(
Googleree056632019-10-10 13:04:49 -0700935 "cannot override built-in struct function 'to_proto'", "struct(to_proto='foo')");
cparsons07460fc2018-06-20 10:41:48 -0700936 }
937
938 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000939 public void testSimpleTextMessages() throws Exception {
940 checkTextMessage("struct(name='value').to_proto()", "name: \"value\"");
fangismed97d842019-04-12 14:19:46 -0700941 checkTextMessage("struct(name=[]).to_proto()"); // empty lines
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000942 checkTextMessage("struct(name=['a', 'b']).to_proto()", "name: \"a\"", "name: \"b\"");
943 checkTextMessage("struct(name=123).to_proto()", "name: 123");
944 checkTextMessage("struct(name=[1, 2, 3]).to_proto()", "name: 1", "name: 2", "name: 3");
945 checkTextMessage("struct(a=struct(b='b')).to_proto()", "a {", " b: \"b\"", "}");
946 checkTextMessage(
947 "struct(a=[struct(b='x'), struct(b='y')]).to_proto()",
948 "a {",
949 " b: \"x\"",
950 "}",
951 "a {",
952 " b: \"y\"",
953 "}");
954 checkTextMessage(
955 "struct(a=struct(b=struct(c='c'))).to_proto()", "a {", " b {", " c: \"c\"", " }", "}");
fangismed97d842019-04-12 14:19:46 -0700956 // dict to_proto tests
957 checkTextMessage("struct(name={}).to_proto()"); // empty lines
958 checkTextMessage(
959 "struct(name={'a': 'b'}).to_proto()", "name {", " key: \"a\"", " value: \"b\"", "}");
960 checkTextMessage(
961 "struct(name={'c': 'd', 'a': 'b'}).to_proto()",
962 "name {",
963 " key: \"c\"",
964 " value: \"d\"",
965 "}",
966 "name {",
967 " key: \"a\"",
968 " value: \"b\"",
969 "}");
970 checkTextMessage(
971 "struct(x=struct(y={'a': 1})).to_proto()",
972 "x {",
973 " y {",
974 " key: \"a\"",
975 " value: 1",
976 " }",
977 "}");
978 checkTextMessage(
979 "struct(name={'a': struct(b=1, c=2)}).to_proto()",
980 "name {",
981 " key: \"a\"",
982 " value {",
983 " b: 1",
984 " c: 2",
985 " }",
986 "}");
987 checkTextMessage(
988 "struct(name={'a': struct(b={4: 'z', 3: 'y'}, c=2)}).to_proto()",
989 "name {",
990 " key: \"a\"",
991 " value {",
992 " b {",
993 " key: 4",
994 " value: \"z\"",
995 " }",
996 " b {",
997 " key: 3",
998 " value: \"y\"",
999 " }",
1000 " c: 2",
1001 " }",
1002 "}");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001003 }
1004
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +00001005 @Test
Vladimir Moskva76e31d12016-12-05 16:28:37 +00001006 public void testProtoFieldsOrder() throws Exception {
1007 checkTextMessage("struct(d=4, b=2, c=3, a=1).to_proto()", "a: 1", "b: 2", "c: 3", "d: 4");
1008 }
1009
1010 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001011 public void testTextMessageEscapes() throws Exception {
1012 checkTextMessage("struct(name='a\"b').to_proto()", "name: \"a\\\"b\"");
1013 checkTextMessage("struct(name='a\\'b').to_proto()", "name: \"a'b\"");
1014 checkTextMessage("struct(name='a\\nb').to_proto()", "name: \"a\\nb\"");
Googler4489aaf2016-06-17 15:17:37 +00001015
1016 // struct(name="a\\\"b") -> name: "a\\\"b"
1017 checkTextMessage("struct(name='a\\\\\\\"b').to_proto()", "name: \"a\\\\\\\"b\"");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001018 }
1019
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +00001020 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001021 public void testTextMessageInvalidElementInListStructure() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001022 ev.checkEvalErrorContains(
fangismed97d842019-04-12 14:19:46 -07001023 "Invalid text format, expected a struct, a dict, a string, a bool, or "
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001024 + "an int but got a list for list element in struct field 'a'",
1025 "struct(a=[['b']]).to_proto()");
1026 }
1027
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +00001028 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001029 public void testTextMessageInvalidStructure() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001030 ev.checkEvalErrorContains(
fangismed97d842019-04-12 14:19:46 -07001031 "Invalid text format, expected a struct, a dict, a string, a bool, or an int "
Vladimir Moskvacd12f772017-01-10 12:47:06 +00001032 + "but got a function for struct field 'a'",
1033 "struct(a=rule).to_proto()");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001034 }
1035
Erik Abair927f4592016-02-29 18:57:22 +00001036 private void checkJson(String from, String expected) throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001037 Object result = ev.eval(from);
lberkiaea56b32017-05-30 12:35:33 +02001038 assertThat(result).isEqualTo(expected);
Erik Abair927f4592016-02-29 18:57:22 +00001039 }
1040
1041 @Test
1042 public void testJsonBooleanFields() throws Exception {
1043 checkJson("struct(name=True).to_json()", "{\"name\":true}");
1044 checkJson("struct(name=False).to_json()", "{\"name\":false}");
1045 }
1046
1047 @Test
Taras Tsugrii7fe70472018-07-25 13:58:02 -07001048 public void testJsonDictFields() throws Exception {
1049 checkJson("struct(config={}).to_json()", "{\"config\":{}}");
1050 checkJson("struct(config={'key': 'value'}).to_json()", "{\"config\":{\"key\":\"value\"}}");
adonovan4444f1a2020-05-12 20:03:11 -07001051 ev.checkEvalErrorContains(
Taras Tsugrii7fe70472018-07-25 13:58:02 -07001052 "Keys must be a string but got a int for struct field 'config'",
1053 "struct(config={1:2}).to_json()");
adonovan4444f1a2020-05-12 20:03:11 -07001054 ev.checkEvalErrorContains(
Taras Tsugrii7fe70472018-07-25 13:58:02 -07001055 "Keys must be a string but got a int for dict value 'foo'",
1056 "struct(config={'foo':{1:2}}).to_json()");
adonovan4444f1a2020-05-12 20:03:11 -07001057 ev.checkEvalErrorContains(
Taras Tsugrii7fe70472018-07-25 13:58:02 -07001058 "Keys must be a string but got a bool for struct field 'config'",
1059 "struct(config={True: False}).to_json()");
1060 }
1061
1062 @Test
Erik Abair927f4592016-02-29 18:57:22 +00001063 public void testJsonEncoding() throws Exception {
1064 checkJson("struct(name='value').to_json()", "{\"name\":\"value\"}");
1065 checkJson("struct(name=['a', 'b']).to_json()", "{\"name\":[\"a\",\"b\"]}");
1066 checkJson("struct(name=123).to_json()", "{\"name\":123}");
1067 checkJson("struct(name=[1, 2, 3]).to_json()", "{\"name\":[1,2,3]}");
1068 checkJson("struct(a=struct(b='b')).to_json()", "{\"a\":{\"b\":\"b\"}}");
1069 checkJson("struct(a=[struct(b='x'), struct(b='y')]).to_json()",
1070 "{\"a\":[{\"b\":\"x\"},{\"b\":\"y\"}]}");
1071 checkJson("struct(a=struct(b=struct(c='c'))).to_json()",
1072 "{\"a\":{\"b\":{\"c\":\"c\"}}}");
1073 }
1074
1075 @Test
1076 public void testJsonEscapes() throws Exception {
1077 checkJson("struct(name='a\"b').to_json()", "{\"name\":\"a\\\"b\"}");
1078 checkJson("struct(name='a\\'b').to_json()", "{\"name\":\"a'b\"}");
Erik Abairec1f2b92016-03-01 00:45:33 +00001079 checkJson("struct(name='a\\\\b').to_json()", "{\"name\":\"a\\\\b\"}");
Erik Abair927f4592016-02-29 18:57:22 +00001080 checkJson("struct(name='a\\nb').to_json()", "{\"name\":\"a\\nb\"}");
Erik Abairec1f2b92016-03-01 00:45:33 +00001081 checkJson("struct(name='a\\rb').to_json()", "{\"name\":\"a\\rb\"}");
1082 checkJson("struct(name='a\\tb').to_json()", "{\"name\":\"a\\tb\"}");
Erik Abair927f4592016-02-29 18:57:22 +00001083 }
1084
1085 @Test
1086 public void testJsonNestedListStructure() throws Exception {
1087 checkJson("struct(a=[['b']]).to_json()", "{\"a\":[[\"b\"]]}");
1088 }
1089
1090 @Test
1091 public void testJsonInvalidStructure() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001092 ev.checkEvalErrorContains(
Erik Abair927f4592016-02-29 18:57:22 +00001093 "Invalid text format, expected a struct, a string, a bool, or an int but got a "
Vladimir Moskvacd12f772017-01-10 12:47:06 +00001094 + "function for struct field 'a'",
1095 "struct(a=rule).to_json()");
Erik Abair927f4592016-02-29 18:57:22 +00001096 }
1097
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +00001098 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001099 public void testLabelAttrWrongDefault() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001100 ev.checkEvalErrorContains(
adonovan3f602f22020-01-08 10:28:10 -08001101 "got value of type 'int', want 'Label or string or LateBoundDefault or function or"
1102 + " NoneType'",
vladmos9c787fa2017-07-04 11:45:22 -04001103 "attr.label(default = 123)");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001104 }
1105
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +00001106 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001107 public void testLabelGetRelative() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001108 assertThat(ev.eval("Label('//foo:bar').relative('baz')").toString()).isEqualTo("//foo:baz");
1109 assertThat(ev.eval("Label('//foo:bar').relative('//baz:qux')").toString())
1110 .isEqualTo("//baz:qux");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001111 }
1112
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +00001113 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001114 public void testLabelGetRelativeSyntaxError() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001115 ev.checkEvalErrorContains(
Damien Martin-Guillerez934c1d52017-03-03 14:44:56 +00001116 "invalid target name 'bad//syntax': target names may not contain '//' path separators",
1117 "Label('//foo:bar').relative('bad//syntax')");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001118 }
Greg Estren223976c2016-02-04 22:40:56 +00001119
1120 @Test
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001121 public void testStructCreation() throws Exception {
1122 // TODO(fwe): cannot be handled by current testing suite
adonovan4444f1a2020-05-12 20:03:11 -07001123 ev.exec("x = struct(a = 1, b = 2)");
1124 assertThat(ev.lookup("x")).isInstanceOf(ClassObject.class);
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001125 }
1126
1127 @Test
1128 public void testStructFields() throws Exception {
1129 // TODO(fwe): cannot be handled by current testing suite
adonovan4444f1a2020-05-12 20:03:11 -07001130 ev.exec("x = struct(a = 1, b = 2)");
1131 ClassObject x = (ClassObject) ev.lookup("x");
lberkiaea56b32017-05-30 12:35:33 +02001132 assertThat(x.getValue("a")).isEqualTo(1);
1133 assertThat(x.getValue("b")).isEqualTo(2);
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001134 }
1135
1136 @Test
Vladimir Moskvafdfa9882016-11-18 16:24:20 +00001137 public void testStructEquality() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001138 assertThat((Boolean) ev.eval("struct(a = 1, b = 2) == struct(b = 2, a = 1)")).isTrue();
1139 assertThat((Boolean) ev.eval("struct(a = 1) == struct(a = 1, b = 2)")).isFalse();
1140 assertThat((Boolean) ev.eval("struct(a = 1, b = 2) == struct(a = 1)")).isFalse();
Vladimir Moskvafdfa9882016-11-18 16:24:20 +00001141 // Compare a recursive object to itself to make sure reference equality is checked
adonovan4444f1a2020-05-12 20:03:11 -07001142 ev.exec("s = struct(a = 1, b = []); s.b.append(s)");
1143 assertThat((Boolean) ev.eval("s == s")).isTrue();
1144 assertThat((Boolean) ev.eval("struct(a = 1, b = 2) == struct(a = 1, b = 3)")).isFalse();
1145 assertThat((Boolean) ev.eval("struct(a = 1) == [1]")).isFalse();
1146 assertThat((Boolean) ev.eval("[1] == struct(a = 1)")).isFalse();
1147 assertThat((Boolean) ev.eval("struct() == struct()")).isTrue();
1148 assertThat((Boolean) ev.eval("struct() == struct(a = 1)")).isFalse();
Vladimir Moskvafdfa9882016-11-18 16:24:20 +00001149
adonovan4444f1a2020-05-12 20:03:11 -07001150 ev.exec("foo = provider(); bar = provider()");
1151 assertThat((Boolean) ev.eval("struct(a = 1) == foo(a = 1)")).isFalse();
1152 assertThat((Boolean) ev.eval("foo(a = 1) == struct(a = 1)")).isFalse();
1153 assertThat((Boolean) ev.eval("foo(a = 1) == bar(a = 1)")).isFalse();
1154 assertThat((Boolean) ev.eval("foo(a = 1) == foo(a = 1)")).isTrue();
Vladimir Moskvafdfa9882016-11-18 16:24:20 +00001155 }
1156
1157 @Test
Vladimir Moskva76e31d12016-12-05 16:28:37 +00001158 public void testStructIncomparability() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001159 ev.checkEvalErrorContains("Cannot compare structs", "struct(a = 1) < struct(a = 2)");
1160 ev.checkEvalErrorContains("Cannot compare structs", "struct(a = 1) > struct(a = 2)");
1161 ev.checkEvalErrorContains("Cannot compare structs", "struct(a = 1) <= struct(a = 2)");
1162 ev.checkEvalErrorContains("Cannot compare structs", "struct(a = 1) >= struct(a = 2)");
Vladimir Moskva76e31d12016-12-05 16:28:37 +00001163 }
1164
1165 @Test
gregced281df72020-05-11 12:27:06 -07001166 public void testStructAccessingFieldsFromStarlark() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001167 ev.exec("x = struct(a = 1, b = 2)", "x1 = x.a", "x2 = x.b");
1168 assertThat(ev.lookup("x1")).isEqualTo(1);
1169 assertThat(ev.lookup("x2")).isEqualTo(2);
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001170 }
1171
1172 @Test
1173 public void testStructAccessingUnknownField() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001174 ev.checkEvalErrorContains(
adonovan4e2b4952019-12-10 12:19:20 -08001175 "'struct' value has no field or method 'c'\n" + "Available attributes: a, b",
Googleree056632019-10-10 13:04:49 -07001176 "x = struct(a = 1, b = 2)",
1177 "y = x.c");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001178 }
1179
1180 @Test
1181 public void testStructAccessingUnknownFieldWithArgs() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001182 ev.checkEvalErrorContains(
adonovan4e2b4952019-12-10 12:19:20 -08001183 "'struct' value has no field or method 'c'", "x = struct(a = 1, b = 2)", "y = x.c()");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001184 }
1185
1186 @Test
1187 public void testStructAccessingNonFunctionFieldWithArgs() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001188 ev.checkEvalErrorContains(
Googleree056632019-10-10 13:04:49 -07001189 "'int' object is not callable", "x = struct(a = 1, b = 2)", "x1 = x.a(1)");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001190 }
1191
1192 @Test
1193 public void testStructAccessingFunctionFieldWithArgs() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001194 ev.exec("def f(x): return x+5", "x = struct(a = f, b = 2)", "x1 = x.a(1)");
1195 assertThat(ev.lookup("x1")).isEqualTo(6);
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001196 }
1197
1198 @Test
1199 public void testStructPosArgs() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001200 ev.checkEvalErrorContains(
1201 "struct() got unexpected positional argument", "x = struct(1, b = 2)");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001202 }
1203
1204 @Test
1205 public void testStructConcatenationFieldNames() throws Exception {
1206 // TODO(fwe): cannot be handled by current testing suite
adonovan4444f1a2020-05-12 20:03:11 -07001207 ev.exec(
Googler1a1fca22019-10-14 09:31:22 -07001208 "x = struct(a = 1, b = 2)", //
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001209 "y = struct(c = 1, d = 2)",
1210 "z = x + y\n");
adonovan4444f1a2020-05-12 20:03:11 -07001211 StructImpl z = (StructImpl) ev.lookup("z");
jcater91ed0f32019-11-07 10:07:16 -08001212 assertThat(z.getFieldNames()).containsExactly("a", "b", "c", "d");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001213 }
1214
1215 @Test
1216 public void testStructConcatenationFieldValues() throws Exception {
1217 // TODO(fwe): cannot be handled by current testing suite
adonovan4444f1a2020-05-12 20:03:11 -07001218 ev.exec(
Googler1a1fca22019-10-14 09:31:22 -07001219 "x = struct(a = 1, b = 2)", //
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001220 "y = struct(c = 1, d = 2)",
1221 "z = x + y\n");
adonovan4444f1a2020-05-12 20:03:11 -07001222 StructImpl z = (StructImpl) ev.lookup("z");
lberkiaea56b32017-05-30 12:35:33 +02001223 assertThat(z.getValue("a")).isEqualTo(1);
1224 assertThat(z.getValue("b")).isEqualTo(2);
1225 assertThat(z.getValue("c")).isEqualTo(1);
1226 assertThat(z.getValue("d")).isEqualTo(2);
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001227 }
1228
1229 @Test
1230 public void testStructConcatenationCommonFields() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001231 ev.checkEvalErrorContains(
adonovan553dc6c2019-12-10 11:22:48 -08001232 "cannot add struct instances with common field 'a'",
Googleree056632019-10-10 13:04:49 -07001233 "x = struct(a = 1, b = 2)",
1234 "y = struct(c = 1, a = 2)",
1235 "z = x + y\n");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001236 }
1237
1238 @Test
1239 public void testConditionalStructConcatenation() throws Exception {
1240 // TODO(fwe): cannot be handled by current testing suite
adonovan4444f1a2020-05-12 20:03:11 -07001241 ev.exec(
Googler1a1fca22019-10-14 09:31:22 -07001242 "def func():",
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001243 " x = struct(a = 1, b = 2)",
1244 " if True:",
1245 " x += struct(c = 1, d = 2)",
1246 " return x",
1247 "x = func()");
adonovan4444f1a2020-05-12 20:03:11 -07001248 StructImpl x = (StructImpl) ev.lookup("x");
lberkiaea56b32017-05-30 12:35:33 +02001249 assertThat(x.getValue("a")).isEqualTo(1);
1250 assertThat(x.getValue("b")).isEqualTo(2);
1251 assertThat(x.getValue("c")).isEqualTo(1);
1252 assertThat(x.getValue("d")).isEqualTo(2);
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001253 }
1254
1255 @Test
1256 public void testGetattrNoAttr() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001257 ev.checkEvalErrorContains(
adonovan4e2b4952019-12-10 12:19:20 -08001258 "'struct' value has no field or method 'b'\nAvailable attributes: a",
Googler055d6c62018-05-22 13:21:04 -07001259 "s = struct(a='val')",
1260 "getattr(s, 'b')");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001261 }
1262
1263 @Test
1264 public void testGetattr() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001265 ev.exec("s = struct(a='val')", "x = getattr(s, 'a')", "y = getattr(s, 'b', 'def')");
1266 assertThat(ev.lookup("x")).isEqualTo("val");
1267 assertThat(ev.lookup("y")).isEqualTo("def");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001268 }
1269
1270 @Test
1271 public void testHasattr() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001272 ev.exec(
Googler1a1fca22019-10-14 09:31:22 -07001273 "s = struct(a=1)", //
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001274 "x = hasattr(s, 'a')",
1275 "y = hasattr(s, 'b')\n");
adonovan4444f1a2020-05-12 20:03:11 -07001276 assertThat(ev.lookup("x")).isEqualTo(true);
1277 assertThat(ev.lookup("y")).isEqualTo(false);
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001278 }
1279
1280 @Test
1281 public void testStructStr() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001282 assertThat(ev.eval("str(struct(x = 2, y = 3, z = 4))"))
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001283 .isEqualTo("struct(x = 2, y = 3, z = 4)");
1284 }
1285
1286 @Test
1287 public void testStructsInSets() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001288 ev.exec("depset([struct(a='a')])");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001289 }
1290
1291 @Test
Vladimir Moskva76e31d12016-12-05 16:28:37 +00001292 public void testStructsInDicts() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001293 ev.exec("d = {struct(a = 1): 'aa', struct(b = 2): 'bb'}");
1294 assertThat(ev.eval("d[struct(a = 1)]")).isEqualTo("aa");
1295 assertThat(ev.eval("d[struct(b = 2)]")).isEqualTo("bb");
1296 assertThat(ev.eval("str([d[k] for k in d])")).isEqualTo("[\"aa\", \"bb\"]");
Vladimir Moskva76e31d12016-12-05 16:28:37 +00001297
adonovan4444f1a2020-05-12 20:03:11 -07001298 ev.checkEvalErrorContains("unhashable type: 'struct'", "{struct(a = []): 'foo'}");
Vladimir Moskva76e31d12016-12-05 16:28:37 +00001299 }
1300
1301 @Test
Vladimir Moskva10770382016-08-23 15:04:54 +00001302 public void testStructDictMembersAreMutable() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001303 ev.exec(
Googler1a1fca22019-10-14 09:31:22 -07001304 "s = struct(x = {'a' : 1})", //
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001305 "s.x['b'] = 2\n");
adonovan4444f1a2020-05-12 20:03:11 -07001306 assertThat(((StructImpl) ev.lookup("s")).getValue("x"))
1307 .isEqualTo(ImmutableMap.of("a", 1, "b", 2));
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001308 }
1309
1310 @Test
adonovan0de69d02020-05-07 20:39:16 -07001311 public void testDepsetGoodCompositeItem() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001312 ev.exec("def func():", " return depset([struct(a='a')])", "s = func()");
1313 ImmutableList<?> result = ((Depset) ev.lookup("s")).toList();
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001314 assertThat(result).hasSize(1);
adonovan0de69d02020-05-07 20:39:16 -07001315 assertThat(result.get(0)).isInstanceOf(StructImpl.class);
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001316 }
1317
cparsons4ebf6c02018-08-17 14:49:36 -07001318 private static StructImpl makeStruct(String field, Object value) {
cparsons0c5c1c62018-05-24 10:37:03 -07001319 return StructProvider.STRUCT.create(ImmutableMap.of(field, value), "no field '%'");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001320 }
1321
Googler92578702019-11-21 12:19:31 -08001322 private static StructImpl makeBigStruct(@Nullable Mutability mu) {
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001323 // struct(a=[struct(x={1:1}), ()], b=(), c={2:2})
cparsons0c5c1c62018-05-24 10:37:03 -07001324 return StructProvider.STRUCT.create(
Dmitry Lomovea9de072016-08-09 09:35:40 +00001325 ImmutableMap.<String, Object>of(
dslomovde965ac2017-07-31 21:07:51 +02001326 "a",
Googler942e1c42019-11-12 13:11:44 -08001327 StarlarkList.<Object>of(
Googler92578702019-11-21 12:19:31 -08001328 mu,
cparsons0c5c1c62018-05-24 10:37:03 -07001329 StructProvider.STRUCT.create(
Googler92578702019-11-21 12:19:31 -08001330 ImmutableMap.<String, Object>of("x", Dict.<Object, Object>of(mu, 1, 1)),
dslomovde965ac2017-07-31 21:07:51 +02001331 "no field '%s'"),
1332 Tuple.of()),
Dmitry Lomovea9de072016-08-09 09:35:40 +00001333 "b", Tuple.of(),
Googler92578702019-11-21 12:19:31 -08001334 "c", Dict.<Object, Object>of(mu, 2, 2)),
Dmitry Lomovea9de072016-08-09 09:35:40 +00001335 "no field '%s'");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001336 }
1337
1338 @Test
1339 public void testStructMutabilityShallow() throws Exception {
lberkiaea56b32017-05-30 12:35:33 +02001340 assertThat(EvalUtils.isImmutable(makeStruct("a", 1))).isTrue();
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001341 }
1342
Googler92578702019-11-21 12:19:31 -08001343 private static StarlarkList<Object> makeList(@Nullable Mutability mu) {
1344 return StarlarkList.<Object>of(mu, 1, 2, 3);
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001345 }
1346
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001347 @Test
1348 public void testStructMutabilityDeep() throws Exception {
lberkiaea56b32017-05-30 12:35:33 +02001349 assertThat(EvalUtils.isImmutable(Tuple.<Object>of(makeList(null)))).isTrue();
1350 assertThat(EvalUtils.isImmutable(makeStruct("a", makeList(null)))).isTrue();
1351 assertThat(EvalUtils.isImmutable(makeBigStruct(null))).isTrue();
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001352
adonovan09d13702020-05-19 08:26:55 -07001353 Mutability mu = Mutability.create("test");
Googler92578702019-11-21 12:19:31 -08001354 assertThat(EvalUtils.isImmutable(Tuple.<Object>of(makeList(mu)))).isFalse();
1355 assertThat(EvalUtils.isImmutable(makeStruct("a", makeList(mu)))).isFalse();
1356 assertThat(EvalUtils.isImmutable(makeBigStruct(mu))).isFalse();
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001357 }
1358
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001359 @Test
1360 public void declaredProviders() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001361 evalAndExport(ev, "data = provider()", "d = data(x = 1, y ='abc')", "d_x = d.x", "d_y = d.y");
1362 assertThat(ev.lookup("d_x")).isEqualTo(1);
1363 assertThat(ev.lookup("d_y")).isEqualTo("abc");
1364 StarlarkProvider dataConstructor = (StarlarkProvider) ev.lookup("data");
1365 StructImpl data = (StructImpl) ev.lookup("d");
dslomovde965ac2017-07-31 21:07:51 +02001366 assertThat(data.getProvider()).isEqualTo(dataConstructor);
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001367 assertThat(dataConstructor.isExported()).isTrue();
1368 assertThat(dataConstructor.getPrintableName()).isEqualTo("data");
gregceb6eafee2020-04-20 08:04:51 -07001369 assertThat(dataConstructor.getKey()).isEqualTo(new StarlarkProvider.Key(FAKE_LABEL, "data"));
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001370 }
1371
1372 @Test
1373 public void declaredProvidersConcatSuccess() throws Exception {
1374 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -07001375 ev,
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001376 "data = provider()",
1377 "dx = data(x = 1)",
1378 "dy = data(y = 'abc')",
1379 "dxy = dx + dy",
1380 "x = dxy.x",
adonovan4444f1a2020-05-12 20:03:11 -07001381 "y = dxy.y");
1382 assertThat(ev.lookup("x")).isEqualTo(1);
1383 assertThat(ev.lookup("y")).isEqualTo("abc");
1384 StarlarkProvider dataConstructor = (StarlarkProvider) ev.lookup("data");
1385 StructImpl dx = (StructImpl) ev.lookup("dx");
dslomovde965ac2017-07-31 21:07:51 +02001386 assertThat(dx.getProvider()).isEqualTo(dataConstructor);
adonovan4444f1a2020-05-12 20:03:11 -07001387 StructImpl dy = (StructImpl) ev.lookup("dy");
dslomovde965ac2017-07-31 21:07:51 +02001388 assertThat(dy.getProvider()).isEqualTo(dataConstructor);
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001389 }
1390
1391 @Test
1392 public void declaredProvidersConcatError() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001393 evalAndExport(ev, "data1 = provider()", "data2 = provider()");
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001394
adonovan4444f1a2020-05-12 20:03:11 -07001395 ev.checkEvalErrorContains(
Googleree056632019-10-10 13:04:49 -07001396 "Cannot use '+' operator on instances of different providers (data1 and data2)",
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001397 "d1 = data1(x = 1)",
1398 "d2 = data2(y = 2)",
Googleree056632019-10-10 13:04:49 -07001399 "d = d1 + d2");
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001400 }
Dmitry Lomov94b2c882016-09-25 20:24:24 +00001401
1402 @Test
Benjamin Peterson8610d972017-11-27 03:14:43 -08001403 public void declaredProvidersWithFieldsConcatSuccess() throws Exception {
1404 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -07001405 ev,
Benjamin Peterson8610d972017-11-27 03:14:43 -08001406 "data = provider(fields=['f1', 'f2'])",
1407 "d1 = data(f1 = 4)",
1408 "d2 = data(f2 = 5)",
1409 "d3 = d1 + d2",
1410 "f1 = d3.f1",
1411 "f2 = d3.f2");
adonovan4444f1a2020-05-12 20:03:11 -07001412 assertThat(ev.lookup("f1")).isEqualTo(4);
1413 assertThat(ev.lookup("f2")).isEqualTo(5);
Benjamin Peterson8610d972017-11-27 03:14:43 -08001414 }
1415
1416 @Test
1417 public void declaredProvidersWithFieldsConcatError() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001418 evalAndExport(ev, "data1 = provider(fields=['f1', 'f2'])", "data2 = provider(fields=['f3'])");
1419 ev.checkEvalErrorContains(
brandjona29648a2018-01-10 08:38:43 -08001420 "Cannot use '+' operator on instances of different providers (data1 and data2)",
Benjamin Peterson8610d972017-11-27 03:14:43 -08001421 "d1 = data1(f1=1, f2=2)",
1422 "d2 = data2(f3=3)",
1423 "d = d1 + d2");
1424 }
1425
1426 @Test
1427 public void declaredProvidersWithOverlappingFieldsConcatError() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001428 evalAndExport(ev, "data = provider(fields=['f1', 'f2'])");
1429 ev.checkEvalErrorContains(
adonovan553dc6c2019-12-10 11:22:48 -08001430 "cannot add struct instances with common field 'f1'",
Benjamin Peterson8610d972017-11-27 03:14:43 -08001431 "d1 = data(f1 = 4)",
1432 "d2 = data(f1 = 5)",
1433 "d1 + d2");
1434 }
1435
1436 @Test
Dmitry Lomov94b2c882016-09-25 20:24:24 +00001437 public void structsAsDeclaredProvidersTest() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001438 evalAndExport(ev, "data = struct(x = 1)");
1439 StructImpl data = (StructImpl) ev.lookup("data");
cparsons0c5c1c62018-05-24 10:37:03 -07001440 assertThat(StructProvider.STRUCT.isExported()).isTrue();
1441 assertThat(data.getProvider()).isEqualTo(StructProvider.STRUCT);
1442 assertThat(data.getProvider().getKey()).isEqualTo(StructProvider.STRUCT.getKey());
Dmitry Lomov94b2c882016-09-25 20:24:24 +00001443 }
Dmitry Lomov0692c7f2016-09-30 16:43:30 +00001444
1445 @Test
allevato45b79e52017-07-07 21:40:50 +02001446 public void declaredProvidersDoc() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001447 evalAndExport(ev, "data1 = provider(doc='foo')");
allevato45b79e52017-07-07 21:40:50 +02001448 }
1449
1450 @Test
1451 public void declaredProvidersBadTypeForDoc() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001452 ev.checkEvalErrorContains("got value of type 'int', want 'string'", "provider(doc = 1)");
allevato45b79e52017-07-07 21:40:50 +02001453 }
1454
1455 @Test
Dmitry Lomov0692c7f2016-09-30 16:43:30 +00001456 public void aspectAllAttrs() throws Exception {
1457 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -07001458 ev,
1459 "def _impl(target, ctx):", //
Dmitry Lomov0692c7f2016-09-30 16:43:30 +00001460 " pass",
1461 "my_aspect = aspect(_impl, attr_aspects=['*'])");
1462
adonovan4444f1a2020-05-12 20:03:11 -07001463 StarlarkDefinedAspect myAspect = (StarlarkDefinedAspect) ev.lookup("my_aspect");
lberki3bd28392019-02-05 01:01:02 -08001464 assertThat(myAspect.getDefinition(AspectParameters.EMPTY).propagateAlong("foo")).isTrue();
Dmitry Lomov0692c7f2016-09-30 16:43:30 +00001465 }
1466
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001467 @Test
1468 public void aspectRequiredAspectProvidersSingle() throws Exception {
1469 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -07001470 ev,
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001471 "def _impl(target, ctx):",
1472 " pass",
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001473 "cc = provider()",
adonovan4444f1a2020-05-12 20:03:11 -07001474 "my_aspect = aspect(_impl, required_aspect_providers=['java', cc])");
1475 StarlarkDefinedAspect myAspect = (StarlarkDefinedAspect) ev.lookup("my_aspect");
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001476 RequiredProviders requiredProviders = myAspect.getDefinition(AspectParameters.EMPTY)
1477 .getRequiredProvidersForAspects();
1478 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.ANY)).isTrue();
1479 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.EMPTY)).isFalse();
gregced7c1cef2020-05-12 07:51:48 -07001480 assertThat(
1481 requiredProviders.isSatisfiedBy(
1482 AdvertisedProviderSet.builder()
1483 .addStarlark(declared("cc"))
1484 .addStarlark("java")
1485 .build()))
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001486 .isTrue();
gregced7c1cef2020-05-12 07:51:48 -07001487 assertThat(
1488 requiredProviders.isSatisfiedBy(
1489 AdvertisedProviderSet.builder().addStarlark("cc").build()))
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001490 .isFalse();
1491 }
1492
1493 @Test
1494 public void aspectRequiredAspectProvidersAlternatives() throws Exception {
1495 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -07001496 ev,
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001497 "def _impl(target, ctx):",
1498 " pass",
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001499 "cc = provider()",
adonovan4444f1a2020-05-12 20:03:11 -07001500 "my_aspect = aspect(_impl, required_aspect_providers=[['java'], [cc]])");
1501 StarlarkDefinedAspect myAspect = (StarlarkDefinedAspect) ev.lookup("my_aspect");
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001502 RequiredProviders requiredProviders = myAspect.getDefinition(AspectParameters.EMPTY)
1503 .getRequiredProvidersForAspects();
1504 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.ANY)).isTrue();
1505 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.EMPTY)).isFalse();
gregced7c1cef2020-05-12 07:51:48 -07001506 assertThat(
1507 requiredProviders.isSatisfiedBy(
1508 AdvertisedProviderSet.builder().addStarlark("java").build()))
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001509 .isTrue();
gregced7c1cef2020-05-12 07:51:48 -07001510 assertThat(
1511 requiredProviders.isSatisfiedBy(
1512 AdvertisedProviderSet.builder().addStarlark(declared("cc")).build()))
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001513 .isTrue();
gregced7c1cef2020-05-12 07:51:48 -07001514 assertThat(
1515 requiredProviders.isSatisfiedBy(
1516 AdvertisedProviderSet.builder().addStarlark("prolog").build()))
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001517 .isFalse();
1518 }
1519
1520 @Test
1521 public void aspectRequiredAspectProvidersEmpty() throws Exception {
1522 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -07001523 ev,
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001524 "def _impl(target, ctx):",
1525 " pass",
adonovan4444f1a2020-05-12 20:03:11 -07001526 "my_aspect = aspect(_impl, required_aspect_providers=[])");
1527 StarlarkDefinedAspect myAspect = (StarlarkDefinedAspect) ev.lookup("my_aspect");
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001528 RequiredProviders requiredProviders = myAspect.getDefinition(AspectParameters.EMPTY)
1529 .getRequiredProvidersForAspects();
1530 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.ANY)).isFalse();
1531 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.EMPTY)).isFalse();
1532 }
1533
1534 @Test
1535 public void aspectRequiredAspectProvidersDefault() throws Exception {
1536 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -07001537 ev,
1538 "def _impl(target, ctx):", //
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001539 " pass",
adonovan4444f1a2020-05-12 20:03:11 -07001540 "my_aspect = aspect(_impl)");
1541 StarlarkDefinedAspect myAspect = (StarlarkDefinedAspect) ev.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
Dmitry Lomov950310f2017-03-01 17:45:12 +00001548 @Test
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001549 public void aspectProvides() throws Exception {
1550 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -07001551 ev,
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001552 "def _impl(target, ctx):",
1553 " pass",
1554 "y = provider()",
adonovan4444f1a2020-05-12 20:03:11 -07001555 "my_aspect = aspect(_impl, provides = ['x', y])");
1556 StarlarkDefinedAspect myAspect = (StarlarkDefinedAspect) ev.lookup("my_aspect");
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001557 AdvertisedProviderSet advertisedProviders = myAspect.getDefinition(AspectParameters.EMPTY)
1558 .getAdvertisedProviders();
1559 assertThat(advertisedProviders.canHaveAnyProvider()).isFalse();
gregced7c1cef2020-05-12 07:51:48 -07001560 assertThat(advertisedProviders.getStarlarkProviders())
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001561 .containsExactly(legacy("x"), declared("y"));
1562 }
1563
1564 @Test
1565 public void aspectProvidesError() throws Exception {
1566 ev.setFailFast(false);
1567 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -07001568 ev,
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001569 "def _impl(target, ctx):",
1570 " pass",
1571 "y = provider()",
adonovan4444f1a2020-05-12 20:03:11 -07001572 "my_aspect = aspect(_impl, provides = ['x', 1])");
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001573 MoreAsserts.assertContainsEvent(ev.getEventCollector(),
1574 " Illegal argument: element in 'provides' is of unexpected type."
cparsonsaab98682018-04-27 13:04:59 -07001575 + " Should be list of providers, but got item of type int. ");
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001576 }
1577
allevato45b79e52017-07-07 21:40:50 +02001578 @Test
1579 public void aspectDoc() throws Exception {
1580 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -07001581 ev,
1582 "def _impl(target, ctx):", //
allevato45b79e52017-07-07 21:40:50 +02001583 " pass",
1584 "my_aspect = aspect(_impl, doc='foo')");
1585 }
1586
1587 @Test
1588 public void aspectBadTypeForDoc() throws Exception {
Googler3fcfbe12019-08-28 08:10:11 -07001589 registerDummyStarlarkFunction();
adonovan4444f1a2020-05-12 20:03:11 -07001590 ev.checkEvalErrorContains("got value of type 'int', want 'string'", "aspect(impl, doc = 1)");
allevato45b79e52017-07-07 21:40:50 +02001591 }
1592
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001593 @Test
Dmitry Lomov950310f2017-03-01 17:45:12 +00001594 public void fancyExports() throws Exception {
1595 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -07001596 ev,
Dmitry Lomov950310f2017-03-01 17:45:12 +00001597 "def _impla(target, ctx): pass",
1598 "p, (a, p1) = [",
1599 " provider(),",
1600 " [ aspect(_impla),",
1601 " provider() ]",
adonovan4444f1a2020-05-12 20:03:11 -07001602 "]");
1603 StarlarkProvider p = (StarlarkProvider) ev.lookup("p");
1604 StarlarkDefinedAspect a = (StarlarkDefinedAspect) ev.lookup("a");
1605 StarlarkProvider p1 = (StarlarkProvider) ev.lookup("p1");
Dmitry Lomov950310f2017-03-01 17:45:12 +00001606 assertThat(p.getPrintableName()).isEqualTo("p");
gregceb6eafee2020-04-20 08:04:51 -07001607 assertThat(p.getKey()).isEqualTo(new StarlarkProvider.Key(FAKE_LABEL, "p"));
Dmitry Lomov950310f2017-03-01 17:45:12 +00001608 assertThat(p1.getPrintableName()).isEqualTo("p1");
gregceb6eafee2020-04-20 08:04:51 -07001609 assertThat(p1.getKey()).isEqualTo(new StarlarkProvider.Key(FAKE_LABEL, "p1"));
gregce18694cd2020-05-12 15:40:05 -07001610 assertThat(a.getAspectClass()).isEqualTo(new StarlarkAspectClass(FAKE_LABEL, "a"));
Dmitry Lomov950310f2017-03-01 17:45:12 +00001611 }
1612
1613 @Test
1614 public void multipleTopLevels() throws Exception {
1615 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -07001616 ev,
1617 "p = provider()", //
1618 "p1 = p");
1619 StarlarkProvider p = (StarlarkProvider) ev.lookup("p");
1620 StarlarkProvider p1 = (StarlarkProvider) ev.lookup("p1");
Dmitry Lomov950310f2017-03-01 17:45:12 +00001621 assertThat(p).isEqualTo(p1);
gregceb6eafee2020-04-20 08:04:51 -07001622 assertThat(p.getKey()).isEqualTo(new StarlarkProvider.Key(FAKE_LABEL, "p"));
1623 assertThat(p1.getKey()).isEqualTo(new StarlarkProvider.Key(FAKE_LABEL, "p"));
Dmitry Lomov950310f2017-03-01 17:45:12 +00001624 }
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001625
dslomov0667b832017-08-25 09:29:50 +02001626 @Test
1627 public void providerWithFields() throws Exception {
1628 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -07001629 ev,
1630 "p = provider(fields = ['x', 'y'])", //
dslomov0667b832017-08-25 09:29:50 +02001631 "p1 = p(x = 1, y = 2)",
1632 "x = p1.x",
adonovan4444f1a2020-05-12 20:03:11 -07001633 "y = p1.y");
1634 StarlarkProvider p = (StarlarkProvider) ev.lookup("p");
1635 StarlarkInfo p1 = (StarlarkInfo) ev.lookup("p1");
dslomov0667b832017-08-25 09:29:50 +02001636
dslomov0667b832017-08-25 09:29:50 +02001637 assertThat(p1.getProvider()).isEqualTo(p);
adonovan4444f1a2020-05-12 20:03:11 -07001638 assertThat(ev.lookup("x")).isEqualTo(1);
1639 assertThat(ev.lookup("y")).isEqualTo(2);
dslomov0667b832017-08-25 09:29:50 +02001640 }
1641
1642 @Test
1643 public void providerWithFieldsDict() throws Exception {
1644 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -07001645 ev,
dslomov0667b832017-08-25 09:29:50 +02001646 "p = provider(fields = { 'x' : 'I am x', 'y' : 'I am y'})",
1647 "p1 = p(x = 1, y = 2)",
1648 "x = p1.x",
adonovan4444f1a2020-05-12 20:03:11 -07001649 "y = p1.y");
1650 StarlarkProvider p = (StarlarkProvider) ev.lookup("p");
1651 StarlarkInfo p1 = (StarlarkInfo) ev.lookup("p1");
dslomov0667b832017-08-25 09:29:50 +02001652
dslomov0667b832017-08-25 09:29:50 +02001653 assertThat(p1.getProvider()).isEqualTo(p);
adonovan4444f1a2020-05-12 20:03:11 -07001654 assertThat(ev.lookup("x")).isEqualTo(1);
1655 assertThat(ev.lookup("y")).isEqualTo(2);
dslomov0667b832017-08-25 09:29:50 +02001656 }
1657
1658 @Test
1659 public void providerWithFieldsOptional() throws Exception {
1660 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -07001661 ev,
1662 "p = provider(fields = ['x', 'y'])", //
dslomov0667b832017-08-25 09:29:50 +02001663 "p1 = p(y = 2)",
adonovan4444f1a2020-05-12 20:03:11 -07001664 "y = p1.y");
1665 StarlarkProvider p = (StarlarkProvider) ev.lookup("p");
1666 StarlarkInfo p1 = (StarlarkInfo) ev.lookup("p1");
dslomov0667b832017-08-25 09:29:50 +02001667
dslomov0667b832017-08-25 09:29:50 +02001668 assertThat(p1.getProvider()).isEqualTo(p);
adonovan4444f1a2020-05-12 20:03:11 -07001669 assertThat(ev.lookup("y")).isEqualTo(2);
dslomov0667b832017-08-25 09:29:50 +02001670 }
1671
1672 @Test
1673 public void providerWithFieldsOptionalError() throws Exception {
1674 ev.setFailFast(false);
1675 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -07001676 ev,
1677 "p = provider(fields = ['x', 'y'])", //
dslomov0667b832017-08-25 09:29:50 +02001678 "p1 = p(y = 2)",
adonovan4444f1a2020-05-12 20:03:11 -07001679 "x = p1.x");
adonovan4e2b4952019-12-10 12:19:20 -08001680 MoreAsserts.assertContainsEvent(
1681 ev.getEventCollector(), " 'p' value has no field or method 'x'");
dslomov0667b832017-08-25 09:29:50 +02001682 }
1683
1684 @Test
1685 public void providerWithExtraFieldsError() throws Exception {
1686 ev.setFailFast(false);
adonovan4444f1a2020-05-12 20:03:11 -07001687 evalAndExport(ev, "p = provider(fields = ['x', 'y'])", "p1 = p(x = 1, y = 2, z = 3)");
adonovan7f266402020-04-20 10:15:47 -07001688 MoreAsserts.assertContainsEvent(
1689 ev.getEventCollector(), "unexpected keyword z in call to instantiate provider p");
dslomov0667b832017-08-25 09:29:50 +02001690 }
1691
1692 @Test
1693 public void providerWithEmptyFieldsError() throws Exception {
1694 ev.setFailFast(false);
1695 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -07001696 ev,
1697 "p = provider(fields = [])", //
1698 "p1 = p(x = 1, y = 2, z = 3)");
adonovan7f266402020-04-20 10:15:47 -07001699 MoreAsserts.assertContainsEvent(
1700 ev.getEventCollector(), "unexpected keywords x, y, z in call to instantiate provider p");
1701 }
1702
1703 @Test
1704 public void providerWithDuplicateFieldsError() throws Exception {
1705 ev.setFailFast(false);
adonovan4444f1a2020-05-12 20:03:11 -07001706 evalAndExport(
1707 ev,
1708 "p = provider(fields = ['a', 'b'])", //
1709 "p(a = 1, b = 2, **dict(b = 3))");
adonovan7f266402020-04-20 10:15:47 -07001710 MoreAsserts.assertContainsEvent(
1711 ev.getEventCollector(),
1712 "got multiple values for parameter b in call to instantiate provider p");
dslomov0667b832017-08-25 09:29:50 +02001713 }
1714
Dmitry Lomov0692c7f2016-09-30 16:43:30 +00001715 @Test
1716 public void starTheOnlyAspectArg() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001717 ev.checkEvalErrorContains(
Googleree056632019-10-10 13:04:49 -07001718 "'*' must be the only string in 'attr_aspects' list",
Dmitry Lomov0692c7f2016-09-30 16:43:30 +00001719 "def _impl(target, ctx):",
1720 " pass",
1721 "aspect(_impl, attr_aspects=['*', 'foo'])");
1722 }
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +00001723
1724 @Test
1725 public void testMandatoryConfigParameterForExecutableLabels() throws Exception {
1726 scratch.file("third_party/foo/extension.bzl",
1727 "def _main_rule_impl(ctx):",
1728 " pass",
1729 "my_rule = rule(_main_rule_impl,",
1730 " attrs = { ",
1731 " 'exe' : attr.label(executable = True, allow_files = True),",
1732 " },",
1733 ")"
1734 );
1735 scratch.file("third_party/foo/BUILD",
laurentlb5ddd8042017-11-30 12:03:31 -08001736 "load(':extension.bzl', 'my_rule')",
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +00001737 "my_rule(name = 'main', exe = ':tool.sh')"
1738 );
1739
cushon978cb002018-02-24 14:05:37 -08001740 AssertionError expected =
1741 assertThrows(AssertionError.class, () -> createRuleContext("//third_party/foo:main"));
brandjon8bd20162017-12-28 08:49:54 -08001742 assertThat(expected).hasMessageThat()
1743 .contains("cfg parameter is mandatory when executable=True is provided.");
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +00001744 }
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +00001745
John Catereca28402017-05-17 21:44:12 +02001746 @Test
1747 public void testRuleAddToolchain() throws Exception {
John Cater9a8d16e2017-07-05 16:12:07 -04001748 scratch.file("test/BUILD", "toolchain_type(name = 'my_toolchain_type')");
John Catereca28402017-05-17 21:44:12 +02001749 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -07001750 ev,
1751 "def impl(ctx): return None",
1752 "r1 = rule(impl, toolchains=['//test:my_toolchain_type'])");
1753 RuleClass c = ((StarlarkRuleFunction) ev.lookup("r1")).getRuleClass();
John Cater9a8d16e2017-07-05 16:12:07 -04001754 assertThat(c.getRequiredToolchains()).containsExactly(makeLabel("//test:my_toolchain_type"));
John Catereca28402017-05-17 21:44:12 +02001755 }
vladmos2f32e382017-06-19 12:44:21 -04001756
1757 @Test
John Caterd79e9772018-06-14 02:24:57 -07001758 public void testRuleAddExecutionConstraints() throws Exception {
Googler3fcfbe12019-08-28 08:10:11 -07001759 registerDummyStarlarkFunction();
John Caterd79e9772018-06-14 02:24:57 -07001760 scratch.file("test/BUILD", "toolchain_type(name = 'my_toolchain_type')");
1761 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -07001762 ev,
John Caterd79e9772018-06-14 02:24:57 -07001763 "r1 = rule(",
1764 " implementation = impl,",
1765 " toolchains=['//test:my_toolchain_type'],",
1766 " exec_compatible_with=['//constraint:cv1', '//constraint:cv2'],",
1767 ")");
adonovan4444f1a2020-05-12 20:03:11 -07001768 RuleClass c = ((StarlarkRuleFunction) ev.lookup("r1")).getRuleClass();
John Caterd79e9772018-06-14 02:24:57 -07001769 assertThat(c.getExecutionPlatformConstraints())
1770 .containsExactly(makeLabel("//constraint:cv1"), makeLabel("//constraint:cv2"));
1771 }
1772
1773 @Test
juliexxia693048d2020-04-01 06:41:42 -07001774 public void testRuleAddExecGroup() throws Exception {
gregce5ac90862020-04-20 13:07:58 -07001775 setStarlarkSemanticsOptions("--experimental_exec_groups=true");
juliexxia693048d2020-04-01 06:41:42 -07001776
1777 registerDummyStarlarkFunction();
1778 scratch.file("test/BUILD", "toolchain_type(name = 'my_toolchain_type')");
1779 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -07001780 ev,
juliexxia693048d2020-04-01 06:41:42 -07001781 "plum = rule(",
1782 " implementation = impl,",
1783 " exec_groups = {",
1784 " 'group': exec_group(",
1785 " toolchains=['//test:my_toolchain_type'],",
1786 " exec_compatible_with=['//constraint:cv1', '//constraint:cv2'],",
1787 " ),",
1788 " },",
1789 ")");
adonovan4444f1a2020-05-12 20:03:11 -07001790 RuleClass plum = ((StarlarkRuleFunction) ev.lookup("plum")).getRuleClass();
juliexxia693048d2020-04-01 06:41:42 -07001791 assertThat(plum.getRequiredToolchains()).isEmpty();
juliexxiafae89042020-06-12 12:05:21 -07001792 assertThat(plum.getExecGroups().get("group").requiredToolchains())
juliexxia693048d2020-04-01 06:41:42 -07001793 .containsExactly(makeLabel("//test:my_toolchain_type"));
1794 assertThat(plum.getExecutionPlatformConstraints()).isEmpty();
juliexxiafae89042020-06-12 12:05:21 -07001795 assertThat(plum.getExecGroups().get("group").execCompatibleWith())
juliexxia693048d2020-04-01 06:41:42 -07001796 .containsExactly(makeLabel("//constraint:cv1"), makeLabel("//constraint:cv2"));
1797 }
1798
1799 @Test
vladmos2f32e382017-06-19 12:44:21 -04001800 public void testRuleFunctionReturnsNone() throws Exception {
1801 scratch.file("test/rule.bzl",
1802 "def _impl(ctx):",
1803 " pass",
1804 "foo_rule = rule(",
1805 " implementation = _impl,",
1806 " attrs = {'params': attr.string_list()},",
1807 ")");
1808 scratch.file("test/BUILD",
1809 "load(':rule.bzl', 'foo_rule')",
1810 "r = foo_rule(name='foo')", // Custom rule should return None
1811 "c = cc_library(name='cc')", // Native rule should return None
1812 "",
1813 "foo_rule(",
1814 " name='check',",
1815 " params = [type(r), type(c)]",
1816 ")");
1817 invalidatePackages();
gregced281df72020-05-11 12:27:06 -07001818 StarlarkRuleContext context = createRuleContext("//test:check");
vladmos2f32e382017-06-19 12:44:21 -04001819 @SuppressWarnings("unchecked")
Googler942e1c42019-11-12 13:11:44 -08001820 StarlarkList<Object> params = (StarlarkList<Object>) context.getAttr().getValue("params");
vladmos2f32e382017-06-19 12:44:21 -04001821 assertThat(params.get(0)).isEqualTo("NoneType");
1822 assertThat(params.get(1)).isEqualTo("NoneType");
1823 }
cparsons0c5c1c62018-05-24 10:37:03 -07001824
1825 @Test
1826 public void testTypeOfStruct() throws Exception {
adonovan4444f1a2020-05-12 20:03:11 -07001827 ev.exec("p = type(struct)", "s = type(struct())");
cparsons0c5c1c62018-05-24 10:37:03 -07001828
adonovan4444f1a2020-05-12 20:03:11 -07001829 assertThat(ev.lookup("p")).isEqualTo("Provider");
1830 assertThat(ev.lookup("s")).isEqualTo("struct");
cparsons0c5c1c62018-05-24 10:37:03 -07001831 }
juliexxia44e21432020-03-31 08:21:20 -07001832
1833 @Test
1834 public void testCreateExecGroup() throws Exception {
gregce5ac90862020-04-20 13:07:58 -07001835 setStarlarkSemanticsOptions("--experimental_exec_groups=true");
juliexxia44e21432020-03-31 08:21:20 -07001836
1837 scratch.file("test/BUILD", "toolchain_type(name = 'my_toolchain_type')");
1838 evalAndExport(
adonovan4444f1a2020-05-12 20:03:11 -07001839 ev,
juliexxia44e21432020-03-31 08:21:20 -07001840 "group = exec_group(",
1841 " toolchains=['//test:my_toolchain_type'],",
1842 " exec_compatible_with=['//constraint:cv1', '//constraint:cv2'],",
1843 ")");
adonovan4444f1a2020-05-12 20:03:11 -07001844 ExecGroup group = ((ExecGroup) ev.lookup("group"));
juliexxiafae89042020-06-12 12:05:21 -07001845 assertThat(group.requiredToolchains()).containsExactly(makeLabel("//test:my_toolchain_type"));
1846 assertThat(group.execCompatibleWith())
juliexxia44e21432020-03-31 08:21:20 -07001847 .containsExactly(makeLabel("//constraint:cv1"), makeLabel("//constraint:cv2"));
1848 }
John Catereca28402017-05-17 21:44:12 +02001849}