blob: aef0e11052a57d33abf008b17a90a59e821d7a6c [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;
lberki4a45ea82017-06-01 10:05:42 +020018import static org.junit.Assert.fail;
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;
25import com.google.devtools.build.lib.cmdline.Label;
Dmitry Lomovf868b3e2017-01-17 10:25:28 +000026import com.google.devtools.build.lib.packages.AdvertisedProviderSet;
Dmitry Lomov0692c7f2016-09-30 16:43:30 +000027import com.google.devtools.build.lib.packages.AspectParameters;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000028import com.google.devtools.build.lib.packages.Attribute;
29import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition;
30import com.google.devtools.build.lib.packages.BuildType;
31import com.google.devtools.build.lib.packages.ImplicitOutputsFunction;
Dmitry Lomov654717f2017-03-02 14:39:52 +000032import com.google.devtools.build.lib.packages.NativeClassObjectConstructor;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000033import com.google.devtools.build.lib.packages.PredicateWithMessage;
Dmitry Lomovf868b3e2017-01-17 10:25:28 +000034import com.google.devtools.build.lib.packages.RequiredProviders;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000035import com.google.devtools.build.lib.packages.RuleClass;
36import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
Googler74558fc2016-05-06 21:47:42 +000037import com.google.devtools.build.lib.packages.SkylarkAspect;
Dmitry Lomov950310f2017-03-01 17:45:12 +000038import com.google.devtools.build.lib.packages.SkylarkAspectClass;
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +000039import com.google.devtools.build.lib.packages.SkylarkClassObject;
Dmitry Lomovea9de072016-08-09 09:35:40 +000040import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
Dmitry Lomovfd2bdc32016-10-07 08:52:10 +000041import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier;
John Catereca28402017-05-17 21:44:12 +020042import com.google.devtools.build.lib.packages.ToolchainConstructor;
Dmitry Lomov7b599452015-11-26 10:07:32 +000043import com.google.devtools.build.lib.rules.SkylarkAttr;
Dmitry Lomov8ff5a872017-03-04 00:58:14 +000044import com.google.devtools.build.lib.rules.SkylarkAttr.Descriptor;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000045import com.google.devtools.build.lib.rules.SkylarkFileType;
46import com.google.devtools.build.lib.rules.SkylarkRuleClassFunctions.RuleFunction;
vladmos2f32e382017-06-19 12:44:21 -040047import com.google.devtools.build.lib.rules.SkylarkRuleContext;
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;
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +000053import com.google.devtools.build.lib.syntax.EvalUtils;
54import com.google.devtools.build.lib.syntax.SkylarkDict;
55import com.google.devtools.build.lib.syntax.SkylarkList.MutableList;
56import com.google.devtools.build.lib.syntax.SkylarkList.Tuple;
57import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000058import com.google.devtools.build.lib.syntax.Type;
Dmitry Lomov8ff5a872017-03-04 00:58:14 +000059import com.google.devtools.build.lib.testutil.MoreAsserts;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000060import com.google.devtools.build.lib.util.FileTypeSet;
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +000061import java.util.Collection;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000062import org.junit.Before;
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +000063import org.junit.Rule;
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +000064import org.junit.Test;
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +000065import org.junit.rules.ExpectedException;
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +000066import org.junit.runner.RunWith;
67import org.junit.runners.JUnit4;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000068
69/**
70 * Tests for SkylarkRuleClassFunctions.
71 */
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +000072@RunWith(JUnit4.class)
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000073public class SkylarkRuleClassFunctionsTest extends SkylarkTestCase {
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +000074 @Rule public ExpectedException thrown = ExpectedException.none();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000075
76 @Before
Florian Weikertb4c59042015-12-01 10:47:18 +000077 public final void createBuildFile() throws Exception {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000078 scratch.file(
79 "foo/BUILD",
80 "genrule(name = 'foo',",
81 " cmd = 'dummy_cmd',",
82 " srcs = ['a.txt', 'b.img'],",
83 " tools = ['t.exe'],",
84 " outs = ['c.txt'])",
85 "genrule(name = 'bar',",
86 " cmd = 'dummy_cmd',",
87 " srcs = [':jl', ':gl'],",
88 " outs = ['d.txt'])",
89 "java_library(name = 'jl',",
90 " srcs = ['a.java'])",
91 "genrule(name = 'gl',",
92 " cmd = 'touch $(OUTS)',",
93 " srcs = ['a.go'],",
94 " outs = [ 'gl.a', 'gl.gcgox', ],",
95 " output_to_bindir = 1,",
96 ")");
97 }
98
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +000099 @Test
Florian Weikerte96b0b82015-09-25 11:35:11 +0000100 public void testCannotOverrideBuiltInAttribute() throws Exception {
Dmitry Lomov950310f2017-03-01 17:45:12 +0000101 ev.setFailFast(false);
Dmitry Lomov7b599452015-11-26 10:07:32 +0000102 try {
103 evalAndExport(
104 "def impl(ctx): return", "r = rule(impl, attrs = {'tags': attr.string_list()})");
lberki4a45ea82017-06-01 10:05:42 +0200105 fail("Expected error '"
Dmitry Lomov7b599452015-11-26 10:07:32 +0000106 + "There is already a built-in attribute 'tags' which cannot be overridden"
107 + "' but got no error");
Dmitry Lomov950310f2017-03-01 17:45:12 +0000108 } catch (AssertionError e) {
lberkiaea56b32017-05-30 12:35:33 +0200109 assertThat(e)
110 .hasMessageThat()
111 .contains("There is already a built-in attribute 'tags' which cannot be overridden");
Dmitry Lomov7b599452015-11-26 10:07:32 +0000112 }
Florian Weikerte96b0b82015-09-25 11:35:11 +0000113 }
114
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000115 @Test
Vladimir Moskvada574922016-10-05 16:36:49 +0000116 public void testCannotOverrideBuiltInAttributeName() throws Exception {
Dmitry Lomov950310f2017-03-01 17:45:12 +0000117 ev.setFailFast(false);
Vladimir Moskvada574922016-10-05 16:36:49 +0000118 try {
119 evalAndExport(
120 "def impl(ctx): return", "r = rule(impl, attrs = {'name': attr.string()})");
lberki4a45ea82017-06-01 10:05:42 +0200121 fail("Expected error '"
Vladimir Moskvada574922016-10-05 16:36:49 +0000122 + "There is already a built-in attribute 'name' which cannot be overridden"
123 + "' but got no error");
Dmitry Lomov950310f2017-03-01 17:45:12 +0000124 } catch (AssertionError e) {
lberkiaea56b32017-05-30 12:35:33 +0200125 assertThat(e)
126 .hasMessageThat()
127 .contains("There is already a built-in attribute 'name' which cannot be overridden");
Vladimir Moskvada574922016-10-05 16:36:49 +0000128 }
129 }
130
131 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000132 public void testImplicitArgsAttribute() throws Exception {
Dmitry Lomov950310f2017-03-01 17:45:12 +0000133 ev.setFailFast(false);
Dmitry Lomov7b599452015-11-26 10:07:32 +0000134 evalAndExport(
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000135 "def _impl(ctx):",
136 " pass",
137 "exec_rule = rule(implementation = _impl, executable = True)",
138 "non_exec_rule = rule(implementation = _impl)");
lberkiaea56b32017-05-30 12:35:33 +0200139 assertThat(getRuleClass("exec_rule").hasAttr("args", Type.STRING_LIST)).isTrue();
140 assertThat(getRuleClass("non_exec_rule").hasAttr("args", Type.STRING_LIST)).isFalse();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000141 }
142
143 private RuleClass getRuleClass(String name) throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000144 return ((RuleFunction) lookup(name)).getRuleClass();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000145 }
146
147 private void registerDummyUserDefinedFunction() throws Exception {
148 eval("def impl():\n" + " return 0\n");
149 }
150
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000151 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000152 public void testAttrWithOnlyType() throws Exception {
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000153 Attribute attr = buildAttribute("a1", "attr.string_list()");
lberkiaea56b32017-05-30 12:35:33 +0200154 assertThat(attr.getType()).isEqualTo(Type.STRING_LIST);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000155 }
156
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000157 private Attribute buildAttribute(String name, String... lines) throws Exception {
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000158 String[] strings = lines.clone();
159 strings[strings.length - 1] = String.format("%s = %s", name, strings[strings.length - 1]);
160 evalAndExport(strings);
161 Descriptor lookup = (Descriptor) ev.lookup(name);
162 return lookup != null ? lookup.build(name) : null;
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000163 }
164
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000165 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000166 public void testOutputListAttr() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000167 Attribute attr = buildAttribute("a1", "attr.output_list()");
lberkiaea56b32017-05-30 12:35:33 +0200168 assertThat(attr.getType()).isEqualTo(BuildType.OUTPUT_LIST);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000169 }
170
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000171 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000172 public void testIntListAttr() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000173 Attribute attr = buildAttribute("a1", "attr.int_list()");
lberkiaea56b32017-05-30 12:35:33 +0200174 assertThat(attr.getType()).isEqualTo(Type.INTEGER_LIST);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000175 }
176
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000177 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000178 public void testOutputAttr() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000179 Attribute attr = buildAttribute("a1", "attr.output()");
lberkiaea56b32017-05-30 12:35:33 +0200180 assertThat(attr.getType()).isEqualTo(BuildType.OUTPUT);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000181 }
182
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000183 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000184 public void testStringDictAttr() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000185 Attribute attr = buildAttribute("a1", "attr.string_dict(default = {'a': 'b'})");
lberkiaea56b32017-05-30 12:35:33 +0200186 assertThat(attr.getType()).isEqualTo(Type.STRING_DICT);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000187 }
188
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000189 @Test
Francois-Rene Rideau028e3192015-10-29 14:26:59 +0000190 public void testStringListDictAttr() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000191 Attribute attr = buildAttribute("a1", "attr.string_list_dict(default = {'a': ['b', 'c']})");
lberkiaea56b32017-05-30 12:35:33 +0200192 assertThat(attr.getType()).isEqualTo(Type.STRING_LIST_DICT);
Francois-Rene Rideau028e3192015-10-29 14:26:59 +0000193 }
194
195 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000196 public void testAttrAllowedFileTypesAnyFile() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000197 Attribute attr = buildAttribute("a1", "attr.label_list(allow_files = True)");
lberkiaea56b32017-05-30 12:35:33 +0200198 assertThat(attr.getAllowedFileTypesPredicate()).isEqualTo(FileTypeSet.ANY_FILE);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000199 }
200
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000201 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000202 public void testAttrAllowedFileTypesWrongType() throws Exception {
203 checkErrorContains(
Laurent Le Brun2445df12016-05-11 14:36:40 +0000204 "allow_files should be a boolean or a string list",
205 "attr.label_list(allow_files = 18)");
206 }
207
208 @Test
Laurent Le Brun50681c12016-07-05 10:08:54 +0000209 public void testAttrAllowedSingleFileTypesWrongType() throws Exception {
210 checkErrorContains(
211 "allow_single_file should be a boolean or a string list",
212 "attr.label(allow_single_file = 18)");
213 }
214
215 @Test
Laurent Le Brun2445df12016-05-11 14:36:40 +0000216 public void testAttrWithList() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000217 Attribute attr = buildAttribute("a1", "attr.label_list(allow_files = ['.xml'])");
lberkiaea56b32017-05-30 12:35:33 +0200218 assertThat(attr.getAllowedFileTypesPredicate().apply("a.xml")).isTrue();
219 assertThat(attr.getAllowedFileTypesPredicate().apply("a.txt")).isFalse();
220 assertThat(attr.isSingleArtifact()).isFalse();
Laurent Le Brun50681c12016-07-05 10:08:54 +0000221 }
222
223 @Test
224 public void testAttrSingleFileWithList() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000225 Attribute attr = buildAttribute("a1", "attr.label(allow_single_file = ['.xml'])");
lberkiaea56b32017-05-30 12:35:33 +0200226 assertThat(attr.getAllowedFileTypesPredicate().apply("a.xml")).isTrue();
227 assertThat(attr.getAllowedFileTypesPredicate().apply("a.txt")).isFalse();
228 assertThat(attr.isSingleArtifact()).isTrue();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000229 }
230
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000231 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000232 public void testAttrWithSkylarkFileType() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000233 Attribute attr = buildAttribute("a1", "attr.label_list(allow_files = FileType(['.xml']))");
lberkiaea56b32017-05-30 12:35:33 +0200234 assertThat(attr.getAllowedFileTypesPredicate().apply("a.xml")).isTrue();
235 assertThat(attr.getAllowedFileTypesPredicate().apply("a.txt")).isFalse();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000236 }
237
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000238 private static SkylarkProviderIdentifier legacy(String legacyId) {
239 return SkylarkProviderIdentifier.forLegacy(legacyId);
240 }
241
242 private static SkylarkProviderIdentifier declared(String exportedName) {
243 return SkylarkProviderIdentifier.forKey(
244 new SkylarkClassObjectConstructor.SkylarkKey(FAKE_LABEL, exportedName));
245 }
246
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000247 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000248 public void testAttrWithProviders() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000249 Attribute attr =
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000250 buildAttribute("a1",
251 "b = provider()",
252 "attr.label_list(allow_files = True, providers = ['a', b])");
Dmitry Lomovfd2bdc32016-10-07 08:52:10 +0000253 assertThat(attr.getMandatoryProvidersList())
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000254 .containsExactly(ImmutableSet.of(legacy("a"), declared("b")));
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000255 }
256
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000257 @Test
Yun Peng83fbb91a2016-02-23 18:37:44 +0000258 public void testAttrWithProvidersList() throws Exception {
259 Attribute attr =
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000260 buildAttribute("a1",
261 "b = provider()",
262 "attr.label_list(allow_files = True, providers = [['a', b], ['c']])");
Dmitry Lomovfd2bdc32016-10-07 08:52:10 +0000263 assertThat(attr.getMandatoryProvidersList()).containsExactly(
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000264 ImmutableSet.of(legacy("a"), declared("b")),
Dmitry Lomovfd2bdc32016-10-07 08:52:10 +0000265 ImmutableSet.of(legacy("c")));
Yun Peng83fbb91a2016-02-23 18:37:44 +0000266 }
267
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000268 private void checkAttributeError(String expectedMessage, String... lines) throws Exception {
269 ev.setFailFast(false);
270 buildAttribute("fakeAttribute", lines);
271 MoreAsserts.assertContainsEvent(ev.getEventCollector(), expectedMessage);
272 }
273
Yun Peng83fbb91a2016-02-23 18:37:44 +0000274 @Test
275 public void testAttrWithWrongProvidersList() throws Exception {
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000276 checkAttributeError(
277 "element in 'providers' is of unexpected type. Either all elements should be providers,"
278 + " or all elements should be lists of providers,"
279 + " but got list with an element of type int.",
Yun Pengda9410c2016-03-18 21:14:51 +0000280 "attr.label_list(allow_files = True, providers = [['a', 1], ['c']])");
Yun Peng83fbb91a2016-02-23 18:37:44 +0000281
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000282 checkAttributeError(
283 "element in 'providers' is of unexpected type. Either all elements should be providers,"
284 + " or all elements should be lists of providers,"
285 + " but got an element of type string.",
286 "b = provider()",
287 "attr.label_list(allow_files = True, providers = [['a', b], 'c'])");
288
289 checkAttributeError(
290 "element in 'providers' is of unexpected type. Either all elements should be providers,"
291 + " or all elements should be lists of providers,"
292 + " but got an element of type string.",
293 "c = provider()",
294 "attr.label_list(allow_files = True, providers = [['a', b], c])");
295
Yun Peng83fbb91a2016-02-23 18:37:44 +0000296 }
297
298 @Test
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000299 public void testLabelListWithAspects() throws Exception {
Dmitry Lomov950310f2017-03-01 17:45:12 +0000300 evalAndExport(
Yun Pengda9410c2016-03-18 21:14:51 +0000301 "def _impl(target, ctx):",
302 " pass",
303 "my_aspect = aspect(implementation = _impl)",
Dmitry Lomov950310f2017-03-01 17:45:12 +0000304 "a = attr.label_list(aspects = [my_aspect])");
305 SkylarkAttr.Descriptor attr = (SkylarkAttr.Descriptor) ev.lookup("a");
306 SkylarkAspect aspect = (SkylarkAspect) ev.lookup("my_aspect");
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000307 assertThat(aspect).isNotNull();
Dmitry Lomov950310f2017-03-01 17:45:12 +0000308 assertThat(attr.build("xxx").getAspectClasses()).containsExactly(aspect.getAspectClass());
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000309 }
310
311 @Test
Dmitry Lomov950310f2017-03-01 17:45:12 +0000312 public void testLabelWithAspects() throws Exception {
313 evalAndExport(
314 "def _impl(target, ctx):",
315 " pass",
316 "my_aspect = aspect(implementation = _impl)",
317 "a = attr.label(aspects = [my_aspect])");
318 SkylarkAttr.Descriptor attr = (SkylarkAttr.Descriptor) ev.lookup("a");
319 SkylarkAspect aspect = (SkylarkAspect) ev.lookup("my_aspect");
320 assertThat(aspect).isNotNull();
321 assertThat(attr.build("xxx").getAspectClasses()).containsExactly(aspect.getAspectClass());
322 }
323
324
325 @Test
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000326 public void testLabelListWithAspectsError() throws Exception {
327 checkErrorContains(
Laurent Le Brunc31f3512016-12-29 21:41:33 +0000328 "expected type 'Aspect' for 'aspects' element but got type 'int' instead",
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000329 "def _impl(target, ctx):",
330 " pass",
331 "my_aspect = aspect(implementation = _impl)",
Laurent Le Brunc31f3512016-12-29 21:41:33 +0000332 "attr.label_list(aspects = [my_aspect, 123])");
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000333 }
334
335 @Test
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000336 public void testAspectExtraDeps() throws Exception {
337 evalAndExport(
338 "def _impl(target, ctx):",
339 " pass",
340 "my_aspect = aspect(_impl,",
Dmitry Lomovace678e2015-12-16 15:10:20 +0000341 " attrs = { '_extra_deps' : attr.label(default = Label('//foo/bar:baz')) }",
342 ")");
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000343 SkylarkAspect aspect = (SkylarkAspect) ev.lookup("my_aspect");
Googler74558fc2016-05-06 21:47:42 +0000344 Attribute attribute = Iterables.getOnlyElement(aspect.getAttributes());
345 assertThat(attribute.getName()).isEqualTo("$extra_deps");
346 assertThat(attribute.getDefaultValue(null))
Brian Silvermand7d6d622016-03-17 09:53:39 +0000347 .isEqualTo(Label.parseAbsolute("//foo/bar:baz", false));
Dmitry Lomovace678e2015-12-16 15:10:20 +0000348 }
349
350 @Test
Googler74558fc2016-05-06 21:47:42 +0000351 public void testAspectParameter() throws Exception {
352 evalAndExport(
Dmitry Lomovace678e2015-12-16 15:10:20 +0000353 "def _impl(target, ctx):",
354 " pass",
355 "my_aspect = aspect(_impl,",
Googler74558fc2016-05-06 21:47:42 +0000356 " attrs = { 'param' : attr.string(values=['a', 'b']) }",
Dmitry Lomovace678e2015-12-16 15:10:20 +0000357 ")");
Googler74558fc2016-05-06 21:47:42 +0000358 SkylarkAspect aspect = (SkylarkAspect) ev.lookup("my_aspect");
359 Attribute attribute = Iterables.getOnlyElement(aspect.getAttributes());
360 assertThat(attribute.getName()).isEqualTo("param");
361 }
Michajlo Matijkiw6d471412016-08-09 20:35:45 +0000362
Googler74558fc2016-05-06 21:47:42 +0000363 @Test
364 public void testAspectParameterRequiresValues() throws Exception {
365 checkErrorContains(
366 "Aspect parameter attribute 'param' must have type 'string' and use the 'values' "
367 + "restriction.",
368 "def _impl(target, ctx):",
369 " pass",
370 "my_aspect = aspect(_impl,",
371 " attrs = { 'param' : attr.string(default = 'c') }",
372 ")");
373 }
374
375 @Test
376 public void testAspectParameterBadType() throws Exception {
377 checkErrorContains(
378 "Aspect parameter attribute 'param' must have type 'string' and use the 'values' "
379 + "restriction.",
380 "def _impl(target, ctx):",
381 " pass",
382 "my_aspect = aspect(_impl,",
383 " attrs = { 'param' : attr.label(default = Label('//foo/bar:baz')) }",
384 ")");
385 }
386
387 @Test
388 public void testAspectParameterAndExtraDeps() throws Exception {
389 evalAndExport(
390 "def _impl(target, ctx):",
391 " pass",
392 "my_aspect = aspect(_impl,",
393 " attrs = { 'param' : attr.string(values=['a', 'b']),",
394 " '_extra' : attr.label(default = Label('//foo/bar:baz')) }",
395 ")");
396 SkylarkAspect aspect = (SkylarkAspect) ev.lookup("my_aspect");
397 assertThat(aspect.getAttributes()).hasSize(2);
398 assertThat(aspect.getParamAttributes()).containsExactly("param");
Dmitry Lomovace678e2015-12-16 15:10:20 +0000399 }
400
401 @Test
402 public void testAspectNoDefaultValueAttribute() throws Exception {
403 checkErrorContains(
404 "Aspect attribute '_extra_deps' has no default value",
405 "def _impl(target, ctx):",
406 " pass",
407 "my_aspect = aspect(_impl,",
408 " attrs = { '_extra_deps' : attr.label() }",
409 ")");
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000410 }
411
412 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000413 public void testNonLabelAttrWithProviders() throws Exception {
414 checkErrorContains(
415 "unexpected keyword 'providers' in call to string", "attr.string(providers = ['a'])");
416 }
417
418 private static final RuleClass.ConfiguredTargetFactory<Object, Object>
419 DUMMY_CONFIGURED_TARGET_FACTORY =
Yun Pengda9410c2016-03-18 21:14:51 +0000420 new RuleClass.ConfiguredTargetFactory<Object, Object>() {
421 @Override
422 public Object create(Object ruleContext) throws InterruptedException {
423 throw new IllegalStateException();
424 }
425 };
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000426
427 private RuleClass ruleClass(String name) {
428 return new RuleClass.Builder(name, RuleClassType.NORMAL, false)
429 .factory(DUMMY_CONFIGURED_TARGET_FACTORY)
430 .add(Attribute.attr("tags", Type.STRING_LIST))
431 .build();
432 }
vladmos9c787fa2017-07-04 11:45:22 -0400433
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000434 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000435 public void testAttrAllowedRuleClassesSpecificRuleClasses() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000436 Attribute attr = buildAttribute("a",
437 "attr.label_list(allow_rules = ['java_binary'], allow_files = True)");
lberkiaea56b32017-05-30 12:35:33 +0200438 assertThat(attr.getAllowedRuleClassesPredicate().apply(ruleClass("java_binary"))).isTrue();
439 assertThat(attr.getAllowedRuleClassesPredicate().apply(ruleClass("genrule"))).isFalse();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000440 }
vladmos9c787fa2017-07-04 11:45:22 -0400441
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000442 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000443 public void testAttrDefaultValue() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000444 Attribute attr = buildAttribute("a1", "attr.string(default = 'some value')");
lberkiaea56b32017-05-30 12:35:33 +0200445 assertThat(attr.getDefaultValueForTesting()).isEqualTo("some value");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000446 }
447
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000448 @Test
vladmos9c787fa2017-07-04 11:45:22 -0400449 public void testLabelAttrDefaultValueAsString() throws Exception {
450 Attribute sligleAttr = buildAttribute("a1", "attr.label(default = '//foo:bar')");
451 assertThat(sligleAttr.getDefaultValueForTesting())
452 .isEqualTo(Label.parseAbsolute("//foo:bar", false));
453
454 Attribute listAttr =
455 buildAttribute("a2", "attr.label_list(default = ['//foo:bar', '//bar:foo'])");
456 assertThat(listAttr.getDefaultValueForTesting())
457 .isEqualTo(
458 ImmutableList.of(
459 Label.parseAbsolute("//foo:bar", false), Label.parseAbsolute("//bar:foo", false)));
460
461 Attribute dictAttr =
462 buildAttribute("a3", "attr.label_keyed_string_dict(default = {'//foo:bar': 'my value'})");
463 assertThat(dictAttr.getDefaultValueForTesting())
464 .isEqualTo(ImmutableMap.of(Label.parseAbsolute("//foo:bar", false), "my value"));
465 }
466
467 @Test
468 public void testLabelAttrDefaultValueAsStringBadValue() throws Exception {
469 checkErrorContains(
470 "invalid label '/foo:bar' in parameter 'default' of attribute 'label': "
471 + "invalid label: /foo:bar",
472 "attr.label(default = '/foo:bar')");
473
474 checkErrorContains(
475 "invalid label '/bar:foo' in element 1 of parameter 'default' of attribute "
476 + "'label_list': invalid label: /bar:foo",
477 "attr.label_list(default = ['//foo:bar', '/bar:foo'])");
478
479 checkErrorContains(
480 "invalid label '/bar:foo' in dict key element: invalid label: /bar:foo",
481 "attr.label_keyed_string_dict(default = {'//foo:bar': 'a', '/bar:foo': 'b'})");
482 }
483
484 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000485 public void testAttrDefaultValueBadType() throws Exception {
486 checkErrorContains(
Laurent Le Brunc31f3512016-12-29 21:41:33 +0000487 "method attr.string(*, default: string, mandatory: bool, values: sequence of strings) "
488 + "is not applicable for arguments (int, bool, list): 'default' is 'int', "
489 + "but should be 'string'",
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000490 "attr.string(default = 1)");
491 }
492
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000493 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000494 public void testAttrMandatory() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000495 Attribute attr = buildAttribute("a1", "attr.string(mandatory=True)");
lberkiaea56b32017-05-30 12:35:33 +0200496 assertThat(attr.isMandatory()).isTrue();
497 assertThat(attr.isNonEmpty()).isFalse();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000498 }
499
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000500 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000501 public void testAttrNonEmpty() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000502 Attribute attr = buildAttribute("a1", "attr.string_list(non_empty=True)");
lberkiaea56b32017-05-30 12:35:33 +0200503 assertThat(attr.isNonEmpty()).isTrue();
504 assertThat(attr.isMandatory()).isFalse();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000505 }
506
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000507 @Test
Laurent Le Brunbc16f722016-07-06 13:40:24 +0000508 public void testAttrAllowEmpty() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000509 Attribute attr = buildAttribute("a1", "attr.string_list(allow_empty=False)");
lberkiaea56b32017-05-30 12:35:33 +0200510 assertThat(attr.isNonEmpty()).isTrue();
511 assertThat(attr.isMandatory()).isFalse();
Laurent Le Brunbc16f722016-07-06 13:40:24 +0000512 }
513
514 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000515 public void testAttrBadKeywordArguments() throws Exception {
516 checkErrorContains(
517 "unexpected keyword 'bad_keyword' in call to string", "attr.string(bad_keyword = '')");
518 }
519
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000520 @Test
Laurent Le Brunc4ddf6f2016-07-04 13:38:38 +0000521 public void testAttrCfg() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000522 Attribute attr = buildAttribute("a1", "attr.label(cfg = 'host', allow_files = True)");
lberkiaea56b32017-05-30 12:35:33 +0200523 assertThat(attr.getConfigurationTransition()).isEqualTo(ConfigurationTransition.HOST);
Laurent Le Brunc4ddf6f2016-07-04 13:38:38 +0000524 }
525
526 @Test
527 public void testAttrCfgData() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000528 Attribute attr = buildAttribute("a1", "attr.label(cfg = 'data', allow_files = True)");
lberkiaea56b32017-05-30 12:35:33 +0200529 assertThat(attr.getConfigurationTransition()).isEqualTo(ConfigurationTransition.DATA);
Laurent Le Brunc4ddf6f2016-07-04 13:38:38 +0000530 }
531
532 @Test
Vladimir Moskva5a510772016-11-23 19:03:38 +0000533 public void testAttrCfgTarget() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000534 Attribute attr = buildAttribute("a1", "attr.label(cfg = 'target', allow_files = True)");
lberkiaea56b32017-05-30 12:35:33 +0200535 assertThat(attr.getConfigurationTransition()).isEqualTo(ConfigurationTransition.NONE);
Vladimir Moskva5a510772016-11-23 19:03:38 +0000536 }
537
538 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000539 public void testAttrValues() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000540 Attribute attr = buildAttribute("a1", "attr.string(values = ['ab', 'cd'])");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000541 PredicateWithMessage<Object> predicate = attr.getAllowedValues();
542 assertThat(predicate.apply("ab")).isTrue();
543 assertThat(predicate.apply("xy")).isFalse();
544 }
545
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000546 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000547 public void testAttrIntValues() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000548 Attribute attr = buildAttribute("a1", "attr.int(values = [1, 2])");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000549 PredicateWithMessage<Object> predicate = attr.getAllowedValues();
550 assertThat(predicate.apply(2)).isTrue();
551 assertThat(predicate.apply(3)).isFalse();
552 }
553
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000554 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000555 public void testRuleImplementation() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000556 evalAndExport("def impl(ctx): return None", "rule1 = rule(impl)");
557 RuleClass c = ((RuleFunction) lookup("rule1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200558 assertThat(c.getConfiguredTargetFunction().getName()).isEqualTo("impl");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000559 }
560
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000561 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000562 public void testLateBoundAttrWorksWithOnlyLabel() throws Exception {
563 checkEvalError(
Laurent Le Brunc31f3512016-12-29 21:41:33 +0000564 "method attr.string(*, default: string, mandatory: bool, values: sequence of strings) "
565 + "is not applicable for arguments (function, bool, list): 'default' is 'function', "
566 + "but should be 'string'",
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000567 "def attr_value(cfg): return 'a'",
568 "attr.string(default=attr_value)");
569 }
570
Dmitry Lomov7b599452015-11-26 10:07:32 +0000571 private static final Label FAKE_LABEL = Label.parseAbsoluteUnchecked("//fake/label.bzl");
572
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000573 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000574 public void testRuleAddAttribute() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000575 evalAndExport("def impl(ctx): return None", "r1 = rule(impl, attrs={'a1': attr.string()})");
576 RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200577 assertThat(c.hasAttr("a1", Type.STRING)).isTrue();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000578 }
579
Dmitry Lomov7b599452015-11-26 10:07:32 +0000580 protected void evalAndExport(String... lines) throws Exception {
Dmitry Lomov950310f2017-03-01 17:45:12 +0000581 BuildFileAST buildFileAST = BuildFileAST.parseAndValidateSkylarkString(
582 ev.getEnvironment(), lines);
583 SkylarkImportLookupFunction.execAndExport(
584 buildFileAST, FAKE_LABEL, ev.getEventHandler(), ev.getEnvironment());
Dmitry Lomov7b599452015-11-26 10:07:32 +0000585 }
586
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000587 @Test
Jon Brandveinded4fbb2017-01-18 22:21:04 +0000588 public void testExportAliasedName() throws Exception {
589 // When there are multiple names aliasing the same SkylarkExportable, the first one to be
590 // declared should be used. Make sure we're not using lexicographical order, hash order,
591 // non-deterministic order, or anything else.
592 evalAndExport(
593 "def _impl(ctx): pass",
594 "d = rule(implementation = _impl)",
595 "a = d",
596 // Having more names improves the chance that non-determinism will be caught.
597 "b = d",
598 "c = d",
599 "e = d",
600 "f = d",
601 "foo = d",
602 "bar = d",
603 "baz = d",
604 "x = d",
605 "y = d",
606 "z = d");
607 String dName = ((RuleFunction) lookup("d")).getRuleClass().getName();
608 String fooName = ((RuleFunction) lookup("foo")).getRuleClass().getName();
609 assertThat(dName).isEqualTo("d");
610 assertThat(fooName).isEqualTo("d");
611 }
612
613 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000614 public void testOutputToGenfiles() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000615 evalAndExport("def impl(ctx): pass", "r1 = rule(impl, output_to_genfiles=True)");
616 RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200617 assertThat(c.hasBinaryOutput()).isFalse();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000618 }
619
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000620 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000621 public void testRuleAddMultipleAttributes() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000622 evalAndExport(
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000623 "def impl(ctx): return None",
624 "r1 = rule(impl,",
625 " attrs = {",
626 " 'a1': attr.label_list(allow_files=True),",
627 " 'a2': attr.int()",
628 "})");
Dmitry Lomov7b599452015-11-26 10:07:32 +0000629 RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200630 assertThat(c.hasAttr("a1", BuildType.LABEL_LIST)).isTrue();
631 assertThat(c.hasAttr("a2", Type.INTEGER)).isTrue();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000632 }
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000633 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000634 public void testRuleAttributeFlag() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000635 evalAndExport(
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000636 "def impl(ctx): return None",
637 "r1 = rule(impl, attrs = {'a1': attr.string(mandatory=True)})");
Dmitry Lomov7b599452015-11-26 10:07:32 +0000638 RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200639 assertThat(c.getAttributeByName("a1").isMandatory()).isTrue();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000640 }
641
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000642 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000643 public void testRuleOutputs() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000644 evalAndExport(
645 "def impl(ctx): return None",
646 "r1 = rule(impl, outputs = {'a': 'a.txt'})");
647 RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
Michajlo Matijkiw6d471412016-08-09 20:35:45 +0000648 ImplicitOutputsFunction function = c.getDefaultImplicitOutputsFunction();
lberkiaea56b32017-05-30 12:35:33 +0200649 assertThat(function.getImplicitOutputs(null)).containsExactly("a.txt");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000650 }
651
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000652 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000653 public void testRuleUnknownKeyword() throws Exception {
654 registerDummyUserDefinedFunction();
655 checkErrorContains(
656 "unexpected keyword 'bad_keyword' in call to " + "rule(implementation: function, ",
657 "rule(impl, bad_keyword = 'some text')");
658 }
659
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000660 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000661 public void testRuleImplementationMissing() throws Exception {
662 checkErrorContains(
663 "missing mandatory positional argument 'implementation' while calling "
664 + "rule(implementation",
665 "rule(attrs = {})");
666 }
667
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000668 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000669 public void testRuleBadTypeForAdd() throws Exception {
670 registerDummyUserDefinedFunction();
671 checkErrorContains(
672 "expected dict or NoneType for 'attrs' while calling rule but got string instead: "
673 + "some text",
674 "rule(impl, attrs = 'some text')");
675 }
676
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000677 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000678 public void testRuleBadTypeInAdd() throws Exception {
679 registerDummyUserDefinedFunction();
680 checkErrorContains(
Laurent Le Brunc31f3512016-12-29 21:41:33 +0000681 "expected <String, Descriptor> type for 'attrs' but got <string, string> instead",
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000682 "rule(impl, attrs = {'a1': 'some text'})");
683 }
684
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000685 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000686 public void testLabel() throws Exception {
687 Object result = evalRuleClassCode("Label('//foo/foo:foo')");
Dmitry Lomov7b599452015-11-26 10:07:32 +0000688 assertThat(result).isInstanceOf(Label.class);
lberkiaea56b32017-05-30 12:35:33 +0200689 assertThat(result.toString()).isEqualTo("//foo/foo:foo");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000690 }
691
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000692 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000693 public void testLabelSameInstance() throws Exception {
694 Object l1 = evalRuleClassCode("Label('//foo/foo:foo')");
695 // Implicitly creates a new pkgContext and environment, yet labels should be the same.
696 Object l2 = evalRuleClassCode("Label('//foo/foo:foo')");
lberkiaea56b32017-05-30 12:35:33 +0200697 assertThat(l1).isSameAs(l2);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000698 }
699
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000700 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000701 public void testLabelNameAndPackage() throws Exception {
702 Object result = evalRuleClassCode("Label('//foo/bar:baz').name");
lberkiaea56b32017-05-30 12:35:33 +0200703 assertThat(result).isEqualTo("baz");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000704 // NB: implicitly creates a new pkgContext and environments, yet labels should be the same.
705 result = evalRuleClassCode("Label('//foo/bar:baz').package");
lberkiaea56b32017-05-30 12:35:33 +0200706 assertThat(result).isEqualTo("foo/bar");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000707 }
708
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000709 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000710 public void testRuleLabelDefaultValue() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000711 evalAndExport(
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000712 "def impl(ctx): return None\n"
713 + "r1 = rule(impl, attrs = {'a1': "
714 + "attr.label(default = Label('//foo:foo'), allow_files=True)})");
Dmitry Lomov7b599452015-11-26 10:07:32 +0000715 RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000716 Attribute a = c.getAttributeByName("a1");
Dmitry Lomov7b599452015-11-26 10:07:32 +0000717 assertThat(a.getDefaultValueForTesting()).isInstanceOf(Label.class);
lberkiaea56b32017-05-30 12:35:33 +0200718 assertThat(a.getDefaultValueForTesting().toString()).isEqualTo("//foo:foo");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000719 }
720
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000721 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000722 public void testIntDefaultValue() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000723 evalAndExport(
724 "def impl(ctx): return None",
725 "r1 = rule(impl, attrs = {'a1': attr.int(default = 40+2)})");
726 RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000727 Attribute a = c.getAttributeByName("a1");
lberkiaea56b32017-05-30 12:35:33 +0200728 assertThat(a.getDefaultValueForTesting()).isEqualTo(42);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000729 }
730
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000731 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000732 public void testFileType() throws Exception {
733 Object result = evalRuleClassCode("FileType(['.css'])");
734 SkylarkFileType fts = (SkylarkFileType) result;
lberkiaea56b32017-05-30 12:35:33 +0200735 assertThat(fts.getExtensions()).isEqualTo(ImmutableList.of(".css"));
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000736 }
737
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000738 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000739 public void testRuleInheritsBaseRuleAttributes() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000740 evalAndExport("def impl(ctx): return None", "r1 = rule(impl)");
741 RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200742 assertThat(c.hasAttr("tags", Type.STRING_LIST)).isTrue();
743 assertThat(c.hasAttr("visibility", BuildType.NODEP_LABEL_LIST)).isTrue();
744 assertThat(c.hasAttr("deprecation", Type.STRING)).isTrue();
745 assertThat(c.hasAttr(":action_listener", BuildType.LABEL_LIST))
746 .isTrue(); // required for extra actions
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000747 }
748
749 private void checkTextMessage(String from, String... lines) throws Exception {
750 Object result = evalRuleClassCode(from);
lberkiaea56b32017-05-30 12:35:33 +0200751 assertThat(result).isEqualTo(Joiner.on("\n").join(lines) + "\n");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000752 }
753
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000754 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000755 public void testSimpleTextMessagesBooleanFields() throws Exception {
756 checkTextMessage("struct(name=True).to_proto()", "name: true");
757 checkTextMessage("struct(name=False).to_proto()", "name: false");
758 }
759
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000760 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000761 public void testSimpleTextMessages() throws Exception {
762 checkTextMessage("struct(name='value').to_proto()", "name: \"value\"");
763 checkTextMessage("struct(name=['a', 'b']).to_proto()", "name: \"a\"", "name: \"b\"");
764 checkTextMessage("struct(name=123).to_proto()", "name: 123");
765 checkTextMessage("struct(name=[1, 2, 3]).to_proto()", "name: 1", "name: 2", "name: 3");
766 checkTextMessage("struct(a=struct(b='b')).to_proto()", "a {", " b: \"b\"", "}");
767 checkTextMessage(
768 "struct(a=[struct(b='x'), struct(b='y')]).to_proto()",
769 "a {",
770 " b: \"x\"",
771 "}",
772 "a {",
773 " b: \"y\"",
774 "}");
775 checkTextMessage(
776 "struct(a=struct(b=struct(c='c'))).to_proto()", "a {", " b {", " c: \"c\"", " }", "}");
777 }
778
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000779 @Test
Vladimir Moskva76e31d12016-12-05 16:28:37 +0000780 public void testProtoFieldsOrder() throws Exception {
781 checkTextMessage("struct(d=4, b=2, c=3, a=1).to_proto()", "a: 1", "b: 2", "c: 3", "d: 4");
782 }
783
784 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000785 public void testTextMessageEscapes() throws Exception {
786 checkTextMessage("struct(name='a\"b').to_proto()", "name: \"a\\\"b\"");
787 checkTextMessage("struct(name='a\\'b').to_proto()", "name: \"a'b\"");
788 checkTextMessage("struct(name='a\\nb').to_proto()", "name: \"a\\nb\"");
Googler4489aaf2016-06-17 15:17:37 +0000789
790 // struct(name="a\\\"b") -> name: "a\\\"b"
791 checkTextMessage("struct(name='a\\\\\\\"b').to_proto()", "name: \"a\\\\\\\"b\"");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000792 }
793
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000794 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000795 public void testTextMessageInvalidElementInListStructure() throws Exception {
796 checkErrorContains(
797 "Invalid text format, expected a struct, a string, a bool, or "
798 + "an int but got a list for list element in struct field 'a'",
799 "struct(a=[['b']]).to_proto()");
800 }
801
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000802 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000803 public void testTextMessageInvalidStructure() throws Exception {
804 checkErrorContains(
805 "Invalid text format, expected a struct, a string, a bool, or an int "
Vladimir Moskvacd12f772017-01-10 12:47:06 +0000806 + "but got a function for struct field 'a'",
807 "struct(a=rule).to_proto()");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000808 }
809
Erik Abair927f4592016-02-29 18:57:22 +0000810 private void checkJson(String from, String expected) throws Exception {
811 Object result = evalRuleClassCode(from);
lberkiaea56b32017-05-30 12:35:33 +0200812 assertThat(result).isEqualTo(expected);
Erik Abair927f4592016-02-29 18:57:22 +0000813 }
814
815 @Test
816 public void testJsonBooleanFields() throws Exception {
817 checkJson("struct(name=True).to_json()", "{\"name\":true}");
818 checkJson("struct(name=False).to_json()", "{\"name\":false}");
819 }
820
821 @Test
822 public void testJsonEncoding() throws Exception {
823 checkJson("struct(name='value').to_json()", "{\"name\":\"value\"}");
824 checkJson("struct(name=['a', 'b']).to_json()", "{\"name\":[\"a\",\"b\"]}");
825 checkJson("struct(name=123).to_json()", "{\"name\":123}");
826 checkJson("struct(name=[1, 2, 3]).to_json()", "{\"name\":[1,2,3]}");
827 checkJson("struct(a=struct(b='b')).to_json()", "{\"a\":{\"b\":\"b\"}}");
828 checkJson("struct(a=[struct(b='x'), struct(b='y')]).to_json()",
829 "{\"a\":[{\"b\":\"x\"},{\"b\":\"y\"}]}");
830 checkJson("struct(a=struct(b=struct(c='c'))).to_json()",
831 "{\"a\":{\"b\":{\"c\":\"c\"}}}");
832 }
833
834 @Test
835 public void testJsonEscapes() throws Exception {
836 checkJson("struct(name='a\"b').to_json()", "{\"name\":\"a\\\"b\"}");
837 checkJson("struct(name='a\\'b').to_json()", "{\"name\":\"a'b\"}");
Erik Abairec1f2b92016-03-01 00:45:33 +0000838 checkJson("struct(name='a\\\\b').to_json()", "{\"name\":\"a\\\\b\"}");
Erik Abair927f4592016-02-29 18:57:22 +0000839 checkJson("struct(name='a\\nb').to_json()", "{\"name\":\"a\\nb\"}");
Erik Abairec1f2b92016-03-01 00:45:33 +0000840 checkJson("struct(name='a\\rb').to_json()", "{\"name\":\"a\\rb\"}");
841 checkJson("struct(name='a\\tb').to_json()", "{\"name\":\"a\\tb\"}");
Erik Abair927f4592016-02-29 18:57:22 +0000842 }
843
844 @Test
845 public void testJsonNestedListStructure() throws Exception {
846 checkJson("struct(a=[['b']]).to_json()", "{\"a\":[[\"b\"]]}");
847 }
848
849 @Test
850 public void testJsonInvalidStructure() throws Exception {
851 checkErrorContains(
852 "Invalid text format, expected a struct, a string, a bool, or an int but got a "
Vladimir Moskvacd12f772017-01-10 12:47:06 +0000853 + "function for struct field 'a'",
854 "struct(a=rule).to_json()");
Erik Abair927f4592016-02-29 18:57:22 +0000855 }
856
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000857 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000858 public void testLabelAttrWrongDefault() throws Exception {
859 checkErrorContains(
vladmos9c787fa2017-07-04 11:45:22 -0400860 "expected value of type 'string' for parameter 'default' of attribute 'label', "
861 + "but got 123 (int)",
862 "attr.label(default = 123)");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000863 }
864
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000865 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000866 public void testLabelGetRelative() throws Exception {
lberkiaea56b32017-05-30 12:35:33 +0200867 assertThat(eval("Label('//foo:bar').relative('baz')").toString()).isEqualTo("//foo:baz");
868 assertThat(eval("Label('//foo:bar').relative('//baz:qux')").toString()).isEqualTo("//baz:qux");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000869 }
870
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000871 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000872 public void testLabelGetRelativeSyntaxError() throws Exception {
873 checkErrorContains(
Damien Martin-Guillerez934c1d52017-03-03 14:44:56 +0000874 "invalid target name 'bad//syntax': target names may not contain '//' path separators",
875 "Label('//foo:bar').relative('bad//syntax')");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000876 }
Greg Estren223976c2016-02-04 22:40:56 +0000877
878 @Test
879 public void testLicenseAttributesNonconfigurable() throws Exception {
880 scratch.file("test/BUILD");
881 scratch.file("test/rule.bzl",
882 "def _impl(ctx):",
883 " return",
884 "some_rule = rule(",
885 " implementation = _impl,",
886 " attrs = {",
887 " 'licenses': attr.license()",
888 " }",
889 ")");
890 scratch.file("third_party/foo/BUILD",
891 "load('/test/rule', 'some_rule')",
892 "some_rule(",
893 " name='r',",
894 " licenses = ['unencumbered']",
895 ")");
896 invalidatePackages();
897 // Should succeed without a "licenses attribute is potentially configurable" loading error:
898 createRuleContext("//third_party/foo:r");
899 }
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +0000900
901 @Test
902 public void testStructCreation() throws Exception {
903 // TODO(fwe): cannot be handled by current testing suite
904 eval("x = struct(a = 1, b = 2)");
905 assertThat(lookup("x")).isInstanceOf(ClassObject.class);
906 }
907
908 @Test
909 public void testStructFields() throws Exception {
910 // TODO(fwe): cannot be handled by current testing suite
911 eval("x = struct(a = 1, b = 2)");
912 ClassObject x = (ClassObject) lookup("x");
lberkiaea56b32017-05-30 12:35:33 +0200913 assertThat(x.getValue("a")).isEqualTo(1);
914 assertThat(x.getValue("b")).isEqualTo(2);
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +0000915 }
916
917 @Test
Vladimir Moskvafdfa9882016-11-18 16:24:20 +0000918 public void testStructEquality() throws Exception {
lberkiaea56b32017-05-30 12:35:33 +0200919 assertThat((Boolean) eval("struct(a = 1, b = 2) == struct(b = 2, a = 1)")).isTrue();
920 assertThat((Boolean) eval("struct(a = 1) == struct(a = 1, b = 2)")).isFalse();
921 assertThat((Boolean) eval("struct(a = 1, b = 2) == struct(a = 1)")).isFalse();
Vladimir Moskvafdfa9882016-11-18 16:24:20 +0000922 // Compare a recursive object to itself to make sure reference equality is checked
lberkiaea56b32017-05-30 12:35:33 +0200923 assertThat((Boolean) eval("s = (struct(a = 1, b = [])); s.b.append(s); s == s")).isTrue();
924 assertThat((Boolean) eval("struct(a = 1, b = 2) == struct(a = 1, b = 3)")).isFalse();
925 assertThat((Boolean) eval("struct(a = 1) == [1]")).isFalse();
926 assertThat((Boolean) eval("[1] == struct(a = 1)")).isFalse();
927 assertThat((Boolean) eval("struct() == struct()")).isTrue();
928 assertThat((Boolean) eval("struct() == struct(a = 1)")).isFalse();
Vladimir Moskvafdfa9882016-11-18 16:24:20 +0000929
930 eval("foo = provider(); bar = provider()");
lberkiaea56b32017-05-30 12:35:33 +0200931 assertThat((Boolean) eval("struct(a = 1) == foo(a = 1)")).isFalse();
932 assertThat((Boolean) eval("foo(a = 1) == struct(a = 1)")).isFalse();
933 assertThat((Boolean) eval("foo(a = 1) == bar(a = 1)")).isFalse();
934 assertThat((Boolean) eval("foo(a = 1) == foo(a = 1)")).isTrue();
Vladimir Moskvafdfa9882016-11-18 16:24:20 +0000935 }
936
937 @Test
Vladimir Moskva76e31d12016-12-05 16:28:37 +0000938 public void testStructIncomparability() throws Exception {
939 checkErrorContains("Cannot compare structs", "struct(a = 1) < struct(a = 2)");
940 checkErrorContains("Cannot compare structs", "struct(a = 1) > struct(a = 2)");
941 checkErrorContains("Cannot compare structs", "struct(a = 1) <= struct(a = 2)");
942 checkErrorContains("Cannot compare structs", "struct(a = 1) >= struct(a = 2)");
943 }
944
945 @Test
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +0000946 public void testStructAccessingFieldsFromSkylark() throws Exception {
947 eval("x = struct(a = 1, b = 2)", "x1 = x.a", "x2 = x.b");
948 assertThat(lookup("x1")).isEqualTo(1);
949 assertThat(lookup("x2")).isEqualTo(2);
950 }
951
952 @Test
953 public void testStructAccessingUnknownField() throws Exception {
954 checkErrorContains(
955 "'struct' object has no attribute 'c'\n" + "Available attributes: a, b",
956 "x = struct(a = 1, b = 2)",
957 "y = x.c");
958 }
959
960 @Test
961 public void testStructAccessingUnknownFieldWithArgs() throws Exception {
962 checkErrorContains(
963 "struct has no method 'c'", "x = struct(a = 1, b = 2)", "y = x.c()");
964 }
965
966 @Test
967 public void testStructAccessingNonFunctionFieldWithArgs() throws Exception {
968 checkErrorContains(
969 "struct field 'a' is not a function", "x = struct(a = 1, b = 2)", "x1 = x.a(1)");
970 }
971
972 @Test
973 public void testStructAccessingFunctionFieldWithArgs() throws Exception {
974 eval("def f(x): return x+5", "x = struct(a = f, b = 2)", "x1 = x.a(1)");
975 assertThat(lookup("x1")).isEqualTo(6);
976 }
977
978 @Test
979 public void testStructPosArgs() throws Exception {
980 checkErrorContains(
981 "struct(**kwargs) does not accept positional arguments, but got 1", "x = struct(1, b = 2)");
982 }
983
984 @Test
985 public void testStructConcatenationFieldNames() throws Exception {
986 // TODO(fwe): cannot be handled by current testing suite
987 eval("x = struct(a = 1, b = 2)",
988 "y = struct(c = 1, d = 2)",
989 "z = x + y\n");
990 SkylarkClassObject z = (SkylarkClassObject) lookup("z");
lberkiaea56b32017-05-30 12:35:33 +0200991 assertThat(z.getKeys()).isEqualTo(ImmutableSet.of("a", "b", "c", "d"));
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +0000992 }
993
994 @Test
995 public void testStructConcatenationFieldValues() throws Exception {
996 // TODO(fwe): cannot be handled by current testing suite
997 eval("x = struct(a = 1, b = 2)",
998 "y = struct(c = 1, d = 2)",
999 "z = x + y\n");
1000 SkylarkClassObject z = (SkylarkClassObject) lookup("z");
lberkiaea56b32017-05-30 12:35:33 +02001001 assertThat(z.getValue("a")).isEqualTo(1);
1002 assertThat(z.getValue("b")).isEqualTo(2);
1003 assertThat(z.getValue("c")).isEqualTo(1);
1004 assertThat(z.getValue("d")).isEqualTo(2);
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001005 }
1006
1007 @Test
1008 public void testStructConcatenationCommonFields() throws Exception {
1009 checkErrorContains("Cannot concat structs with common field(s): a",
1010 "x = struct(a = 1, b = 2)", "y = struct(c = 1, a = 2)", "z = x + y\n");
1011 }
1012
1013 @Test
1014 public void testConditionalStructConcatenation() throws Exception {
1015 // TODO(fwe): cannot be handled by current testing suite
1016 eval("def func():",
1017 " x = struct(a = 1, b = 2)",
1018 " if True:",
1019 " x += struct(c = 1, d = 2)",
1020 " return x",
1021 "x = func()");
1022 SkylarkClassObject x = (SkylarkClassObject) lookup("x");
lberkiaea56b32017-05-30 12:35:33 +02001023 assertThat(x.getValue("a")).isEqualTo(1);
1024 assertThat(x.getValue("b")).isEqualTo(2);
1025 assertThat(x.getValue("c")).isEqualTo(1);
1026 assertThat(x.getValue("d")).isEqualTo(2);
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001027 }
1028
1029 @Test
1030 public void testGetattrNoAttr() throws Exception {
Laurent Le Brunc31f3512016-12-29 21:41:33 +00001031 checkErrorContains(
1032 "object of type 'struct' has no attribute \"b\"", "s = struct(a='val')", "getattr(s, 'b')");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001033 }
1034
1035 @Test
1036 public void testGetattr() throws Exception {
1037 eval(
1038 "s = struct(a='val')",
1039 "x = getattr(s, 'a')",
1040 "y = getattr(s, 'b', 'def')",
1041 "z = getattr(s, 'b', default = 'def')",
1042 "w = getattr(s, 'a', default='ignored')");
1043 assertThat(lookup("x")).isEqualTo("val");
1044 assertThat(lookup("y")).isEqualTo("def");
1045 assertThat(lookup("z")).isEqualTo("def");
1046 assertThat(lookup("w")).isEqualTo("val");
1047 }
1048
1049 @Test
1050 public void testHasattr() throws Exception {
1051 eval("s = struct(a=1)",
1052 "x = hasattr(s, 'a')",
1053 "y = hasattr(s, 'b')\n");
1054 assertThat(lookup("x")).isEqualTo(true);
1055 assertThat(lookup("y")).isEqualTo(false);
1056 }
1057
1058 @Test
1059 public void testStructStr() throws Exception {
1060 assertThat(eval("str(struct(x = 2, y = 3, z = 4))"))
1061 .isEqualTo("struct(x = 2, y = 3, z = 4)");
1062 }
1063
1064 @Test
1065 public void testStructsInSets() throws Exception {
Vladimir Moskvad200daf2016-12-23 16:35:37 +00001066 eval("depset([struct(a='a')])");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001067 }
1068
1069 @Test
Vladimir Moskva76e31d12016-12-05 16:28:37 +00001070 public void testStructsInDicts() throws Exception {
1071 eval("d = {struct(a = 1): 'aa', struct(b = 2): 'bb'}");
1072 assertThat(eval("d[struct(a = 1)]")).isEqualTo("aa");
1073 assertThat(eval("d[struct(b = 2)]")).isEqualTo("bb");
1074 assertThat(eval("str([d[k] for k in d])")).isEqualTo("[\"aa\", \"bb\"]");
1075
1076 checkErrorContains(
1077 "unhashable type: 'struct'",
1078 "{struct(a = []): 'foo'}");
1079 }
1080
1081 @Test
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001082 public void testStructMembersAreImmutable() throws Exception {
1083 checkErrorContains(
Jon Brandvein162f9eb2016-11-10 00:22:43 +00001084 "cannot assign to 's.x'",
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001085 "s = struct(x = 'a')",
1086 "s.x = 'b'\n");
1087 }
1088
1089 @Test
Vladimir Moskva10770382016-08-23 15:04:54 +00001090 public void testStructDictMembersAreMutable() throws Exception {
1091 eval(
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001092 "s = struct(x = {'a' : 1})",
1093 "s.x['b'] = 2\n");
Vladimir Moskva10770382016-08-23 15:04:54 +00001094 assertThat(((SkylarkClassObject) lookup("s")).getValue("x"))
1095 .isEqualTo(ImmutableMap.of("a", 1, "b", 2));
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001096 }
1097
1098 @Test
1099 public void testNsetGoodCompositeItem() throws Exception {
Vladimir Moskvad200daf2016-12-23 16:35:37 +00001100 eval("def func():", " return depset([struct(a='a')])", "s = func()");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001101 Collection<Object> result = ((SkylarkNestedSet) lookup("s")).toCollection();
1102 assertThat(result).hasSize(1);
1103 assertThat(result.iterator().next()).isInstanceOf(SkylarkClassObject.class);
1104 }
1105
1106 @Test
1107 public void testNsetBadMutableItem() throws Exception {
Vladimir Moskvad200daf2016-12-23 16:35:37 +00001108 checkEvalError("depsets cannot contain mutable items", "depset([([],)])");
1109 checkEvalError("depsets cannot contain mutable items", "depset([struct(a=[])])");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001110 }
1111
1112 private static SkylarkClassObject makeStruct(String field, Object value) {
Dmitry Lomov654717f2017-03-02 14:39:52 +00001113 return NativeClassObjectConstructor.STRUCT.create(
Dmitry Lomovea9de072016-08-09 09:35:40 +00001114 ImmutableMap.of(field, value),
1115 "no field '%'");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001116 }
1117
1118 private static SkylarkClassObject makeBigStruct(Environment env) {
1119 // struct(a=[struct(x={1:1}), ()], b=(), c={2:2})
Dmitry Lomov654717f2017-03-02 14:39:52 +00001120 return NativeClassObjectConstructor.STRUCT.create(
Dmitry Lomovea9de072016-08-09 09:35:40 +00001121 ImmutableMap.<String, Object>of(
1122 "a", MutableList.<Object>of(env,
Dmitry Lomov654717f2017-03-02 14:39:52 +00001123 NativeClassObjectConstructor.STRUCT.create(ImmutableMap.<String, Object>of(
Dmitry Lomovea9de072016-08-09 09:35:40 +00001124 "x", SkylarkDict.<Object, Object>of(env, 1, 1)),
1125 "no field '%s'"),
1126 Tuple.of()),
1127 "b", Tuple.of(),
1128 "c", SkylarkDict.<Object, Object>of(env, 2, 2)),
1129 "no field '%s'");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001130 }
1131
1132 @Test
1133 public void testStructMutabilityShallow() throws Exception {
lberkiaea56b32017-05-30 12:35:33 +02001134 assertThat(EvalUtils.isImmutable(makeStruct("a", 1))).isTrue();
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001135 }
1136
1137 private static MutableList<Object> makeList(Environment env) {
1138 return MutableList.<Object>of(env, 1, 2, 3);
1139 }
1140
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001141 @Test
1142 public void testStructMutabilityDeep() throws Exception {
lberkiaea56b32017-05-30 12:35:33 +02001143 assertThat(EvalUtils.isImmutable(Tuple.<Object>of(makeList(null)))).isTrue();
1144 assertThat(EvalUtils.isImmutable(makeStruct("a", makeList(null)))).isTrue();
1145 assertThat(EvalUtils.isImmutable(makeBigStruct(null))).isTrue();
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001146
lberkiaea56b32017-05-30 12:35:33 +02001147 assertThat(EvalUtils.isImmutable(Tuple.<Object>of(makeList(ev.getEnvironment())))).isFalse();
1148 assertThat(EvalUtils.isImmutable(makeStruct("a", makeList(ev.getEnvironment())))).isFalse();
1149 assertThat(EvalUtils.isImmutable(makeBigStruct(ev.getEnvironment()))).isFalse();
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001150 }
1151
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001152 @Test
1153 public void declaredProviders() throws Exception {
1154 evalAndExport(
1155 "data = provider()",
1156 "d = data(x = 1, y ='abc')",
1157 "d_x = d.x",
1158 "d_y = d.y"
1159 );
1160 assertThat(lookup("d_x")).isEqualTo(1);
1161 assertThat(lookup("d_y")).isEqualTo("abc");
1162 SkylarkClassObjectConstructor dataConstructor = (SkylarkClassObjectConstructor) lookup("data");
1163 SkylarkClassObject data = (SkylarkClassObject) lookup("d");
1164 assertThat(data.getConstructor()).isEqualTo(dataConstructor);
1165 assertThat(dataConstructor.isExported()).isTrue();
1166 assertThat(dataConstructor.getPrintableName()).isEqualTo("data");
1167 assertThat(dataConstructor.getKey()).isEqualTo(
Dmitry Lomov94b2c882016-09-25 20:24:24 +00001168 new SkylarkClassObjectConstructor.SkylarkKey(FAKE_LABEL, "data")
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001169 );
1170 }
1171
1172 @Test
1173 public void declaredProvidersConcatSuccess() throws Exception {
1174 evalAndExport(
1175 "data = provider()",
1176 "dx = data(x = 1)",
1177 "dy = data(y = 'abc')",
1178 "dxy = dx + dy",
1179 "x = dxy.x",
1180 "y = dxy.y"
1181 );
1182 assertThat(lookup("x")).isEqualTo(1);
1183 assertThat(lookup("y")).isEqualTo("abc");
1184 SkylarkClassObjectConstructor dataConstructor = (SkylarkClassObjectConstructor) lookup("data");
1185 SkylarkClassObject dx = (SkylarkClassObject) lookup("dx");
1186 assertThat(dx.getConstructor()).isEqualTo(dataConstructor);
1187 SkylarkClassObject dy = (SkylarkClassObject) lookup("dy");
1188 assertThat(dy.getConstructor()).isEqualTo(dataConstructor);
1189 }
1190
1191 @Test
1192 public void declaredProvidersConcatError() throws Exception {
1193 evalAndExport(
1194 "data1 = provider()",
1195 "data2 = provider()"
1196 );
1197
1198 checkEvalError("Cannot concat data1 with data2",
1199 "d1 = data1(x = 1)",
1200 "d2 = data2(y = 2)",
1201 "d = d1 + d2"
1202 );
1203 }
Dmitry Lomov94b2c882016-09-25 20:24:24 +00001204
1205 @Test
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +00001206
Dmitry Lomov94b2c882016-09-25 20:24:24 +00001207 public void structsAsDeclaredProvidersTest() throws Exception {
1208 evalAndExport(
1209 "data = struct(x = 1)"
1210 );
1211 SkylarkClassObject data = (SkylarkClassObject) lookup("data");
Dmitry Lomov654717f2017-03-02 14:39:52 +00001212 assertThat(NativeClassObjectConstructor.STRUCT.isExported()).isTrue();
1213 assertThat(data.getConstructor()).isEqualTo(NativeClassObjectConstructor.STRUCT);
Dmitry Lomov94b2c882016-09-25 20:24:24 +00001214 assertThat(data.getConstructor().getKey())
Dmitry Lomov654717f2017-03-02 14:39:52 +00001215 .isEqualTo(NativeClassObjectConstructor.STRUCT.getKey());
Dmitry Lomov94b2c882016-09-25 20:24:24 +00001216 }
Dmitry Lomov0692c7f2016-09-30 16:43:30 +00001217
1218 @Test
1219 public void aspectAllAttrs() throws Exception {
1220 evalAndExport(
1221 "def _impl(target, ctx):",
1222 " pass",
1223 "my_aspect = aspect(_impl, attr_aspects=['*'])");
1224
1225 SkylarkAspect myAspect = (SkylarkAspect) lookup("my_aspect");
Dmitry Lomov0d380642017-01-20 14:39:55 +00001226 assertThat(myAspect.getDefinition(AspectParameters.EMPTY).propagateAlong(
Dmitry Lomov0692c7f2016-09-30 16:43:30 +00001227 Attribute.attr("foo", BuildType.LABEL).allowedFileTypes().build()
Dmitry Lomov0d380642017-01-20 14:39:55 +00001228 )).isTrue();
Dmitry Lomov0692c7f2016-09-30 16:43:30 +00001229 }
1230
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001231 @Test
1232 public void aspectRequiredAspectProvidersSingle() throws Exception {
1233 evalAndExport(
1234 "def _impl(target, ctx):",
1235 " pass",
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001236 "cc = provider()",
1237 "my_aspect = aspect(_impl, required_aspect_providers=['java', cc])"
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001238 );
1239 SkylarkAspect myAspect = (SkylarkAspect) lookup("my_aspect");
1240 RequiredProviders requiredProviders = myAspect.getDefinition(AspectParameters.EMPTY)
1241 .getRequiredProvidersForAspects();
1242 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.ANY)).isTrue();
1243 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.EMPTY)).isFalse();
1244 assertThat(requiredProviders.isSatisfiedBy(
1245 AdvertisedProviderSet.builder()
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001246 .addSkylark(declared("cc"))
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001247 .addSkylark("java")
1248 .build()))
1249 .isTrue();
1250 assertThat(requiredProviders.isSatisfiedBy(
1251 AdvertisedProviderSet.builder()
1252 .addSkylark("cc")
1253 .build()))
1254 .isFalse();
1255 }
1256
1257 @Test
1258 public void aspectRequiredAspectProvidersAlternatives() throws Exception {
1259 evalAndExport(
1260 "def _impl(target, ctx):",
1261 " pass",
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001262 "cc = provider()",
1263 "my_aspect = aspect(_impl, required_aspect_providers=[['java'], [cc]])"
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001264 );
1265 SkylarkAspect myAspect = (SkylarkAspect) lookup("my_aspect");
1266 RequiredProviders requiredProviders = myAspect.getDefinition(AspectParameters.EMPTY)
1267 .getRequiredProvidersForAspects();
1268 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.ANY)).isTrue();
1269 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.EMPTY)).isFalse();
1270 assertThat(requiredProviders.isSatisfiedBy(
1271 AdvertisedProviderSet.builder()
1272 .addSkylark("java")
1273 .build()))
1274 .isTrue();
1275 assertThat(requiredProviders.isSatisfiedBy(
1276 AdvertisedProviderSet.builder()
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001277 .addSkylark(declared("cc"))
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001278 .build()))
1279 .isTrue();
1280 assertThat(requiredProviders.isSatisfiedBy(
1281 AdvertisedProviderSet.builder()
1282 .addSkylark("prolog")
1283 .build()))
1284 .isFalse();
1285 }
1286
1287 @Test
1288 public void aspectRequiredAspectProvidersEmpty() throws Exception {
1289 evalAndExport(
1290 "def _impl(target, ctx):",
1291 " pass",
1292 "my_aspect = aspect(_impl, required_aspect_providers=[])"
1293 );
1294 SkylarkAspect myAspect = (SkylarkAspect) lookup("my_aspect");
1295 RequiredProviders requiredProviders = myAspect.getDefinition(AspectParameters.EMPTY)
1296 .getRequiredProvidersForAspects();
1297 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.ANY)).isFalse();
1298 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.EMPTY)).isFalse();
1299 }
1300
1301 @Test
1302 public void aspectRequiredAspectProvidersDefault() throws Exception {
1303 evalAndExport(
1304 "def _impl(target, ctx):",
1305 " pass",
1306 "my_aspect = aspect(_impl)"
1307 );
1308 SkylarkAspect myAspect = (SkylarkAspect) lookup("my_aspect");
1309 RequiredProviders requiredProviders = myAspect.getDefinition(AspectParameters.EMPTY)
1310 .getRequiredProvidersForAspects();
1311 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.ANY)).isFalse();
1312 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.EMPTY)).isFalse();
1313 }
1314
Dmitry Lomov950310f2017-03-01 17:45:12 +00001315 @Test
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001316 public void aspectProvides() throws Exception {
1317 evalAndExport(
1318 "def _impl(target, ctx):",
1319 " pass",
1320 "y = provider()",
1321 "my_aspect = aspect(_impl, provides = ['x', y])"
1322 );
1323 SkylarkAspect myAspect = (SkylarkAspect) lookup("my_aspect");
1324 AdvertisedProviderSet advertisedProviders = myAspect.getDefinition(AspectParameters.EMPTY)
1325 .getAdvertisedProviders();
1326 assertThat(advertisedProviders.canHaveAnyProvider()).isFalse();
1327 assertThat(advertisedProviders.getSkylarkProviders())
1328 .containsExactly(legacy("x"), declared("y"));
1329 }
1330
1331 @Test
1332 public void aspectProvidesError() throws Exception {
1333 ev.setFailFast(false);
1334 evalAndExport(
1335 "def _impl(target, ctx):",
1336 " pass",
1337 "y = provider()",
1338 "my_aspect = aspect(_impl, provides = ['x', 1])"
1339 );
1340 MoreAsserts.assertContainsEvent(ev.getEventCollector(),
1341 " Illegal argument: element in 'provides' is of unexpected type."
1342 + " Should be list of providers, but got int. ");
1343 }
1344
1345
1346
1347 @Test
Dmitry Lomov950310f2017-03-01 17:45:12 +00001348 public void fancyExports() throws Exception {
1349 evalAndExport(
1350 "def _impla(target, ctx): pass",
1351 "p, (a, p1) = [",
1352 " provider(),",
1353 " [ aspect(_impla),",
1354 " provider() ]",
1355 "]"
1356 );
1357 SkylarkClassObjectConstructor p = (SkylarkClassObjectConstructor) lookup("p");
1358 SkylarkAspect a = (SkylarkAspect) lookup("a");
1359 SkylarkClassObjectConstructor p1 = (SkylarkClassObjectConstructor) lookup("p1");
1360 assertThat(p.getPrintableName()).isEqualTo("p");
1361 assertThat(p.getKey())
1362 .isEqualTo(new SkylarkClassObjectConstructor.SkylarkKey(FAKE_LABEL, "p"));
1363 assertThat(p1.getPrintableName()).isEqualTo("p1");
1364 assertThat(p1.getKey())
1365 .isEqualTo(new SkylarkClassObjectConstructor.SkylarkKey(FAKE_LABEL, "p1"));
1366 assertThat(a.getAspectClass()).isEqualTo(
1367 new SkylarkAspectClass(FAKE_LABEL, "a")
1368 );
1369 }
1370
1371 @Test
1372 public void multipleTopLevels() throws Exception {
1373 evalAndExport(
1374 "p = provider()",
1375 "p1 = p"
1376 );
1377 SkylarkClassObjectConstructor p = (SkylarkClassObjectConstructor) lookup("p");
1378 SkylarkClassObjectConstructor p1 = (SkylarkClassObjectConstructor) lookup("p1");
1379 assertThat(p).isEqualTo(p1);
1380 assertThat(p.getKey())
1381 .isEqualTo(new SkylarkClassObjectConstructor.SkylarkKey(FAKE_LABEL, "p"));
1382 assertThat(p1.getKey())
1383 .isEqualTo(new SkylarkClassObjectConstructor.SkylarkKey(FAKE_LABEL, "p"));
1384 }
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001385
Dmitry Lomov0692c7f2016-09-30 16:43:30 +00001386
1387 @Test
1388 public void starTheOnlyAspectArg() throws Exception {
1389 checkEvalError("'*' must be the only string in 'attr_aspects' list",
1390 "def _impl(target, ctx):",
1391 " pass",
1392 "aspect(_impl, attr_aspects=['*', 'foo'])");
1393 }
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +00001394
1395 @Test
1396 public void testMandatoryConfigParameterForExecutableLabels() throws Exception {
1397 scratch.file("third_party/foo/extension.bzl",
1398 "def _main_rule_impl(ctx):",
1399 " pass",
1400 "my_rule = rule(_main_rule_impl,",
1401 " attrs = { ",
1402 " 'exe' : attr.label(executable = True, allow_files = True),",
1403 " },",
1404 ")"
1405 );
1406 scratch.file("third_party/foo/BUILD",
1407 "load('extension', 'my_rule')",
1408 "my_rule(name = 'main', exe = ':tool.sh')"
1409 );
1410
1411 try {
1412 createRuleContext("//third_party/foo:main");
lberki4a45ea82017-06-01 10:05:42 +02001413 fail();
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +00001414 } catch (AssertionError e) {
lberkiaea56b32017-05-30 12:35:33 +02001415 assertThat(e)
1416 .hasMessageThat()
1417 .contains("cfg parameter is mandatory when executable=True is " + "provided.");
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +00001418 }
1419 }
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +00001420
John Catereca28402017-05-17 21:44:12 +02001421 @Test
1422 public void testRuleAddToolchain() throws Exception {
1423 evalAndExport(
1424 "my_toolchain_type = platform_common.toolchain_type()",
1425 "def impl(ctx): return None",
1426 "r1 = rule(impl, toolchains=[my_toolchain_type])");
1427 ToolchainConstructor toolchain = (ToolchainConstructor) lookup("my_toolchain_type");
1428 RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
1429 assertThat(c.getRequiredToolchains()).containsExactly(toolchain.getKey());
1430 }
vladmos2f32e382017-06-19 12:44:21 -04001431
1432 @Test
1433 public void testRuleFunctionReturnsNone() throws Exception {
1434 scratch.file("test/rule.bzl",
1435 "def _impl(ctx):",
1436 " pass",
1437 "foo_rule = rule(",
1438 " implementation = _impl,",
1439 " attrs = {'params': attr.string_list()},",
1440 ")");
1441 scratch.file("test/BUILD",
1442 "load(':rule.bzl', 'foo_rule')",
1443 "r = foo_rule(name='foo')", // Custom rule should return None
1444 "c = cc_library(name='cc')", // Native rule should return None
1445 "",
1446 "foo_rule(",
1447 " name='check',",
1448 " params = [type(r), type(c)]",
1449 ")");
1450 invalidatePackages();
1451 SkylarkRuleContext context = createRuleContext("//test:check");
1452 @SuppressWarnings("unchecked")
1453 MutableList<Object> params = (MutableList<Object>) context.getAttr().getValue("params");
1454 assertThat(params.get(0)).isEqualTo("NoneType");
1455 assertThat(params.get(1)).isEqualTo("NoneType");
1456 }
John Catereca28402017-05-17 21:44:12 +02001457}