blob: d280b3a2ad22bd30bfc0cf58df2f1bb68103eb06 [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;
cushon978cb002018-02-24 14:05:37 -080018import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000019
20import com.google.common.base.Joiner;
21import com.google.common.collect.ImmutableList;
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +000022import com.google.common.collect.ImmutableMap;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000023import com.google.common.collect.ImmutableSet;
24import com.google.common.collect.Iterables;
gregce7fa23ea2018-01-18 12:46:04 -080025import com.google.devtools.build.lib.analysis.config.transitions.NoTransition;
ulfjack35625252017-08-08 19:45:46 +020026import com.google.devtools.build.lib.analysis.skylark.SkylarkAttr;
27import com.google.devtools.build.lib.analysis.skylark.SkylarkAttr.Descriptor;
tomlu72642a22017-10-18 06:23:14 +020028import com.google.devtools.build.lib.analysis.skylark.SkylarkRuleClassFunctions.SkylarkRuleFunction;
ulfjack35625252017-08-08 19:45:46 +020029import com.google.devtools.build.lib.analysis.skylark.SkylarkRuleContext;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000030import com.google.devtools.build.lib.cmdline.Label;
Dmitry Lomovf868b3e2017-01-17 10:25:28 +000031import com.google.devtools.build.lib.packages.AdvertisedProviderSet;
Dmitry Lomov0692c7f2016-09-30 16:43:30 +000032import com.google.devtools.build.lib.packages.AspectParameters;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000033import com.google.devtools.build.lib.packages.Attribute;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000034import com.google.devtools.build.lib.packages.BuildType;
35import com.google.devtools.build.lib.packages.ImplicitOutputsFunction;
36import com.google.devtools.build.lib.packages.PredicateWithMessage;
Dmitry Lomovf868b3e2017-01-17 10:25:28 +000037import com.google.devtools.build.lib.packages.RequiredProviders;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000038import com.google.devtools.build.lib.packages.RuleClass;
39import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
John Caterd79e9772018-06-14 02:24:57 -070040import com.google.devtools.build.lib.packages.RuleClass.ExecutionPlatformConstraintsAllowed;
Dmitry Lomov950310f2017-03-01 17:45:12 +000041import com.google.devtools.build.lib.packages.SkylarkAspectClass;
cparsons0d55f4c2017-12-20 14:49:13 -080042import com.google.devtools.build.lib.packages.SkylarkDefinedAspect;
dslomov0667b832017-08-25 09:29:50 +020043import com.google.devtools.build.lib.packages.SkylarkInfo;
dslomovde965ac2017-07-31 21:07:51 +020044import com.google.devtools.build.lib.packages.SkylarkProvider;
Dmitry Lomovfd2bdc32016-10-07 08:52:10 +000045import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier;
cparsons4ebf6c02018-08-17 14:49:36 -070046import com.google.devtools.build.lib.packages.StructImpl;
cparsons0c5c1c62018-05-24 10:37:03 -070047import com.google.devtools.build.lib.packages.StructProvider;
Dmitry Lomov950310f2017-03-01 17:45:12 +000048import com.google.devtools.build.lib.skyframe.SkylarkImportLookupFunction;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000049import com.google.devtools.build.lib.skylark.util.SkylarkTestCase;
Dmitry Lomov950310f2017-03-01 17:45:12 +000050import com.google.devtools.build.lib.syntax.BuildFileAST;
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +000051import com.google.devtools.build.lib.syntax.ClassObject;
52import com.google.devtools.build.lib.syntax.Environment;
laurentlb707acfe2018-04-13 06:09:30 -070053import com.google.devtools.build.lib.syntax.EvalException;
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +000054import com.google.devtools.build.lib.syntax.EvalUtils;
55import com.google.devtools.build.lib.syntax.SkylarkDict;
56import com.google.devtools.build.lib.syntax.SkylarkList.MutableList;
57import com.google.devtools.build.lib.syntax.SkylarkList.Tuple;
58import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
laurentlb6659b4c2019-02-18 07:23:36 -080059import com.google.devtools.build.lib.syntax.StarlarkSemantics;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000060import com.google.devtools.build.lib.syntax.Type;
Dmitry Lomov8ff5a872017-03-04 00:58:14 +000061import com.google.devtools.build.lib.testutil.MoreAsserts;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000062import com.google.devtools.build.lib.util.FileTypeSet;
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +000063import java.util.Collection;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000064import org.junit.Before;
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +000065import org.junit.Rule;
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +000066import org.junit.Test;
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +000067import org.junit.rules.ExpectedException;
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +000068import org.junit.runner.RunWith;
69import org.junit.runners.JUnit4;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000070
71/**
72 * Tests for SkylarkRuleClassFunctions.
73 */
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +000074@RunWith(JUnit4.class)
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000075public class SkylarkRuleClassFunctionsTest extends SkylarkTestCase {
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +000076 @Rule public ExpectedException thrown = ExpectedException.none();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000077
78 @Before
Taras Tsugrii7fe70472018-07-25 13:58:02 -070079 public final void createBuildFile() throws Exception {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000080 scratch.file(
81 "foo/BUILD",
82 "genrule(name = 'foo',",
83 " cmd = 'dummy_cmd',",
84 " srcs = ['a.txt', 'b.img'],",
85 " tools = ['t.exe'],",
86 " outs = ['c.txt'])",
87 "genrule(name = 'bar',",
88 " cmd = 'dummy_cmd',",
89 " srcs = [':jl', ':gl'],",
90 " outs = ['d.txt'])",
91 "java_library(name = 'jl',",
92 " srcs = ['a.java'])",
93 "genrule(name = 'gl',",
94 " cmd = 'touch $(OUTS)',",
95 " srcs = ['a.go'],",
96 " outs = [ 'gl.a', 'gl.gcgox', ],",
97 " output_to_bindir = 1,",
98 ")");
99 }
100
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000101 @Test
Florian Weikerte96b0b82015-09-25 11:35:11 +0000102 public void testCannotOverrideBuiltInAttribute() throws Exception {
Dmitry Lomov950310f2017-03-01 17:45:12 +0000103 ev.setFailFast(false);
brandjon8bd20162017-12-28 08:49:54 -0800104 evalAndExport(
105 "def impl(ctx):",
106 " return",
107 "r = rule(impl, attrs = {'tags': attr.string_list()})");
108 ev.assertContainsError(
109 "There is already a built-in attribute 'tags' which cannot be overridden");
Florian Weikerte96b0b82015-09-25 11:35:11 +0000110 }
111
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000112 @Test
Vladimir Moskvada574922016-10-05 16:36:49 +0000113 public void testCannotOverrideBuiltInAttributeName() throws Exception {
Dmitry Lomov950310f2017-03-01 17:45:12 +0000114 ev.setFailFast(false);
brandjon8bd20162017-12-28 08:49:54 -0800115 evalAndExport(
116 "def impl(ctx):",
117 " return",
118 "r = rule(impl, attrs = {'name': attr.string()})");
119 ev.assertContainsError(
120 "There is already a built-in attribute 'name' which cannot be overridden");
Vladimir Moskvada574922016-10-05 16:36:49 +0000121 }
122
123 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000124 public void testImplicitArgsAttribute() throws Exception {
Dmitry Lomov950310f2017-03-01 17:45:12 +0000125 ev.setFailFast(false);
Dmitry Lomov7b599452015-11-26 10:07:32 +0000126 evalAndExport(
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000127 "def _impl(ctx):",
128 " pass",
129 "exec_rule = rule(implementation = _impl, executable = True)",
130 "non_exec_rule = rule(implementation = _impl)");
lberkiaea56b32017-05-30 12:35:33 +0200131 assertThat(getRuleClass("exec_rule").hasAttr("args", Type.STRING_LIST)).isTrue();
132 assertThat(getRuleClass("non_exec_rule").hasAttr("args", Type.STRING_LIST)).isFalse();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000133 }
134
135 private RuleClass getRuleClass(String name) throws Exception {
tomlu72642a22017-10-18 06:23:14 +0200136 return ((SkylarkRuleFunction) lookup(name)).getRuleClass();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000137 }
138
139 private void registerDummyUserDefinedFunction() throws Exception {
John Cater56887932019-04-26 05:27:07 -0700140 eval("def impl():", " pass");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000141 }
142
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000143 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000144 public void testAttrWithOnlyType() throws Exception {
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000145 Attribute attr = buildAttribute("a1", "attr.string_list()");
lberkiaea56b32017-05-30 12:35:33 +0200146 assertThat(attr.getType()).isEqualTo(Type.STRING_LIST);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000147 }
148
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000149 private Attribute buildAttribute(String name, String... lines) throws Exception {
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000150 String[] strings = lines.clone();
151 strings[strings.length - 1] = String.format("%s = %s", name, strings[strings.length - 1]);
152 evalAndExport(strings);
153 Descriptor lookup = (Descriptor) ev.lookup(name);
154 return lookup != null ? lookup.build(name) : null;
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000155 }
156
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000157 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000158 public void testOutputListAttr() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000159 Attribute attr = buildAttribute("a1", "attr.output_list()");
lberkiaea56b32017-05-30 12:35:33 +0200160 assertThat(attr.getType()).isEqualTo(BuildType.OUTPUT_LIST);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000161 }
162
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000163 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000164 public void testIntListAttr() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000165 Attribute attr = buildAttribute("a1", "attr.int_list()");
lberkiaea56b32017-05-30 12:35:33 +0200166 assertThat(attr.getType()).isEqualTo(Type.INTEGER_LIST);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000167 }
168
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000169 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000170 public void testOutputAttr() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000171 Attribute attr = buildAttribute("a1", "attr.output()");
lberkiaea56b32017-05-30 12:35:33 +0200172 assertThat(attr.getType()).isEqualTo(BuildType.OUTPUT);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000173 }
174
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000175 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000176 public void testStringDictAttr() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000177 Attribute attr = buildAttribute("a1", "attr.string_dict(default = {'a': 'b'})");
lberkiaea56b32017-05-30 12:35:33 +0200178 assertThat(attr.getType()).isEqualTo(Type.STRING_DICT);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000179 }
180
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000181 @Test
Francois-Rene Rideau028e3192015-10-29 14:26:59 +0000182 public void testStringListDictAttr() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000183 Attribute attr = buildAttribute("a1", "attr.string_list_dict(default = {'a': ['b', 'c']})");
lberkiaea56b32017-05-30 12:35:33 +0200184 assertThat(attr.getType()).isEqualTo(Type.STRING_LIST_DICT);
Francois-Rene Rideau028e3192015-10-29 14:26:59 +0000185 }
186
187 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000188 public void testAttrAllowedFileTypesAnyFile() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000189 Attribute attr = buildAttribute("a1", "attr.label_list(allow_files = True)");
lberkiaea56b32017-05-30 12:35:33 +0200190 assertThat(attr.getAllowedFileTypesPredicate()).isEqualTo(FileTypeSet.ANY_FILE);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000191 }
192
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000193 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000194 public void testAttrAllowedFileTypesWrongType() throws Exception {
195 checkErrorContains(
Laurent Le Brun2445df12016-05-11 14:36:40 +0000196 "allow_files should be a boolean or a string list",
197 "attr.label_list(allow_files = 18)");
198 }
199
200 @Test
cparsonse5068582018-07-16 13:33:33 -0700201 public void testDisableDeprecatedParams() throws Exception {
laurentlb6659b4c2019-02-18 07:23:36 -0800202 ev =
203 createEvaluationTestCase(
204 StarlarkSemantics.DEFAULT_SEMANTICS
205 .toBuilder()
206 .incompatibleDisableDeprecatedAttrParams(true)
207 .build());
cparsonse5068582018-07-16 13:33:33 -0700208 ev.initialize();
209
210 // Verify 'single_file' deprecation.
211 EvalException expected =
212 assertThrows(EvalException.class, () -> eval("attr.label(single_file = True)"));
213 assertThat(expected).hasMessageThat().contains(
214 "'single_file' is no longer supported. use allow_single_file instead.");
215 Attribute attr = buildAttribute("a1", "attr.label(allow_single_file = ['.xml'])");
216 assertThat(attr.isSingleArtifact()).isTrue();
217
218 // Verify 'non_empty' deprecation.
219 expected =
220 assertThrows(EvalException.class, () -> eval("attr.string_list(non_empty=True)"));
221 assertThat(expected).hasMessageThat().contains(
222 "'non_empty' is no longer supported. use allow_empty instead.");
223 attr = buildAttribute("a2", "attr.string_list(allow_empty=False)");
224 assertThat(attr.isNonEmpty()).isTrue();
225 }
226
227 @Test
Laurent Le Brun50681c12016-07-05 10:08:54 +0000228 public void testAttrAllowedSingleFileTypesWrongType() throws Exception {
229 checkErrorContains(
230 "allow_single_file should be a boolean or a string list",
231 "attr.label(allow_single_file = 18)");
232 }
233
234 @Test
Laurent Le Brun2445df12016-05-11 14:36:40 +0000235 public void testAttrWithList() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000236 Attribute attr = buildAttribute("a1", "attr.label_list(allow_files = ['.xml'])");
lberkiaea56b32017-05-30 12:35:33 +0200237 assertThat(attr.getAllowedFileTypesPredicate().apply("a.xml")).isTrue();
238 assertThat(attr.getAllowedFileTypesPredicate().apply("a.txt")).isFalse();
239 assertThat(attr.isSingleArtifact()).isFalse();
Laurent Le Brun50681c12016-07-05 10:08:54 +0000240 }
241
242 @Test
243 public void testAttrSingleFileWithList() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000244 Attribute attr = buildAttribute("a1", "attr.label(allow_single_file = ['.xml'])");
lberkiaea56b32017-05-30 12:35:33 +0200245 assertThat(attr.getAllowedFileTypesPredicate().apply("a.xml")).isTrue();
246 assertThat(attr.getAllowedFileTypesPredicate().apply("a.txt")).isFalse();
247 assertThat(attr.isSingleArtifact()).isTrue();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000248 }
249
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000250 private static SkylarkProviderIdentifier legacy(String legacyId) {
251 return SkylarkProviderIdentifier.forLegacy(legacyId);
252 }
253
254 private static SkylarkProviderIdentifier declared(String exportedName) {
255 return SkylarkProviderIdentifier.forKey(
dslomovde965ac2017-07-31 21:07:51 +0200256 new SkylarkProvider.SkylarkKey(FAKE_LABEL, exportedName));
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000257 }
258
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000259 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000260 public void testAttrWithProviders() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000261 Attribute attr =
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000262 buildAttribute("a1",
263 "b = provider()",
264 "attr.label_list(allow_files = True, providers = ['a', b])");
dslomovc13bb392017-08-02 23:29:54 +0200265 assertThat(attr.getRequiredProviders().isSatisfiedBy(set(legacy("a"), declared("b")))).isTrue();
266 assertThat(attr.getRequiredProviders().isSatisfiedBy(set(legacy("a")))).isFalse();
267 }
268
269 @Test
270 public void testAttrWithProvidersOneEmpty() throws Exception {
271 Attribute attr =
272 buildAttribute(
273 "a1",
274 "b = provider()",
275 "attr.label_list(allow_files = True, providers = [['a', b],[]])");
276 assertThat(attr.getRequiredProviders().acceptsAny()).isTrue();
dslomovc32e1b12017-07-31 19:23:52 +0200277 }
278
dslomovc32e1b12017-07-31 19:23:52 +0200279 @Test
Yun Peng83fbb91a2016-02-23 18:37:44 +0000280 public void testAttrWithProvidersList() throws Exception {
281 Attribute attr =
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000282 buildAttribute("a1",
283 "b = provider()",
284 "attr.label_list(allow_files = True, providers = [['a', b], ['c']])");
dslomovc13bb392017-08-02 23:29:54 +0200285 assertThat(attr.getRequiredProviders().isSatisfiedBy(set(legacy("a"), declared("b")))).isTrue();
286 assertThat(attr.getRequiredProviders().isSatisfiedBy(set(legacy("c")))).isTrue();
287 assertThat(attr.getRequiredProviders().isSatisfiedBy(set(legacy("a")))).isFalse();
288 }
289
290 private static AdvertisedProviderSet set(SkylarkProviderIdentifier... ids) {
291 AdvertisedProviderSet.Builder builder = AdvertisedProviderSet.builder();
292 for (SkylarkProviderIdentifier id : ids) {
293 builder.addSkylark(id);
294 }
295 return builder.build();
Yun Peng83fbb91a2016-02-23 18:37:44 +0000296 }
297
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000298 private void checkAttributeError(String expectedMessage, String... lines) throws Exception {
299 ev.setFailFast(false);
300 buildAttribute("fakeAttribute", lines);
301 MoreAsserts.assertContainsEvent(ev.getEventCollector(), expectedMessage);
302 }
303
Yun Peng83fbb91a2016-02-23 18:37:44 +0000304 @Test
305 public void testAttrWithWrongProvidersList() throws Exception {
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000306 checkAttributeError(
307 "element in 'providers' is of unexpected type. Either all elements should be providers,"
308 + " or all elements should be lists of providers,"
309 + " but got list with an element of type int.",
Yun Pengda9410c2016-03-18 21:14:51 +0000310 "attr.label_list(allow_files = True, providers = [['a', 1], ['c']])");
Yun Peng83fbb91a2016-02-23 18:37:44 +0000311
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000312 checkAttributeError(
313 "element in 'providers' is of unexpected type. Either all elements should be providers,"
314 + " or all elements should be lists of providers,"
315 + " but got an element of type string.",
316 "b = provider()",
317 "attr.label_list(allow_files = True, providers = [['a', b], 'c'])");
318
319 checkAttributeError(
320 "element in 'providers' is of unexpected type. Either all elements should be providers,"
321 + " or all elements should be lists of providers,"
322 + " but got an element of type string.",
323 "c = provider()",
324 "attr.label_list(allow_files = True, providers = [['a', b], c])");
Yun Peng83fbb91a2016-02-23 18:37:44 +0000325 }
326
327 @Test
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000328 public void testLabelListWithAspects() throws Exception {
Dmitry Lomov950310f2017-03-01 17:45:12 +0000329 evalAndExport(
Yun Pengda9410c2016-03-18 21:14:51 +0000330 "def _impl(target, ctx):",
331 " pass",
332 "my_aspect = aspect(implementation = _impl)",
Dmitry Lomov950310f2017-03-01 17:45:12 +0000333 "a = attr.label_list(aspects = [my_aspect])");
334 SkylarkAttr.Descriptor attr = (SkylarkAttr.Descriptor) ev.lookup("a");
cparsons0d55f4c2017-12-20 14:49:13 -0800335 SkylarkDefinedAspect aspect = (SkylarkDefinedAspect) ev.lookup("my_aspect");
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000336 assertThat(aspect).isNotNull();
Dmitry Lomov950310f2017-03-01 17:45:12 +0000337 assertThat(attr.build("xxx").getAspectClasses()).containsExactly(aspect.getAspectClass());
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000338 }
339
340 @Test
Dmitry Lomov950310f2017-03-01 17:45:12 +0000341 public void testLabelWithAspects() throws Exception {
342 evalAndExport(
343 "def _impl(target, ctx):",
344 " pass",
345 "my_aspect = aspect(implementation = _impl)",
346 "a = attr.label(aspects = [my_aspect])");
347 SkylarkAttr.Descriptor attr = (SkylarkAttr.Descriptor) ev.lookup("a");
cparsons0d55f4c2017-12-20 14:49:13 -0800348 SkylarkDefinedAspect aspect = (SkylarkDefinedAspect) ev.lookup("my_aspect");
Dmitry Lomov950310f2017-03-01 17:45:12 +0000349 assertThat(aspect).isNotNull();
350 assertThat(attr.build("xxx").getAspectClasses()).containsExactly(aspect.getAspectClass());
351 }
352
Dmitry Lomov950310f2017-03-01 17:45:12 +0000353 @Test
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000354 public void testLabelListWithAspectsError() throws Exception {
355 checkErrorContains(
Laurent Le Brunc31f3512016-12-29 21:41:33 +0000356 "expected type 'Aspect' for 'aspects' element but got type 'int' instead",
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000357 "def _impl(target, ctx):",
358 " pass",
359 "my_aspect = aspect(implementation = _impl)",
Laurent Le Brunc31f3512016-12-29 21:41:33 +0000360 "attr.label_list(aspects = [my_aspect, 123])");
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000361 }
362
363 @Test
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000364 public void testAspectExtraDeps() throws Exception {
365 evalAndExport(
366 "def _impl(target, ctx):",
367 " pass",
368 "my_aspect = aspect(_impl,",
Dmitry Lomovace678e2015-12-16 15:10:20 +0000369 " attrs = { '_extra_deps' : attr.label(default = Label('//foo/bar:baz')) }",
370 ")");
cparsons0d55f4c2017-12-20 14:49:13 -0800371 SkylarkDefinedAspect aspect = (SkylarkDefinedAspect) ev.lookup("my_aspect");
Googler74558fc2016-05-06 21:47:42 +0000372 Attribute attribute = Iterables.getOnlyElement(aspect.getAttributes());
373 assertThat(attribute.getName()).isEqualTo("$extra_deps");
374 assertThat(attribute.getDefaultValue(null))
dannarkda327bb2018-06-22 11:44:27 -0700375 .isEqualTo(
376 Label.parseAbsolute(
377 "//foo/bar:baz",
378 /* defaultToMain= */ false,
379 /* repositoryMapping= */ ImmutableMap.of()));
Dmitry Lomovace678e2015-12-16 15:10:20 +0000380 }
381
382 @Test
Googler74558fc2016-05-06 21:47:42 +0000383 public void testAspectParameter() throws Exception {
384 evalAndExport(
Dmitry Lomovace678e2015-12-16 15:10:20 +0000385 "def _impl(target, ctx):",
386 " pass",
387 "my_aspect = aspect(_impl,",
Googler74558fc2016-05-06 21:47:42 +0000388 " attrs = { 'param' : attr.string(values=['a', 'b']) }",
Dmitry Lomovace678e2015-12-16 15:10:20 +0000389 ")");
cparsons0d55f4c2017-12-20 14:49:13 -0800390 SkylarkDefinedAspect aspect = (SkylarkDefinedAspect) ev.lookup("my_aspect");
Googler74558fc2016-05-06 21:47:42 +0000391 Attribute attribute = Iterables.getOnlyElement(aspect.getAttributes());
392 assertThat(attribute.getName()).isEqualTo("param");
393 }
Michajlo Matijkiw6d471412016-08-09 20:35:45 +0000394
Googler74558fc2016-05-06 21:47:42 +0000395 @Test
396 public void testAspectParameterRequiresValues() throws Exception {
397 checkErrorContains(
398 "Aspect parameter attribute 'param' must have type 'string' and use the 'values' "
399 + "restriction.",
400 "def _impl(target, ctx):",
401 " pass",
402 "my_aspect = aspect(_impl,",
403 " attrs = { 'param' : attr.string(default = 'c') }",
404 ")");
405 }
406
407 @Test
408 public void testAspectParameterBadType() throws Exception {
409 checkErrorContains(
410 "Aspect parameter attribute 'param' must have type 'string' and use the 'values' "
411 + "restriction.",
412 "def _impl(target, ctx):",
413 " pass",
414 "my_aspect = aspect(_impl,",
415 " attrs = { 'param' : attr.label(default = Label('//foo/bar:baz')) }",
416 ")");
417 }
418
419 @Test
420 public void testAspectParameterAndExtraDeps() throws Exception {
421 evalAndExport(
422 "def _impl(target, ctx):",
423 " pass",
424 "my_aspect = aspect(_impl,",
425 " attrs = { 'param' : attr.string(values=['a', 'b']),",
426 " '_extra' : attr.label(default = Label('//foo/bar:baz')) }",
427 ")");
cparsons0d55f4c2017-12-20 14:49:13 -0800428 SkylarkDefinedAspect aspect = (SkylarkDefinedAspect) ev.lookup("my_aspect");
Googler74558fc2016-05-06 21:47:42 +0000429 assertThat(aspect.getAttributes()).hasSize(2);
430 assertThat(aspect.getParamAttributes()).containsExactly("param");
Dmitry Lomovace678e2015-12-16 15:10:20 +0000431 }
432
433 @Test
434 public void testAspectNoDefaultValueAttribute() throws Exception {
435 checkErrorContains(
436 "Aspect attribute '_extra_deps' has no default value",
437 "def _impl(target, ctx):",
438 " pass",
439 "my_aspect = aspect(_impl,",
440 " attrs = { '_extra_deps' : attr.label() }",
441 ")");
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000442 }
443
444 @Test
John Cater9a8d16e2017-07-05 16:12:07 -0400445 public void testAspectAddToolchain() throws Exception {
446 scratch.file("test/BUILD", "toolchain_type(name = 'my_toolchain_type')");
447 evalAndExport(
448 "def _impl(ctx): pass", "a1 = aspect(_impl, toolchains=['//test:my_toolchain_type'])");
cparsons0d55f4c2017-12-20 14:49:13 -0800449 SkylarkDefinedAspect a = (SkylarkDefinedAspect) lookup("a1");
John Cater9a8d16e2017-07-05 16:12:07 -0400450 assertThat(a.getRequiredToolchains()).containsExactly(makeLabel("//test:my_toolchain_type"));
451 }
452
453 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000454 public void testNonLabelAttrWithProviders() throws Exception {
455 checkErrorContains(
cparsons03035142019-01-15 13:35:22 -0800456 "unexpected keyword 'providers', for call to method "
457 + "string(default = '', doc = '', mandatory = False, values = []) "
458 + "of 'attr (a language module)'",
cparsons0446f992018-03-22 14:49:22 -0700459 "attr.string(providers = ['a'])");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000460 }
461
cparsonse2d200f2018-03-06 16:15:11 -0800462 private static final RuleClass.ConfiguredTargetFactory<Object, Object, Exception>
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000463 DUMMY_CONFIGURED_TARGET_FACTORY =
dslomovc13bb392017-08-02 23:29:54 +0200464 ruleContext -> {
465 throw new IllegalStateException();
466 };
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000467
468 private RuleClass ruleClass(String name) {
469 return new RuleClass.Builder(name, RuleClassType.NORMAL, false)
470 .factory(DUMMY_CONFIGURED_TARGET_FACTORY)
471 .add(Attribute.attr("tags", Type.STRING_LIST))
472 .build();
473 }
vladmos9c787fa2017-07-04 11:45:22 -0400474
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000475 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000476 public void testAttrAllowedRuleClassesSpecificRuleClasses() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000477 Attribute attr = buildAttribute("a",
478 "attr.label_list(allow_rules = ['java_binary'], allow_files = True)");
lberkiaea56b32017-05-30 12:35:33 +0200479 assertThat(attr.getAllowedRuleClassesPredicate().apply(ruleClass("java_binary"))).isTrue();
480 assertThat(attr.getAllowedRuleClassesPredicate().apply(ruleClass("genrule"))).isFalse();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000481 }
vladmos9c787fa2017-07-04 11:45:22 -0400482
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000483 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000484 public void testAttrDefaultValue() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000485 Attribute attr = buildAttribute("a1", "attr.string(default = 'some value')");
juliexxia84d1a662018-12-26 14:07:04 -0800486 assertThat(attr.getDefaultValueUnchecked()).isEqualTo("some value");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000487 }
488
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000489 @Test
vladmos9c787fa2017-07-04 11:45:22 -0400490 public void testLabelAttrDefaultValueAsString() throws Exception {
491 Attribute sligleAttr = buildAttribute("a1", "attr.label(default = '//foo:bar')");
juliexxia84d1a662018-12-26 14:07:04 -0800492 assertThat(sligleAttr.getDefaultValueUnchecked())
dannarkda327bb2018-06-22 11:44:27 -0700493 .isEqualTo(
494 Label.parseAbsolute(
495 "//foo:bar",
496 /* defaultToMain= */ false,
497 /* repositoryMapping= */ ImmutableMap.of()));
vladmos9c787fa2017-07-04 11:45:22 -0400498
499 Attribute listAttr =
500 buildAttribute("a2", "attr.label_list(default = ['//foo:bar', '//bar:foo'])");
juliexxia84d1a662018-12-26 14:07:04 -0800501 assertThat(listAttr.getDefaultValueUnchecked())
vladmos9c787fa2017-07-04 11:45:22 -0400502 .isEqualTo(
503 ImmutableList.of(
dannarkda327bb2018-06-22 11:44:27 -0700504 Label.parseAbsolute(
505 "//foo:bar",
506 /* defaultToMain= */ false,
507 /* repositoryMapping= */ ImmutableMap.of()),
508 Label.parseAbsolute(
509 "//bar:foo",
510 /* defaultToMain= */ false,
511 /*repositoryMapping= */ ImmutableMap.of())));
vladmos9c787fa2017-07-04 11:45:22 -0400512
513 Attribute dictAttr =
514 buildAttribute("a3", "attr.label_keyed_string_dict(default = {'//foo:bar': 'my value'})");
juliexxia84d1a662018-12-26 14:07:04 -0800515 assertThat(dictAttr.getDefaultValueUnchecked())
dannarkda327bb2018-06-22 11:44:27 -0700516 .isEqualTo(
517 ImmutableMap.of(
518 Label.parseAbsolute(
519 "//foo:bar",
520 /* defaultToMain= */ false,
521 /* repositoryMapping= */ ImmutableMap.of()),
522 "my value"));
vladmos9c787fa2017-07-04 11:45:22 -0400523 }
524
525 @Test
526 public void testLabelAttrDefaultValueAsStringBadValue() throws Exception {
527 checkErrorContains(
528 "invalid label '/foo:bar' in parameter 'default' of attribute 'label': "
janakrd3fe5e72018-03-30 12:49:12 -0700529 + "invalid target name '/foo:bar'",
vladmos9c787fa2017-07-04 11:45:22 -0400530 "attr.label(default = '/foo:bar')");
531
532 checkErrorContains(
533 "invalid label '/bar:foo' in element 1 of parameter 'default' of attribute "
janakrd3fe5e72018-03-30 12:49:12 -0700534 + "'label_list': invalid target name '/bar:foo'",
vladmos9c787fa2017-07-04 11:45:22 -0400535 "attr.label_list(default = ['//foo:bar', '/bar:foo'])");
536
537 checkErrorContains(
janakrd3fe5e72018-03-30 12:49:12 -0700538 "invalid label '/bar:foo' in dict key element: invalid target name '/bar:foo'",
vladmos9c787fa2017-07-04 11:45:22 -0400539 "attr.label_keyed_string_dict(default = {'//foo:bar': 'a', '/bar:foo': 'b'})");
540 }
541
542 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000543 public void testAttrDefaultValueBadType() throws Exception {
544 checkErrorContains(
cparsons03035142019-01-15 13:35:22 -0800545 "expected value of type 'string' for parameter 'default', for call to method "
546 + "string(default = '', doc = '', mandatory = False, values = []) "
547 + "of 'attr (a language module)'",
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000548 "attr.string(default = 1)");
549 }
550
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000551 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000552 public void testAttrMandatory() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000553 Attribute attr = buildAttribute("a1", "attr.string(mandatory=True)");
lberkiaea56b32017-05-30 12:35:33 +0200554 assertThat(attr.isMandatory()).isTrue();
555 assertThat(attr.isNonEmpty()).isFalse();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000556 }
557
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000558 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000559 public void testAttrNonEmpty() throws Exception {
laurentlb140c04b2019-05-21 06:56:28 -0700560 ev =
561 createEvaluationTestCase(
562 StarlarkSemantics.DEFAULT_SEMANTICS.toBuilder()
563 .incompatibleDisableDeprecatedAttrParams(false)
564 .build());
565 ev.initialize();
566
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000567 Attribute attr = buildAttribute("a1", "attr.string_list(non_empty=True)");
lberkiaea56b32017-05-30 12:35:33 +0200568 assertThat(attr.isNonEmpty()).isTrue();
569 assertThat(attr.isMandatory()).isFalse();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000570 }
571
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000572 @Test
Laurent Le Brunbc16f722016-07-06 13:40:24 +0000573 public void testAttrAllowEmpty() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000574 Attribute attr = buildAttribute("a1", "attr.string_list(allow_empty=False)");
lberkiaea56b32017-05-30 12:35:33 +0200575 assertThat(attr.isNonEmpty()).isTrue();
576 assertThat(attr.isMandatory()).isFalse();
Laurent Le Brunbc16f722016-07-06 13:40:24 +0000577 }
578
579 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000580 public void testAttrBadKeywordArguments() throws Exception {
581 checkErrorContains(
cparsons03035142019-01-15 13:35:22 -0800582 "unexpected keyword 'bad_keyword', for call to method "
583 + "string(default = '', doc = '', mandatory = False, values = []) of "
584 + "'attr (a language module)'",
cparsons0446f992018-03-22 14:49:22 -0700585 "attr.string(bad_keyword = '')");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000586 }
587
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000588 @Test
Laurent Le Brunc4ddf6f2016-07-04 13:38:38 +0000589 public void testAttrCfg() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000590 Attribute attr = buildAttribute("a1", "attr.label(cfg = 'host', allow_files = True)");
jcaterb44167f2019-04-02 12:06:26 -0700591 assertThat(attr.getTransitionFactory().isHost()).isTrue();
Laurent Le Brunc4ddf6f2016-07-04 13:38:38 +0000592 }
593
594 @Test
Vladimir Moskva5a510772016-11-23 19:03:38 +0000595 public void testAttrCfgTarget() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000596 Attribute attr = buildAttribute("a1", "attr.label(cfg = 'target', allow_files = True)");
John Cater5adcd3e2019-03-28 10:14:32 -0700597 assertThat(NoTransition.isInstance(attr.getTransitionFactory())).isTrue();
Vladimir Moskva5a510772016-11-23 19:03:38 +0000598 }
599
600 @Test
gregcebceecab2018-06-27 17:44:45 -0700601 public void incompatibleDataTransition() throws Exception {
gregcebceecab2018-06-27 17:44:45 -0700602 EvalException expected =
603 assertThrows(EvalException.class, () -> eval("attr.label(cfg = 'data')"));
laurentlbd576ed02019-03-26 15:35:06 -0700604 assertThat(expected).hasMessageThat().contains("cfg must be either 'host' or 'target'");
gregcebceecab2018-06-27 17:44:45 -0700605 }
606
607 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000608 public void testAttrValues() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000609 Attribute attr = buildAttribute("a1", "attr.string(values = ['ab', 'cd'])");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000610 PredicateWithMessage<Object> predicate = attr.getAllowedValues();
611 assertThat(predicate.apply("ab")).isTrue();
612 assertThat(predicate.apply("xy")).isFalse();
613 }
614
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000615 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000616 public void testAttrIntValues() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000617 Attribute attr = buildAttribute("a1", "attr.int(values = [1, 2])");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000618 PredicateWithMessage<Object> predicate = attr.getAllowedValues();
619 assertThat(predicate.apply(2)).isTrue();
620 assertThat(predicate.apply(3)).isFalse();
621 }
622
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000623 @Test
allevato45b79e52017-07-07 21:40:50 +0200624 public void testAttrDoc() throws Exception {
625 // We don't actually store the doc in the attr definition; right now it's just meant to be
626 // extracted by documentation generating tools. So we don't have anything to assert and we just
627 // verify that no exceptions were thrown from building them.
628 buildAttribute("a1", "attr.bool(doc='foo')");
629 buildAttribute("a2", "attr.int(doc='foo')");
630 buildAttribute("a3", "attr.int_list(doc='foo')");
631 buildAttribute("a4", "attr.label(doc='foo')");
632 buildAttribute("a5", "attr.label_keyed_string_dict(doc='foo')");
633 buildAttribute("a6", "attr.label_list(doc='foo')");
allevato45b79e52017-07-07 21:40:50 +0200634 buildAttribute("a8", "attr.output(doc='foo')");
635 buildAttribute("a9", "attr.output_list(doc='foo')");
636 buildAttribute("a10", "attr.string(doc='foo')");
637 buildAttribute("a11", "attr.string_dict(doc='foo')");
638 buildAttribute("a12", "attr.string_list(doc='foo')");
639 buildAttribute("a13", "attr.string_list_dict(doc='foo')");
640 }
641
642 @Test
laurentlbd8d37762018-10-26 14:08:33 -0700643 public void testNoAttrLicense() throws Exception {
laurentlbd8d37762018-10-26 14:08:33 -0700644 EvalException expected = assertThrows(EvalException.class, () -> eval("attr.license()"));
645 assertThat(expected)
646 .hasMessageThat()
647 .contains("type 'attr (a language module)' has no method license()");
648 }
649
650 @Test
allevato45b79e52017-07-07 21:40:50 +0200651 public void testAttrDocValueBadType() throws Exception {
652 checkErrorContains(
cparsons03035142019-01-15 13:35:22 -0800653 "expected value of type 'string' for parameter 'doc', for call to method "
654 + "string(default = '', doc = '', mandatory = False, values = []) "
655 + "of 'attr (a language module)'",
allevato45b79e52017-07-07 21:40:50 +0200656 "attr.string(doc = 1)");
657 }
658
659 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000660 public void testRuleImplementation() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000661 evalAndExport("def impl(ctx): return None", "rule1 = rule(impl)");
tomlu72642a22017-10-18 06:23:14 +0200662 RuleClass c = ((SkylarkRuleFunction) lookup("rule1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200663 assertThat(c.getConfiguredTargetFunction().getName()).isEqualTo("impl");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000664 }
665
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000666 @Test
allevato45b79e52017-07-07 21:40:50 +0200667 public void testRuleDoc() throws Exception {
668 evalAndExport("def impl(ctx): return None", "rule1 = rule(impl, doc='foo')");
669 }
670
671 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000672 public void testLateBoundAttrWorksWithOnlyLabel() throws Exception {
673 checkEvalError(
cparsons03035142019-01-15 13:35:22 -0800674 "expected value of type 'string' for parameter 'default', for call to method "
675 + "string(default = '', doc = '', mandatory = False, values = []) "
676 + "of 'attr (a language module)'",
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000677 "def attr_value(cfg): return 'a'",
678 "attr.string(default=attr_value)");
679 }
680
Dmitry Lomov7b599452015-11-26 10:07:32 +0000681 private static final Label FAKE_LABEL = Label.parseAbsoluteUnchecked("//fake/label.bzl");
682
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000683 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000684 public void testRuleAddAttribute() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000685 evalAndExport("def impl(ctx): return None", "r1 = rule(impl, attrs={'a1': attr.string()})");
tomlu72642a22017-10-18 06:23:14 +0200686 RuleClass c = ((SkylarkRuleFunction) lookup("r1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200687 assertThat(c.hasAttr("a1", Type.STRING)).isTrue();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000688 }
689
Dmitry Lomov7b599452015-11-26 10:07:32 +0000690 protected void evalAndExport(String... lines) throws Exception {
Dmitry Lomov950310f2017-03-01 17:45:12 +0000691 BuildFileAST buildFileAST = BuildFileAST.parseAndValidateSkylarkString(
692 ev.getEnvironment(), lines);
693 SkylarkImportLookupFunction.execAndExport(
694 buildFileAST, FAKE_LABEL, ev.getEventHandler(), ev.getEnvironment());
Dmitry Lomov7b599452015-11-26 10:07:32 +0000695 }
696
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000697 @Test
Jon Brandveinded4fbb2017-01-18 22:21:04 +0000698 public void testExportAliasedName() throws Exception {
699 // When there are multiple names aliasing the same SkylarkExportable, the first one to be
700 // declared should be used. Make sure we're not using lexicographical order, hash order,
701 // non-deterministic order, or anything else.
702 evalAndExport(
703 "def _impl(ctx): pass",
704 "d = rule(implementation = _impl)",
705 "a = d",
706 // Having more names improves the chance that non-determinism will be caught.
707 "b = d",
708 "c = d",
709 "e = d",
710 "f = d",
711 "foo = d",
712 "bar = d",
713 "baz = d",
714 "x = d",
715 "y = d",
716 "z = d");
tomlu72642a22017-10-18 06:23:14 +0200717 String dName = ((SkylarkRuleFunction) lookup("d")).getRuleClass().getName();
718 String fooName = ((SkylarkRuleFunction) lookup("foo")).getRuleClass().getName();
Jon Brandveinded4fbb2017-01-18 22:21:04 +0000719 assertThat(dName).isEqualTo("d");
720 assertThat(fooName).isEqualTo("d");
721 }
722
723 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000724 public void testOutputToGenfiles() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000725 evalAndExport("def impl(ctx): pass", "r1 = rule(impl, output_to_genfiles=True)");
tomlu72642a22017-10-18 06:23:14 +0200726 RuleClass c = ((SkylarkRuleFunction) lookup("r1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200727 assertThat(c.hasBinaryOutput()).isFalse();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000728 }
729
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000730 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000731 public void testRuleAddMultipleAttributes() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000732 evalAndExport(
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000733 "def impl(ctx): return None",
734 "r1 = rule(impl,",
735 " attrs = {",
736 " 'a1': attr.label_list(allow_files=True),",
737 " 'a2': attr.int()",
738 "})");
tomlu72642a22017-10-18 06:23:14 +0200739 RuleClass c = ((SkylarkRuleFunction) lookup("r1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200740 assertThat(c.hasAttr("a1", BuildType.LABEL_LIST)).isTrue();
741 assertThat(c.hasAttr("a2", Type.INTEGER)).isTrue();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000742 }
Taras Tsugrii7fe70472018-07-25 13:58:02 -0700743
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000744 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000745 public void testRuleAttributeFlag() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000746 evalAndExport(
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000747 "def impl(ctx): return None",
748 "r1 = rule(impl, attrs = {'a1': attr.string(mandatory=True)})");
tomlu72642a22017-10-18 06:23:14 +0200749 RuleClass c = ((SkylarkRuleFunction) lookup("r1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200750 assertThat(c.getAttributeByName("a1").isMandatory()).isTrue();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000751 }
752
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000753 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000754 public void testRuleOutputs() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000755 evalAndExport(
756 "def impl(ctx): return None",
757 "r1 = rule(impl, outputs = {'a': 'a.txt'})");
tomlu72642a22017-10-18 06:23:14 +0200758 RuleClass c = ((SkylarkRuleFunction) lookup("r1")).getRuleClass();
Michajlo Matijkiw6d471412016-08-09 20:35:45 +0000759 ImplicitOutputsFunction function = c.getDefaultImplicitOutputsFunction();
vladmos076977e2017-12-02 14:15:32 -0800760 assertThat(function.getImplicitOutputs(ev.getEventHandler(), null)).containsExactly("a.txt");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000761 }
762
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000763 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000764 public void testRuleUnknownKeyword() throws Exception {
765 registerDummyUserDefinedFunction();
766 checkErrorContains(
cparsons03035142019-01-15 13:35:22 -0800767 "unexpected keyword 'bad_keyword', for call to function rule(",
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000768 "rule(impl, bad_keyword = 'some text')");
769 }
770
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000771 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000772 public void testRuleImplementationMissing() throws Exception {
773 checkErrorContains(
cparsons03035142019-01-15 13:35:22 -0800774 "parameter 'implementation' has no default value, for call to function rule(",
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000775 "rule(attrs = {})");
776 }
777
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000778 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000779 public void testRuleBadTypeForAdd() throws Exception {
780 registerDummyUserDefinedFunction();
781 checkErrorContains(
cparsons1832ed32018-11-13 14:15:32 -0800782 "expected value of type 'dict or NoneType' for parameter 'attrs', "
cparsons03035142019-01-15 13:35:22 -0800783 + "for call to function rule(",
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000784 "rule(impl, attrs = 'some text')");
785 }
786
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000787 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000788 public void testRuleBadTypeInAdd() throws Exception {
789 registerDummyUserDefinedFunction();
790 checkErrorContains(
Laurent Le Brunc31f3512016-12-29 21:41:33 +0000791 "expected <String, Descriptor> type for 'attrs' but got <string, string> instead",
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000792 "rule(impl, attrs = {'a1': 'some text'})");
793 }
794
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000795 @Test
allevato45b79e52017-07-07 21:40:50 +0200796 public void testRuleBadTypeForDoc() throws Exception {
797 registerDummyUserDefinedFunction();
798 checkErrorContains(
cparsons03035142019-01-15 13:35:22 -0800799 "expected value of type 'string' for parameter 'doc', for call to function rule(",
allevato45b79e52017-07-07 21:40:50 +0200800 "rule(impl, doc = 1)");
801 }
802
803 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000804 public void testLabel() throws Exception {
805 Object result = evalRuleClassCode("Label('//foo/foo:foo')");
Dmitry Lomov7b599452015-11-26 10:07:32 +0000806 assertThat(result).isInstanceOf(Label.class);
lberkiaea56b32017-05-30 12:35:33 +0200807 assertThat(result.toString()).isEqualTo("//foo/foo:foo");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000808 }
809
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000810 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000811 public void testLabelSameInstance() throws Exception {
812 Object l1 = evalRuleClassCode("Label('//foo/foo:foo')");
813 // Implicitly creates a new pkgContext and environment, yet labels should be the same.
814 Object l2 = evalRuleClassCode("Label('//foo/foo:foo')");
cpovirka4d3da62019-05-02 14:27:33 -0700815 assertThat(l1).isSameInstanceAs(l2);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000816 }
817
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000818 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000819 public void testLabelNameAndPackage() throws Exception {
820 Object result = evalRuleClassCode("Label('//foo/bar:baz').name");
lberkiaea56b32017-05-30 12:35:33 +0200821 assertThat(result).isEqualTo("baz");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000822 // NB: implicitly creates a new pkgContext and environments, yet labels should be the same.
823 result = evalRuleClassCode("Label('//foo/bar:baz').package");
lberkiaea56b32017-05-30 12:35:33 +0200824 assertThat(result).isEqualTo("foo/bar");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000825 }
826
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000827 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000828 public void testRuleLabelDefaultValue() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000829 evalAndExport(
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000830 "def impl(ctx): return None\n"
831 + "r1 = rule(impl, attrs = {'a1': "
832 + "attr.label(default = Label('//foo:foo'), allow_files=True)})");
tomlu72642a22017-10-18 06:23:14 +0200833 RuleClass c = ((SkylarkRuleFunction) lookup("r1")).getRuleClass();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000834 Attribute a = c.getAttributeByName("a1");
juliexxia84d1a662018-12-26 14:07:04 -0800835 assertThat(a.getDefaultValueUnchecked()).isInstanceOf(Label.class);
836 assertThat(a.getDefaultValueUnchecked().toString()).isEqualTo("//foo:foo");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000837 }
838
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000839 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000840 public void testIntDefaultValue() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000841 evalAndExport(
842 "def impl(ctx): return None",
843 "r1 = rule(impl, attrs = {'a1': attr.int(default = 40+2)})");
tomlu72642a22017-10-18 06:23:14 +0200844 RuleClass c = ((SkylarkRuleFunction) lookup("r1")).getRuleClass();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000845 Attribute a = c.getAttributeByName("a1");
juliexxia84d1a662018-12-26 14:07:04 -0800846 assertThat(a.getDefaultValueUnchecked()).isEqualTo(42);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000847 }
848
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000849 @Test
laurentlb707acfe2018-04-13 06:09:30 -0700850 public void testFileTypeIsDisabled() throws Exception {
laurentlb6659b4c2019-02-18 07:23:36 -0800851 StarlarkSemantics semantics =
852 StarlarkSemantics.DEFAULT_SEMANTICS.toBuilder().incompatibleDisallowFileType(true).build();
laurentlb707acfe2018-04-13 06:09:30 -0700853 EvalException expected =
854 assertThrows(EvalException.class, () -> evalRuleClassCode(semantics, "FileType(['.css'])"));
855 assertThat(expected).hasMessageThat().contains("FileType function is not available.");
856 }
857
858 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000859 public void testRuleInheritsBaseRuleAttributes() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000860 evalAndExport("def impl(ctx): return None", "r1 = rule(impl)");
tomlu72642a22017-10-18 06:23:14 +0200861 RuleClass c = ((SkylarkRuleFunction) lookup("r1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200862 assertThat(c.hasAttr("tags", Type.STRING_LIST)).isTrue();
863 assertThat(c.hasAttr("visibility", BuildType.NODEP_LABEL_LIST)).isTrue();
864 assertThat(c.hasAttr("deprecation", Type.STRING)).isTrue();
865 assertThat(c.hasAttr(":action_listener", BuildType.LABEL_LIST))
866 .isTrue(); // required for extra actions
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000867 }
868
869 private void checkTextMessage(String from, String... lines) throws Exception {
fangismed97d842019-04-12 14:19:46 -0700870 String[] strings = lines.clone();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000871 Object result = evalRuleClassCode(from);
fangismed97d842019-04-12 14:19:46 -0700872 String expect = "";
873 if (strings.length > 0) {
874 expect = Joiner.on("\n").join(lines) + "\n";
875 }
876 assertThat(result).isEqualTo(expect);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000877 }
878
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000879 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000880 public void testSimpleTextMessagesBooleanFields() throws Exception {
881 checkTextMessage("struct(name=True).to_proto()", "name: true");
882 checkTextMessage("struct(name=False).to_proto()", "name: false");
883 }
884
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000885 @Test
cparsons07460fc2018-06-20 10:41:48 -0700886 public void testStructRestrictedOverrides() throws Exception {
887 checkErrorContains(
888 "cannot override built-in struct function 'to_json'",
889 "struct(to_json='foo')");
890
891 checkErrorContains(
892 "cannot override built-in struct function 'to_proto'",
893 "struct(to_proto='foo')");
894 }
895
896 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000897 public void testSimpleTextMessages() throws Exception {
898 checkTextMessage("struct(name='value').to_proto()", "name: \"value\"");
fangismed97d842019-04-12 14:19:46 -0700899 checkTextMessage("struct(name=[]).to_proto()"); // empty lines
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000900 checkTextMessage("struct(name=['a', 'b']).to_proto()", "name: \"a\"", "name: \"b\"");
901 checkTextMessage("struct(name=123).to_proto()", "name: 123");
902 checkTextMessage("struct(name=[1, 2, 3]).to_proto()", "name: 1", "name: 2", "name: 3");
903 checkTextMessage("struct(a=struct(b='b')).to_proto()", "a {", " b: \"b\"", "}");
904 checkTextMessage(
905 "struct(a=[struct(b='x'), struct(b='y')]).to_proto()",
906 "a {",
907 " b: \"x\"",
908 "}",
909 "a {",
910 " b: \"y\"",
911 "}");
912 checkTextMessage(
913 "struct(a=struct(b=struct(c='c'))).to_proto()", "a {", " b {", " c: \"c\"", " }", "}");
fangismed97d842019-04-12 14:19:46 -0700914 // dict to_proto tests
915 checkTextMessage("struct(name={}).to_proto()"); // empty lines
916 checkTextMessage(
917 "struct(name={'a': 'b'}).to_proto()", "name {", " key: \"a\"", " value: \"b\"", "}");
918 checkTextMessage(
919 "struct(name={'c': 'd', 'a': 'b'}).to_proto()",
920 "name {",
921 " key: \"c\"",
922 " value: \"d\"",
923 "}",
924 "name {",
925 " key: \"a\"",
926 " value: \"b\"",
927 "}");
928 checkTextMessage(
929 "struct(x=struct(y={'a': 1})).to_proto()",
930 "x {",
931 " y {",
932 " key: \"a\"",
933 " value: 1",
934 " }",
935 "}");
936 checkTextMessage(
937 "struct(name={'a': struct(b=1, c=2)}).to_proto()",
938 "name {",
939 " key: \"a\"",
940 " value {",
941 " b: 1",
942 " c: 2",
943 " }",
944 "}");
945 checkTextMessage(
946 "struct(name={'a': struct(b={4: 'z', 3: 'y'}, c=2)}).to_proto()",
947 "name {",
948 " key: \"a\"",
949 " value {",
950 " b {",
951 " key: 4",
952 " value: \"z\"",
953 " }",
954 " b {",
955 " key: 3",
956 " value: \"y\"",
957 " }",
958 " c: 2",
959 " }",
960 "}");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000961 }
962
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000963 @Test
Vladimir Moskva76e31d12016-12-05 16:28:37 +0000964 public void testProtoFieldsOrder() throws Exception {
965 checkTextMessage("struct(d=4, b=2, c=3, a=1).to_proto()", "a: 1", "b: 2", "c: 3", "d: 4");
966 }
967
968 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000969 public void testTextMessageEscapes() throws Exception {
970 checkTextMessage("struct(name='a\"b').to_proto()", "name: \"a\\\"b\"");
971 checkTextMessage("struct(name='a\\'b').to_proto()", "name: \"a'b\"");
972 checkTextMessage("struct(name='a\\nb').to_proto()", "name: \"a\\nb\"");
Googler4489aaf2016-06-17 15:17:37 +0000973
974 // struct(name="a\\\"b") -> name: "a\\\"b"
975 checkTextMessage("struct(name='a\\\\\\\"b').to_proto()", "name: \"a\\\\\\\"b\"");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000976 }
977
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000978 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000979 public void testTextMessageInvalidElementInListStructure() throws Exception {
980 checkErrorContains(
fangismed97d842019-04-12 14:19:46 -0700981 "Invalid text format, expected a struct, a dict, a string, a bool, or "
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000982 + "an int but got a list for list element in struct field 'a'",
983 "struct(a=[['b']]).to_proto()");
984 }
985
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000986 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000987 public void testTextMessageInvalidStructure() throws Exception {
988 checkErrorContains(
fangismed97d842019-04-12 14:19:46 -0700989 "Invalid text format, expected a struct, a dict, a string, a bool, or an int "
Vladimir Moskvacd12f772017-01-10 12:47:06 +0000990 + "but got a function for struct field 'a'",
991 "struct(a=rule).to_proto()");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000992 }
993
Erik Abair927f4592016-02-29 18:57:22 +0000994 private void checkJson(String from, String expected) throws Exception {
995 Object result = evalRuleClassCode(from);
lberkiaea56b32017-05-30 12:35:33 +0200996 assertThat(result).isEqualTo(expected);
Erik Abair927f4592016-02-29 18:57:22 +0000997 }
998
999 @Test
1000 public void testJsonBooleanFields() throws Exception {
1001 checkJson("struct(name=True).to_json()", "{\"name\":true}");
1002 checkJson("struct(name=False).to_json()", "{\"name\":false}");
1003 }
1004
1005 @Test
Taras Tsugrii7fe70472018-07-25 13:58:02 -07001006 public void testJsonDictFields() throws Exception {
1007 checkJson("struct(config={}).to_json()", "{\"config\":{}}");
1008 checkJson("struct(config={'key': 'value'}).to_json()", "{\"config\":{\"key\":\"value\"}}");
1009 checkErrorContains(
1010 "Keys must be a string but got a int for struct field 'config'",
1011 "struct(config={1:2}).to_json()");
1012 checkErrorContains(
1013 "Keys must be a string but got a int for dict value 'foo'",
1014 "struct(config={'foo':{1:2}}).to_json()");
1015 checkErrorContains(
1016 "Keys must be a string but got a bool for struct field 'config'",
1017 "struct(config={True: False}).to_json()");
1018 }
1019
1020 @Test
Erik Abair927f4592016-02-29 18:57:22 +00001021 public void testJsonEncoding() throws Exception {
1022 checkJson("struct(name='value').to_json()", "{\"name\":\"value\"}");
1023 checkJson("struct(name=['a', 'b']).to_json()", "{\"name\":[\"a\",\"b\"]}");
1024 checkJson("struct(name=123).to_json()", "{\"name\":123}");
1025 checkJson("struct(name=[1, 2, 3]).to_json()", "{\"name\":[1,2,3]}");
1026 checkJson("struct(a=struct(b='b')).to_json()", "{\"a\":{\"b\":\"b\"}}");
1027 checkJson("struct(a=[struct(b='x'), struct(b='y')]).to_json()",
1028 "{\"a\":[{\"b\":\"x\"},{\"b\":\"y\"}]}");
1029 checkJson("struct(a=struct(b=struct(c='c'))).to_json()",
1030 "{\"a\":{\"b\":{\"c\":\"c\"}}}");
1031 }
1032
1033 @Test
1034 public void testJsonEscapes() throws Exception {
1035 checkJson("struct(name='a\"b').to_json()", "{\"name\":\"a\\\"b\"}");
1036 checkJson("struct(name='a\\'b').to_json()", "{\"name\":\"a'b\"}");
Erik Abairec1f2b92016-03-01 00:45:33 +00001037 checkJson("struct(name='a\\\\b').to_json()", "{\"name\":\"a\\\\b\"}");
Erik Abair927f4592016-02-29 18:57:22 +00001038 checkJson("struct(name='a\\nb').to_json()", "{\"name\":\"a\\nb\"}");
Erik Abairec1f2b92016-03-01 00:45:33 +00001039 checkJson("struct(name='a\\rb').to_json()", "{\"name\":\"a\\rb\"}");
1040 checkJson("struct(name='a\\tb').to_json()", "{\"name\":\"a\\tb\"}");
Erik Abair927f4592016-02-29 18:57:22 +00001041 }
1042
1043 @Test
1044 public void testJsonNestedListStructure() throws Exception {
1045 checkJson("struct(a=[['b']]).to_json()", "{\"a\":[[\"b\"]]}");
1046 }
1047
1048 @Test
1049 public void testJsonInvalidStructure() throws Exception {
1050 checkErrorContains(
1051 "Invalid text format, expected a struct, a string, a bool, or an int but got a "
Vladimir Moskvacd12f772017-01-10 12:47:06 +00001052 + "function for struct field 'a'",
1053 "struct(a=rule).to_json()");
Erik Abair927f4592016-02-29 18:57:22 +00001054 }
1055
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +00001056 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001057 public void testLabelAttrWrongDefault() throws Exception {
1058 checkErrorContains(
cparsons03035142019-01-15 13:35:22 -08001059 "expected value of type 'Label or string or LateBoundDefault or function or NoneType' "
1060 + "for parameter 'default', for call to method label(",
vladmos9c787fa2017-07-04 11:45:22 -04001061 "attr.label(default = 123)");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001062 }
1063
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +00001064 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001065 public void testLabelGetRelative() throws Exception {
lberkiaea56b32017-05-30 12:35:33 +02001066 assertThat(eval("Label('//foo:bar').relative('baz')").toString()).isEqualTo("//foo:baz");
1067 assertThat(eval("Label('//foo:bar').relative('//baz:qux')").toString()).isEqualTo("//baz:qux");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001068 }
1069
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +00001070 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001071 public void testLabelGetRelativeSyntaxError() throws Exception {
1072 checkErrorContains(
Damien Martin-Guillerez934c1d52017-03-03 14:44:56 +00001073 "invalid target name 'bad//syntax': target names may not contain '//' path separators",
1074 "Label('//foo:bar').relative('bad//syntax')");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001075 }
Greg Estren223976c2016-02-04 22:40:56 +00001076
1077 @Test
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001078 public void testStructCreation() throws Exception {
1079 // TODO(fwe): cannot be handled by current testing suite
1080 eval("x = struct(a = 1, b = 2)");
1081 assertThat(lookup("x")).isInstanceOf(ClassObject.class);
1082 }
1083
1084 @Test
1085 public void testStructFields() throws Exception {
1086 // TODO(fwe): cannot be handled by current testing suite
1087 eval("x = struct(a = 1, b = 2)");
1088 ClassObject x = (ClassObject) lookup("x");
lberkiaea56b32017-05-30 12:35:33 +02001089 assertThat(x.getValue("a")).isEqualTo(1);
1090 assertThat(x.getValue("b")).isEqualTo(2);
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001091 }
1092
1093 @Test
Vladimir Moskvafdfa9882016-11-18 16:24:20 +00001094 public void testStructEquality() throws Exception {
lberkiaea56b32017-05-30 12:35:33 +02001095 assertThat((Boolean) eval("struct(a = 1, b = 2) == struct(b = 2, a = 1)")).isTrue();
1096 assertThat((Boolean) eval("struct(a = 1) == struct(a = 1, b = 2)")).isFalse();
1097 assertThat((Boolean) eval("struct(a = 1, b = 2) == struct(a = 1)")).isFalse();
Vladimir Moskvafdfa9882016-11-18 16:24:20 +00001098 // Compare a recursive object to itself to make sure reference equality is checked
lberkiaea56b32017-05-30 12:35:33 +02001099 assertThat((Boolean) eval("s = (struct(a = 1, b = [])); s.b.append(s); s == s")).isTrue();
1100 assertThat((Boolean) eval("struct(a = 1, b = 2) == struct(a = 1, b = 3)")).isFalse();
1101 assertThat((Boolean) eval("struct(a = 1) == [1]")).isFalse();
1102 assertThat((Boolean) eval("[1] == struct(a = 1)")).isFalse();
1103 assertThat((Boolean) eval("struct() == struct()")).isTrue();
1104 assertThat((Boolean) eval("struct() == struct(a = 1)")).isFalse();
Vladimir Moskvafdfa9882016-11-18 16:24:20 +00001105
1106 eval("foo = provider(); bar = provider()");
lberkiaea56b32017-05-30 12:35:33 +02001107 assertThat((Boolean) eval("struct(a = 1) == foo(a = 1)")).isFalse();
1108 assertThat((Boolean) eval("foo(a = 1) == struct(a = 1)")).isFalse();
1109 assertThat((Boolean) eval("foo(a = 1) == bar(a = 1)")).isFalse();
1110 assertThat((Boolean) eval("foo(a = 1) == foo(a = 1)")).isTrue();
Vladimir Moskvafdfa9882016-11-18 16:24:20 +00001111 }
1112
1113 @Test
Vladimir Moskva76e31d12016-12-05 16:28:37 +00001114 public void testStructIncomparability() throws Exception {
1115 checkErrorContains("Cannot compare structs", "struct(a = 1) < struct(a = 2)");
1116 checkErrorContains("Cannot compare structs", "struct(a = 1) > struct(a = 2)");
1117 checkErrorContains("Cannot compare structs", "struct(a = 1) <= struct(a = 2)");
1118 checkErrorContains("Cannot compare structs", "struct(a = 1) >= struct(a = 2)");
1119 }
1120
1121 @Test
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001122 public void testStructAccessingFieldsFromSkylark() throws Exception {
1123 eval("x = struct(a = 1, b = 2)", "x1 = x.a", "x2 = x.b");
1124 assertThat(lookup("x1")).isEqualTo(1);
1125 assertThat(lookup("x2")).isEqualTo(2);
1126 }
1127
1128 @Test
1129 public void testStructAccessingUnknownField() throws Exception {
1130 checkErrorContains(
1131 "'struct' object has no attribute 'c'\n" + "Available attributes: a, b",
1132 "x = struct(a = 1, b = 2)",
1133 "y = x.c");
1134 }
1135
1136 @Test
1137 public void testStructAccessingUnknownFieldWithArgs() throws Exception {
1138 checkErrorContains(
cparsons71b4dcc2018-10-24 13:46:37 -07001139 "type 'struct' has no method c()", "x = struct(a = 1, b = 2)", "y = x.c()");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001140 }
1141
1142 @Test
1143 public void testStructAccessingNonFunctionFieldWithArgs() throws Exception {
cparsonsda392932018-11-02 16:04:13 -07001144 checkErrorContains("'int' object is not callable", "x = struct(a = 1, b = 2)", "x1 = x.a(1)");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001145 }
1146
1147 @Test
1148 public void testStructAccessingFunctionFieldWithArgs() throws Exception {
1149 eval("def f(x): return x+5", "x = struct(a = f, b = 2)", "x1 = x.a(1)");
1150 assertThat(lookup("x1")).isEqualTo(6);
1151 }
1152
1153 @Test
1154 public void testStructPosArgs() throws Exception {
1155 checkErrorContains(
cparsonsda392932018-11-02 16:04:13 -07001156 "expected no more than 0 positional arguments, but got 1", "x = struct(1, b = 2)");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001157 }
1158
1159 @Test
1160 public void testStructConcatenationFieldNames() throws Exception {
1161 // TODO(fwe): cannot be handled by current testing suite
1162 eval("x = struct(a = 1, b = 2)",
1163 "y = struct(c = 1, d = 2)",
1164 "z = x + y\n");
cparsons4ebf6c02018-08-17 14:49:36 -07001165 StructImpl z = (StructImpl) lookup("z");
brandjond331fa72017-12-28 07:38:31 -08001166 assertThat(z.getFieldNames()).isEqualTo(ImmutableSet.of("a", "b", "c", "d"));
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001167 }
1168
1169 @Test
1170 public void testStructConcatenationFieldValues() throws Exception {
1171 // TODO(fwe): cannot be handled by current testing suite
1172 eval("x = struct(a = 1, b = 2)",
1173 "y = struct(c = 1, d = 2)",
1174 "z = x + y\n");
cparsons4ebf6c02018-08-17 14:49:36 -07001175 StructImpl z = (StructImpl) lookup("z");
lberkiaea56b32017-05-30 12:35:33 +02001176 assertThat(z.getValue("a")).isEqualTo(1);
1177 assertThat(z.getValue("b")).isEqualTo(2);
1178 assertThat(z.getValue("c")).isEqualTo(1);
1179 assertThat(z.getValue("d")).isEqualTo(2);
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001180 }
1181
1182 @Test
1183 public void testStructConcatenationCommonFields() throws Exception {
brandjona29648a2018-01-10 08:38:43 -08001184 checkErrorContains("Cannot use '+' operator on provider instances with overlapping field(s): a",
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001185 "x = struct(a = 1, b = 2)", "y = struct(c = 1, a = 2)", "z = x + y\n");
1186 }
1187
1188 @Test
1189 public void testConditionalStructConcatenation() throws Exception {
1190 // TODO(fwe): cannot be handled by current testing suite
1191 eval("def func():",
1192 " x = struct(a = 1, b = 2)",
1193 " if True:",
1194 " x += struct(c = 1, d = 2)",
1195 " return x",
1196 "x = func()");
cparsons4ebf6c02018-08-17 14:49:36 -07001197 StructImpl x = (StructImpl) lookup("x");
lberkiaea56b32017-05-30 12:35:33 +02001198 assertThat(x.getValue("a")).isEqualTo(1);
1199 assertThat(x.getValue("b")).isEqualTo(2);
1200 assertThat(x.getValue("c")).isEqualTo(1);
1201 assertThat(x.getValue("d")).isEqualTo(2);
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001202 }
1203
1204 @Test
1205 public void testGetattrNoAttr() throws Exception {
Laurent Le Brunc31f3512016-12-29 21:41:33 +00001206 checkErrorContains(
Googler055d6c62018-05-22 13:21:04 -07001207 "'struct' object has no attribute 'b'\nAvailable attributes: a",
1208 "s = struct(a='val')",
1209 "getattr(s, 'b')");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001210 }
1211
1212 @Test
1213 public void testGetattr() throws Exception {
1214 eval(
1215 "s = struct(a='val')",
1216 "x = getattr(s, 'a')",
1217 "y = getattr(s, 'b', 'def')",
1218 "z = getattr(s, 'b', default = 'def')",
1219 "w = getattr(s, 'a', default='ignored')");
1220 assertThat(lookup("x")).isEqualTo("val");
1221 assertThat(lookup("y")).isEqualTo("def");
1222 assertThat(lookup("z")).isEqualTo("def");
1223 assertThat(lookup("w")).isEqualTo("val");
1224 }
1225
1226 @Test
1227 public void testHasattr() throws Exception {
1228 eval("s = struct(a=1)",
1229 "x = hasattr(s, 'a')",
1230 "y = hasattr(s, 'b')\n");
1231 assertThat(lookup("x")).isEqualTo(true);
1232 assertThat(lookup("y")).isEqualTo(false);
1233 }
1234
1235 @Test
1236 public void testStructStr() throws Exception {
1237 assertThat(eval("str(struct(x = 2, y = 3, z = 4))"))
1238 .isEqualTo("struct(x = 2, y = 3, z = 4)");
1239 }
1240
1241 @Test
1242 public void testStructsInSets() throws Exception {
Vladimir Moskvad200daf2016-12-23 16:35:37 +00001243 eval("depset([struct(a='a')])");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001244 }
1245
1246 @Test
Vladimir Moskva76e31d12016-12-05 16:28:37 +00001247 public void testStructsInDicts() throws Exception {
1248 eval("d = {struct(a = 1): 'aa', struct(b = 2): 'bb'}");
1249 assertThat(eval("d[struct(a = 1)]")).isEqualTo("aa");
1250 assertThat(eval("d[struct(b = 2)]")).isEqualTo("bb");
1251 assertThat(eval("str([d[k] for k in d])")).isEqualTo("[\"aa\", \"bb\"]");
1252
1253 checkErrorContains(
1254 "unhashable type: 'struct'",
1255 "{struct(a = []): 'foo'}");
1256 }
1257
1258 @Test
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001259 public void testStructMembersAreImmutable() throws Exception {
1260 checkErrorContains(
Jon Brandvein162f9eb2016-11-10 00:22:43 +00001261 "cannot assign to 's.x'",
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001262 "s = struct(x = 'a')",
1263 "s.x = 'b'\n");
1264 }
1265
1266 @Test
Vladimir Moskva10770382016-08-23 15:04:54 +00001267 public void testStructDictMembersAreMutable() throws Exception {
1268 eval(
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001269 "s = struct(x = {'a' : 1})",
1270 "s.x['b'] = 2\n");
cparsons4ebf6c02018-08-17 14:49:36 -07001271 assertThat(((StructImpl) lookup("s")).getValue("x")).isEqualTo(ImmutableMap.of("a", 1, "b", 2));
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001272 }
1273
1274 @Test
1275 public void testNsetGoodCompositeItem() throws Exception {
Vladimir Moskvad200daf2016-12-23 16:35:37 +00001276 eval("def func():", " return depset([struct(a='a')])", "s = func()");
Googler52fc135a2019-05-06 14:21:04 -07001277 Collection<?> result = ((SkylarkNestedSet) lookup("s")).toCollection();
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001278 assertThat(result).hasSize(1);
cparsons4ebf6c02018-08-17 14:49:36 -07001279 assertThat(result.iterator().next()).isInstanceOf(StructImpl.class);
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001280 }
1281
1282 @Test
1283 public void testNsetBadMutableItem() throws Exception {
Vladimir Moskvad200daf2016-12-23 16:35:37 +00001284 checkEvalError("depsets cannot contain mutable items", "depset([([],)])");
1285 checkEvalError("depsets cannot contain mutable items", "depset([struct(a=[])])");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001286 }
1287
cparsons4ebf6c02018-08-17 14:49:36 -07001288 private static StructImpl makeStruct(String field, Object value) {
cparsons0c5c1c62018-05-24 10:37:03 -07001289 return StructProvider.STRUCT.create(ImmutableMap.of(field, value), "no field '%'");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001290 }
1291
cparsons4ebf6c02018-08-17 14:49:36 -07001292 private static StructImpl makeBigStruct(Environment env) {
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001293 // struct(a=[struct(x={1:1}), ()], b=(), c={2:2})
cparsons0c5c1c62018-05-24 10:37:03 -07001294 return StructProvider.STRUCT.create(
Dmitry Lomovea9de072016-08-09 09:35:40 +00001295 ImmutableMap.<String, Object>of(
dslomovde965ac2017-07-31 21:07:51 +02001296 "a",
1297 MutableList.<Object>of(
1298 env,
cparsons0c5c1c62018-05-24 10:37:03 -07001299 StructProvider.STRUCT.create(
dslomovde965ac2017-07-31 21:07:51 +02001300 ImmutableMap.<String, Object>of(
1301 "x", SkylarkDict.<Object, Object>of(env, 1, 1)),
1302 "no field '%s'"),
1303 Tuple.of()),
Dmitry Lomovea9de072016-08-09 09:35:40 +00001304 "b", Tuple.of(),
1305 "c", SkylarkDict.<Object, Object>of(env, 2, 2)),
1306 "no field '%s'");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001307 }
1308
1309 @Test
1310 public void testStructMutabilityShallow() throws Exception {
lberkiaea56b32017-05-30 12:35:33 +02001311 assertThat(EvalUtils.isImmutable(makeStruct("a", 1))).isTrue();
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001312 }
1313
1314 private static MutableList<Object> makeList(Environment env) {
1315 return MutableList.<Object>of(env, 1, 2, 3);
1316 }
1317
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001318 @Test
1319 public void testStructMutabilityDeep() throws Exception {
lberkiaea56b32017-05-30 12:35:33 +02001320 assertThat(EvalUtils.isImmutable(Tuple.<Object>of(makeList(null)))).isTrue();
1321 assertThat(EvalUtils.isImmutable(makeStruct("a", makeList(null)))).isTrue();
1322 assertThat(EvalUtils.isImmutable(makeBigStruct(null))).isTrue();
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001323
lberkiaea56b32017-05-30 12:35:33 +02001324 assertThat(EvalUtils.isImmutable(Tuple.<Object>of(makeList(ev.getEnvironment())))).isFalse();
1325 assertThat(EvalUtils.isImmutable(makeStruct("a", makeList(ev.getEnvironment())))).isFalse();
1326 assertThat(EvalUtils.isImmutable(makeBigStruct(ev.getEnvironment()))).isFalse();
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001327 }
1328
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001329 @Test
1330 public void declaredProviders() throws Exception {
1331 evalAndExport(
1332 "data = provider()",
1333 "d = data(x = 1, y ='abc')",
1334 "d_x = d.x",
1335 "d_y = d.y"
1336 );
1337 assertThat(lookup("d_x")).isEqualTo(1);
1338 assertThat(lookup("d_y")).isEqualTo("abc");
dslomovde965ac2017-07-31 21:07:51 +02001339 SkylarkProvider dataConstructor = (SkylarkProvider) lookup("data");
cparsons4ebf6c02018-08-17 14:49:36 -07001340 StructImpl data = (StructImpl) lookup("d");
dslomovde965ac2017-07-31 21:07:51 +02001341 assertThat(data.getProvider()).isEqualTo(dataConstructor);
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001342 assertThat(dataConstructor.isExported()).isTrue();
1343 assertThat(dataConstructor.getPrintableName()).isEqualTo("data");
dslomovde965ac2017-07-31 21:07:51 +02001344 assertThat(dataConstructor.getKey())
1345 .isEqualTo(new SkylarkProvider.SkylarkKey(FAKE_LABEL, "data"));
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001346 }
1347
1348 @Test
1349 public void declaredProvidersConcatSuccess() throws Exception {
1350 evalAndExport(
1351 "data = provider()",
1352 "dx = data(x = 1)",
1353 "dy = data(y = 'abc')",
1354 "dxy = dx + dy",
1355 "x = dxy.x",
1356 "y = dxy.y"
1357 );
1358 assertThat(lookup("x")).isEqualTo(1);
1359 assertThat(lookup("y")).isEqualTo("abc");
dslomovde965ac2017-07-31 21:07:51 +02001360 SkylarkProvider dataConstructor = (SkylarkProvider) lookup("data");
cparsons4ebf6c02018-08-17 14:49:36 -07001361 StructImpl dx = (StructImpl) lookup("dx");
dslomovde965ac2017-07-31 21:07:51 +02001362 assertThat(dx.getProvider()).isEqualTo(dataConstructor);
cparsons4ebf6c02018-08-17 14:49:36 -07001363 StructImpl dy = (StructImpl) lookup("dy");
dslomovde965ac2017-07-31 21:07:51 +02001364 assertThat(dy.getProvider()).isEqualTo(dataConstructor);
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001365 }
1366
1367 @Test
1368 public void declaredProvidersConcatError() throws Exception {
1369 evalAndExport(
1370 "data1 = provider()",
1371 "data2 = provider()"
1372 );
1373
brandjona29648a2018-01-10 08:38:43 -08001374 checkEvalError("Cannot use '+' operator on instances of different providers (data1 and data2)",
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001375 "d1 = data1(x = 1)",
1376 "d2 = data2(y = 2)",
1377 "d = d1 + d2"
1378 );
1379 }
Dmitry Lomov94b2c882016-09-25 20:24:24 +00001380
1381 @Test
Benjamin Peterson8610d972017-11-27 03:14:43 -08001382 public void declaredProvidersWithFieldsConcatSuccess() throws Exception {
1383 evalAndExport(
1384 "data = provider(fields=['f1', 'f2'])",
1385 "d1 = data(f1 = 4)",
1386 "d2 = data(f2 = 5)",
1387 "d3 = d1 + d2",
1388 "f1 = d3.f1",
1389 "f2 = d3.f2");
1390 assertThat(lookup("f1")).isEqualTo(4);
1391 assertThat(lookup("f2")).isEqualTo(5);
1392 }
1393
1394 @Test
1395 public void declaredProvidersWithFieldsConcatError() throws Exception {
1396 evalAndExport("data1 = provider(fields=['f1', 'f2'])", "data2 = provider(fields=['f3'])");
1397 checkEvalError(
brandjona29648a2018-01-10 08:38:43 -08001398 "Cannot use '+' operator on instances of different providers (data1 and data2)",
Benjamin Peterson8610d972017-11-27 03:14:43 -08001399 "d1 = data1(f1=1, f2=2)",
1400 "d2 = data2(f3=3)",
1401 "d = d1 + d2");
1402 }
1403
1404 @Test
1405 public void declaredProvidersWithOverlappingFieldsConcatError() throws Exception {
1406 evalAndExport("data = provider(fields=['f1', 'f2'])");
1407 checkEvalError(
brandjona29648a2018-01-10 08:38:43 -08001408 "Cannot use '+' operator on provider instances with overlapping field(s): f1",
Benjamin Peterson8610d972017-11-27 03:14:43 -08001409 "d1 = data(f1 = 4)",
1410 "d2 = data(f1 = 5)",
1411 "d1 + d2");
1412 }
1413
1414 @Test
Dmitry Lomov94b2c882016-09-25 20:24:24 +00001415 public void structsAsDeclaredProvidersTest() throws Exception {
1416 evalAndExport(
1417 "data = struct(x = 1)"
1418 );
cparsons4ebf6c02018-08-17 14:49:36 -07001419 StructImpl data = (StructImpl) lookup("data");
cparsons0c5c1c62018-05-24 10:37:03 -07001420 assertThat(StructProvider.STRUCT.isExported()).isTrue();
1421 assertThat(data.getProvider()).isEqualTo(StructProvider.STRUCT);
1422 assertThat(data.getProvider().getKey()).isEqualTo(StructProvider.STRUCT.getKey());
Dmitry Lomov94b2c882016-09-25 20:24:24 +00001423 }
Dmitry Lomov0692c7f2016-09-30 16:43:30 +00001424
1425 @Test
allevato45b79e52017-07-07 21:40:50 +02001426 public void declaredProvidersDoc() throws Exception {
1427 evalAndExport("data1 = provider(doc='foo')");
1428 }
1429
1430 @Test
1431 public void declaredProvidersBadTypeForDoc() throws Exception {
1432 checkErrorContains(
cparsons03035142019-01-15 13:35:22 -08001433 "expected value of type 'string' for parameter 'doc', for call to function "
1434 + "provider(doc = '', fields = None)",
allevato45b79e52017-07-07 21:40:50 +02001435 "provider(doc = 1)");
1436 }
1437
1438 @Test
Dmitry Lomov0692c7f2016-09-30 16:43:30 +00001439 public void aspectAllAttrs() throws Exception {
1440 evalAndExport(
1441 "def _impl(target, ctx):",
1442 " pass",
1443 "my_aspect = aspect(_impl, attr_aspects=['*'])");
1444
cparsons0d55f4c2017-12-20 14:49:13 -08001445 SkylarkDefinedAspect myAspect = (SkylarkDefinedAspect) lookup("my_aspect");
lberki3bd28392019-02-05 01:01:02 -08001446 assertThat(myAspect.getDefinition(AspectParameters.EMPTY).propagateAlong("foo")).isTrue();
Dmitry Lomov0692c7f2016-09-30 16:43:30 +00001447 }
1448
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001449 @Test
1450 public void aspectRequiredAspectProvidersSingle() throws Exception {
1451 evalAndExport(
1452 "def _impl(target, ctx):",
1453 " pass",
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001454 "cc = provider()",
1455 "my_aspect = aspect(_impl, required_aspect_providers=['java', cc])"
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001456 );
cparsons0d55f4c2017-12-20 14:49:13 -08001457 SkylarkDefinedAspect myAspect = (SkylarkDefinedAspect) lookup("my_aspect");
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001458 RequiredProviders requiredProviders = myAspect.getDefinition(AspectParameters.EMPTY)
1459 .getRequiredProvidersForAspects();
1460 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.ANY)).isTrue();
1461 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.EMPTY)).isFalse();
1462 assertThat(requiredProviders.isSatisfiedBy(
1463 AdvertisedProviderSet.builder()
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001464 .addSkylark(declared("cc"))
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001465 .addSkylark("java")
1466 .build()))
1467 .isTrue();
1468 assertThat(requiredProviders.isSatisfiedBy(
1469 AdvertisedProviderSet.builder()
1470 .addSkylark("cc")
1471 .build()))
1472 .isFalse();
1473 }
1474
1475 @Test
1476 public void aspectRequiredAspectProvidersAlternatives() throws Exception {
1477 evalAndExport(
1478 "def _impl(target, ctx):",
1479 " pass",
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001480 "cc = provider()",
1481 "my_aspect = aspect(_impl, required_aspect_providers=[['java'], [cc]])"
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001482 );
cparsons0d55f4c2017-12-20 14:49:13 -08001483 SkylarkDefinedAspect myAspect = (SkylarkDefinedAspect) lookup("my_aspect");
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001484 RequiredProviders requiredProviders = myAspect.getDefinition(AspectParameters.EMPTY)
1485 .getRequiredProvidersForAspects();
1486 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.ANY)).isTrue();
1487 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.EMPTY)).isFalse();
1488 assertThat(requiredProviders.isSatisfiedBy(
1489 AdvertisedProviderSet.builder()
1490 .addSkylark("java")
1491 .build()))
1492 .isTrue();
1493 assertThat(requiredProviders.isSatisfiedBy(
1494 AdvertisedProviderSet.builder()
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001495 .addSkylark(declared("cc"))
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001496 .build()))
1497 .isTrue();
1498 assertThat(requiredProviders.isSatisfiedBy(
1499 AdvertisedProviderSet.builder()
1500 .addSkylark("prolog")
1501 .build()))
1502 .isFalse();
1503 }
1504
1505 @Test
1506 public void aspectRequiredAspectProvidersEmpty() throws Exception {
1507 evalAndExport(
1508 "def _impl(target, ctx):",
1509 " pass",
1510 "my_aspect = aspect(_impl, required_aspect_providers=[])"
1511 );
cparsons0d55f4c2017-12-20 14:49:13 -08001512 SkylarkDefinedAspect myAspect = (SkylarkDefinedAspect) lookup("my_aspect");
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001513 RequiredProviders requiredProviders = myAspect.getDefinition(AspectParameters.EMPTY)
1514 .getRequiredProvidersForAspects();
1515 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.ANY)).isFalse();
1516 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.EMPTY)).isFalse();
1517 }
1518
1519 @Test
1520 public void aspectRequiredAspectProvidersDefault() throws Exception {
1521 evalAndExport(
1522 "def _impl(target, ctx):",
1523 " pass",
1524 "my_aspect = aspect(_impl)"
1525 );
cparsons0d55f4c2017-12-20 14:49:13 -08001526 SkylarkDefinedAspect myAspect = (SkylarkDefinedAspect) lookup("my_aspect");
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001527 RequiredProviders requiredProviders = myAspect.getDefinition(AspectParameters.EMPTY)
1528 .getRequiredProvidersForAspects();
1529 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.ANY)).isFalse();
1530 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.EMPTY)).isFalse();
1531 }
1532
Dmitry Lomov950310f2017-03-01 17:45:12 +00001533 @Test
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001534 public void aspectProvides() throws Exception {
1535 evalAndExport(
1536 "def _impl(target, ctx):",
1537 " pass",
1538 "y = provider()",
1539 "my_aspect = aspect(_impl, provides = ['x', y])"
1540 );
cparsons0d55f4c2017-12-20 14:49:13 -08001541 SkylarkDefinedAspect myAspect = (SkylarkDefinedAspect) lookup("my_aspect");
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001542 AdvertisedProviderSet advertisedProviders = myAspect.getDefinition(AspectParameters.EMPTY)
1543 .getAdvertisedProviders();
1544 assertThat(advertisedProviders.canHaveAnyProvider()).isFalse();
1545 assertThat(advertisedProviders.getSkylarkProviders())
1546 .containsExactly(legacy("x"), declared("y"));
1547 }
1548
1549 @Test
1550 public void aspectProvidesError() throws Exception {
1551 ev.setFailFast(false);
1552 evalAndExport(
1553 "def _impl(target, ctx):",
1554 " pass",
1555 "y = provider()",
1556 "my_aspect = aspect(_impl, provides = ['x', 1])"
1557 );
1558 MoreAsserts.assertContainsEvent(ev.getEventCollector(),
1559 " Illegal argument: element in 'provides' is of unexpected type."
cparsonsaab98682018-04-27 13:04:59 -07001560 + " Should be list of providers, but got item of type int. ");
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001561 }
1562
allevato45b79e52017-07-07 21:40:50 +02001563 @Test
1564 public void aspectDoc() throws Exception {
1565 evalAndExport(
1566 "def _impl(target, ctx):",
1567 " pass",
1568 "my_aspect = aspect(_impl, doc='foo')");
1569 }
1570
1571 @Test
1572 public void aspectBadTypeForDoc() throws Exception {
1573 registerDummyUserDefinedFunction();
1574 checkErrorContains(
cparsons03035142019-01-15 13:35:22 -08001575 "expected value of type 'string' for parameter 'doc', for call to function aspect(",
allevato45b79e52017-07-07 21:40:50 +02001576 "aspect(impl, doc = 1)");
1577 }
1578
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001579 @Test
Dmitry Lomov950310f2017-03-01 17:45:12 +00001580 public void fancyExports() throws Exception {
1581 evalAndExport(
1582 "def _impla(target, ctx): pass",
1583 "p, (a, p1) = [",
1584 " provider(),",
1585 " [ aspect(_impla),",
1586 " provider() ]",
1587 "]"
1588 );
dslomovde965ac2017-07-31 21:07:51 +02001589 SkylarkProvider p = (SkylarkProvider) lookup("p");
cparsons0d55f4c2017-12-20 14:49:13 -08001590 SkylarkDefinedAspect a = (SkylarkDefinedAspect) lookup("a");
dslomovde965ac2017-07-31 21:07:51 +02001591 SkylarkProvider p1 = (SkylarkProvider) lookup("p1");
Dmitry Lomov950310f2017-03-01 17:45:12 +00001592 assertThat(p.getPrintableName()).isEqualTo("p");
dslomovde965ac2017-07-31 21:07:51 +02001593 assertThat(p.getKey()).isEqualTo(new SkylarkProvider.SkylarkKey(FAKE_LABEL, "p"));
Dmitry Lomov950310f2017-03-01 17:45:12 +00001594 assertThat(p1.getPrintableName()).isEqualTo("p1");
dslomovde965ac2017-07-31 21:07:51 +02001595 assertThat(p1.getKey()).isEqualTo(new SkylarkProvider.SkylarkKey(FAKE_LABEL, "p1"));
Dmitry Lomov950310f2017-03-01 17:45:12 +00001596 assertThat(a.getAspectClass()).isEqualTo(
1597 new SkylarkAspectClass(FAKE_LABEL, "a")
1598 );
1599 }
1600
1601 @Test
1602 public void multipleTopLevels() throws Exception {
1603 evalAndExport(
1604 "p = provider()",
1605 "p1 = p"
1606 );
dslomovde965ac2017-07-31 21:07:51 +02001607 SkylarkProvider p = (SkylarkProvider) lookup("p");
1608 SkylarkProvider p1 = (SkylarkProvider) lookup("p1");
Dmitry Lomov950310f2017-03-01 17:45:12 +00001609 assertThat(p).isEqualTo(p1);
dslomovde965ac2017-07-31 21:07:51 +02001610 assertThat(p.getKey()).isEqualTo(new SkylarkProvider.SkylarkKey(FAKE_LABEL, "p"));
1611 assertThat(p1.getKey()).isEqualTo(new SkylarkProvider.SkylarkKey(FAKE_LABEL, "p"));
Dmitry Lomov950310f2017-03-01 17:45:12 +00001612 }
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001613
dslomov0667b832017-08-25 09:29:50 +02001614 @Test
1615 public void providerWithFields() throws Exception {
1616 evalAndExport(
1617 "p = provider(fields = ['x', 'y'])",
1618 "p1 = p(x = 1, y = 2)",
1619 "x = p1.x",
1620 "y = p1.y"
1621 );
1622 SkylarkProvider p = (SkylarkProvider) lookup("p");
1623 SkylarkInfo p1 = (SkylarkInfo) lookup("p1");
1624
dslomov0667b832017-08-25 09:29:50 +02001625 assertThat(p1.getProvider()).isEqualTo(p);
1626 assertThat(lookup("x")).isEqualTo(1);
1627 assertThat(lookup("y")).isEqualTo(2);
1628 }
1629
1630 @Test
1631 public void providerWithFieldsDict() throws Exception {
1632 evalAndExport(
1633 "p = provider(fields = { 'x' : 'I am x', 'y' : 'I am y'})",
1634 "p1 = p(x = 1, y = 2)",
1635 "x = p1.x",
1636 "y = p1.y"
1637 );
1638 SkylarkProvider p = (SkylarkProvider) lookup("p");
1639 SkylarkInfo p1 = (SkylarkInfo) lookup("p1");
1640
dslomov0667b832017-08-25 09:29:50 +02001641 assertThat(p1.getProvider()).isEqualTo(p);
1642 assertThat(lookup("x")).isEqualTo(1);
1643 assertThat(lookup("y")).isEqualTo(2);
1644 }
1645
1646 @Test
1647 public void providerWithFieldsOptional() throws Exception {
1648 evalAndExport(
1649 "p = provider(fields = ['x', 'y'])",
1650 "p1 = p(y = 2)",
1651 "y = p1.y"
1652 );
1653 SkylarkProvider p = (SkylarkProvider) lookup("p");
1654 SkylarkInfo p1 = (SkylarkInfo) lookup("p1");
1655
dslomov0667b832017-08-25 09:29:50 +02001656 assertThat(p1.getProvider()).isEqualTo(p);
1657 assertThat(lookup("y")).isEqualTo(2);
1658 }
1659
1660 @Test
1661 public void providerWithFieldsOptionalError() throws Exception {
1662 ev.setFailFast(false);
1663 evalAndExport(
1664 "p = provider(fields = ['x', 'y'])",
1665 "p1 = p(y = 2)",
1666 "x = p1.x"
1667 );
1668 MoreAsserts.assertContainsEvent(ev.getEventCollector(),
1669 " 'p' object has no attribute 'x'");
1670 }
1671
1672 @Test
1673 public void providerWithExtraFieldsError() throws Exception {
1674 ev.setFailFast(false);
1675 evalAndExport(
1676 "p = provider(fields = ['x', 'y'])",
1677 "p1 = p(x = 1, y = 2, z = 3)"
1678 );
1679 MoreAsserts.assertContainsEvent(ev.getEventCollector(),
1680 "unexpected keyword 'z' in call to p(*, x = ?, y = ?)");
1681 }
1682
1683 @Test
1684 public void providerWithEmptyFieldsError() throws Exception {
1685 ev.setFailFast(false);
1686 evalAndExport(
1687 "p = provider(fields = [])",
1688 "p1 = p(x = 1, y = 2, z = 3)"
1689 );
1690 MoreAsserts.assertContainsEvent(ev.getEventCollector(),
1691 "unexpected keywords 'x', 'y', 'z' in call to p()");
1692 }
1693
Dmitry Lomov0692c7f2016-09-30 16:43:30 +00001694 @Test
1695 public void starTheOnlyAspectArg() throws Exception {
1696 checkEvalError("'*' must be the only string in 'attr_aspects' list",
1697 "def _impl(target, ctx):",
1698 " pass",
1699 "aspect(_impl, attr_aspects=['*', 'foo'])");
1700 }
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +00001701
1702 @Test
1703 public void testMandatoryConfigParameterForExecutableLabels() throws Exception {
1704 scratch.file("third_party/foo/extension.bzl",
1705 "def _main_rule_impl(ctx):",
1706 " pass",
1707 "my_rule = rule(_main_rule_impl,",
1708 " attrs = { ",
1709 " 'exe' : attr.label(executable = True, allow_files = True),",
1710 " },",
1711 ")"
1712 );
1713 scratch.file("third_party/foo/BUILD",
laurentlb5ddd8042017-11-30 12:03:31 -08001714 "load(':extension.bzl', 'my_rule')",
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +00001715 "my_rule(name = 'main', exe = ':tool.sh')"
1716 );
1717
cushon978cb002018-02-24 14:05:37 -08001718 AssertionError expected =
1719 assertThrows(AssertionError.class, () -> createRuleContext("//third_party/foo:main"));
brandjon8bd20162017-12-28 08:49:54 -08001720 assertThat(expected).hasMessageThat()
1721 .contains("cfg parameter is mandatory when executable=True is provided.");
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +00001722 }
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +00001723
John Catereca28402017-05-17 21:44:12 +02001724 @Test
1725 public void testRuleAddToolchain() throws Exception {
John Cater9a8d16e2017-07-05 16:12:07 -04001726 scratch.file("test/BUILD", "toolchain_type(name = 'my_toolchain_type')");
John Catereca28402017-05-17 21:44:12 +02001727 evalAndExport(
John Cater9a8d16e2017-07-05 16:12:07 -04001728 "def impl(ctx): return None", "r1 = rule(impl, toolchains=['//test:my_toolchain_type'])");
tomlu72642a22017-10-18 06:23:14 +02001729 RuleClass c = ((SkylarkRuleFunction) lookup("r1")).getRuleClass();
John Cater9a8d16e2017-07-05 16:12:07 -04001730 assertThat(c.getRequiredToolchains()).containsExactly(makeLabel("//test:my_toolchain_type"));
John Catereca28402017-05-17 21:44:12 +02001731 }
vladmos2f32e382017-06-19 12:44:21 -04001732
1733 @Test
John Caterd79e9772018-06-14 02:24:57 -07001734 public void testRuleAddExecutionConstraints() throws Exception {
1735 registerDummyUserDefinedFunction();
1736 scratch.file("test/BUILD", "toolchain_type(name = 'my_toolchain_type')");
1737 evalAndExport(
1738 "r1 = rule(",
1739 " implementation = impl,",
1740 " toolchains=['//test:my_toolchain_type'],",
1741 " exec_compatible_with=['//constraint:cv1', '//constraint:cv2'],",
1742 ")");
1743 RuleClass c = ((SkylarkRuleFunction) lookup("r1")).getRuleClass();
1744 assertThat(c.getExecutionPlatformConstraints())
1745 .containsExactly(makeLabel("//constraint:cv1"), makeLabel("//constraint:cv2"));
1746 }
1747
1748 @Test
John Cater56887932019-04-26 05:27:07 -07001749 public void testTargetsCanAddExecutionPlatformConstraints_enabled() throws Exception {
1750 StarlarkSemantics semantics =
1751 StarlarkSemantics.DEFAULT_SEMANTICS.toBuilder()
1752 .incompatibleDisallowRuleExecutionPlatformConstraintsAllowed(false)
1753 .build();
1754 ev = createEvaluationTestCase(semantics);
1755 ev.initialize();
1756
John Caterd79e9772018-06-14 02:24:57 -07001757 registerDummyUserDefinedFunction();
1758 scratch.file("test/BUILD", "toolchain_type(name = 'my_toolchain_type')");
1759 evalAndExport(
1760 "r1 = rule(impl, ",
1761 " toolchains=['//test:my_toolchain_type'],",
1762 " execution_platform_constraints_allowed=True,",
1763 ")");
1764 RuleClass c = ((SkylarkRuleFunction) lookup("r1")).getRuleClass();
1765 assertThat(c.executionPlatformConstraintsAllowed())
1766 .isEqualTo(ExecutionPlatformConstraintsAllowed.PER_TARGET);
1767 }
1768
1769 @Test
John Cater56887932019-04-26 05:27:07 -07001770 public void testTargetsCanAddExecutionPlatformConstraints_notEnabled() throws Exception {
1771 StarlarkSemantics semantics =
1772 StarlarkSemantics.DEFAULT_SEMANTICS.toBuilder()
1773 .incompatibleDisallowRuleExecutionPlatformConstraintsAllowed(false)
1774 .build();
1775 ev = createEvaluationTestCase(semantics);
1776 ev.initialize();
1777
1778 registerDummyUserDefinedFunction();
1779 scratch.file("test/BUILD", "toolchain_type(name = 'my_toolchain_type')");
1780 evalAndExport(
1781 "r1 = rule(impl, ",
1782 " toolchains=['//test:my_toolchain_type'],",
1783 " execution_platform_constraints_allowed=False,",
1784 ")");
1785 RuleClass c = ((SkylarkRuleFunction) lookup("r1")).getRuleClass();
1786 assertThat(c.executionPlatformConstraintsAllowed())
1787 .isEqualTo(ExecutionPlatformConstraintsAllowed.PER_RULE);
1788 }
1789
1790 @Test
1791 public void testTargetsCanAddExecutionPlatformConstraints_disallowed() throws Exception {
1792 StarlarkSemantics semantics =
1793 StarlarkSemantics.DEFAULT_SEMANTICS.toBuilder()
1794 .incompatibleDisallowRuleExecutionPlatformConstraintsAllowed(true)
1795 .build();
1796 ev = createEvaluationTestCase(semantics);
1797 ev.setFailFast(false);
1798 ev.initialize();
1799
1800 registerDummyUserDefinedFunction();
1801 scratch.file("test/BUILD", "toolchain_type(name = 'my_toolchain_type')");
1802 evalAndExport(
1803 "r1 = rule(impl, ",
1804 " toolchains=['//test:my_toolchain_type'],",
1805 " execution_platform_constraints_allowed=True,",
1806 ")");
1807 ev.assertContainsError("parameter 'execution_platform_constraints_allowed' is deprecated");
1808 }
1809
1810 @Test
vladmos2f32e382017-06-19 12:44:21 -04001811 public void testRuleFunctionReturnsNone() throws Exception {
1812 scratch.file("test/rule.bzl",
1813 "def _impl(ctx):",
1814 " pass",
1815 "foo_rule = rule(",
1816 " implementation = _impl,",
1817 " attrs = {'params': attr.string_list()},",
1818 ")");
1819 scratch.file("test/BUILD",
1820 "load(':rule.bzl', 'foo_rule')",
1821 "r = foo_rule(name='foo')", // Custom rule should return None
1822 "c = cc_library(name='cc')", // Native rule should return None
1823 "",
1824 "foo_rule(",
1825 " name='check',",
1826 " params = [type(r), type(c)]",
1827 ")");
1828 invalidatePackages();
1829 SkylarkRuleContext context = createRuleContext("//test:check");
1830 @SuppressWarnings("unchecked")
1831 MutableList<Object> params = (MutableList<Object>) context.getAttr().getValue("params");
1832 assertThat(params.get(0)).isEqualTo("NoneType");
1833 assertThat(params.get(1)).isEqualTo("NoneType");
1834 }
cparsons0c5c1c62018-05-24 10:37:03 -07001835
1836 @Test
1837 public void testTypeOfStruct() throws Exception {
1838 eval("p = type(struct)", "s = type(struct())");
1839
1840 assertThat(lookup("p")).isEqualTo("Provider");
1841 assertThat(lookup("s")).isEqualTo("struct");
1842 }
John Catereca28402017-05-17 21:44:12 +02001843}