blob: 6a7e201827ba24908706efdb59724f1f42699826 [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;
ulfjack35625252017-08-08 19:45:46 +020025import com.google.devtools.build.lib.analysis.skylark.SkylarkAttr;
26import com.google.devtools.build.lib.analysis.skylark.SkylarkAttr.Descriptor;
27import com.google.devtools.build.lib.analysis.skylark.SkylarkFileType;
28import com.google.devtools.build.lib.analysis.skylark.SkylarkRuleClassFunctions.RuleFunction;
29import 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;
34import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition;
35import com.google.devtools.build.lib.packages.BuildType;
36import com.google.devtools.build.lib.packages.ImplicitOutputsFunction;
dslomovde965ac2017-07-31 21:07:51 +020037import com.google.devtools.build.lib.packages.Info;
38import com.google.devtools.build.lib.packages.NativeProvider;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000039import com.google.devtools.build.lib.packages.PredicateWithMessage;
Dmitry Lomovf868b3e2017-01-17 10:25:28 +000040import com.google.devtools.build.lib.packages.RequiredProviders;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000041import com.google.devtools.build.lib.packages.RuleClass;
42import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
Googler74558fc2016-05-06 21:47:42 +000043import com.google.devtools.build.lib.packages.SkylarkAspect;
Dmitry Lomov950310f2017-03-01 17:45:12 +000044import com.google.devtools.build.lib.packages.SkylarkAspectClass;
dslomovde965ac2017-07-31 21:07:51 +020045import com.google.devtools.build.lib.packages.SkylarkProvider;
Dmitry Lomovfd2bdc32016-10-07 08:52:10 +000046import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier;
Dmitry Lomov950310f2017-03-01 17:45:12 +000047import com.google.devtools.build.lib.skyframe.SkylarkImportLookupFunction;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000048import com.google.devtools.build.lib.skylark.util.SkylarkTestCase;
Dmitry Lomov950310f2017-03-01 17:45:12 +000049import com.google.devtools.build.lib.syntax.BuildFileAST;
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +000050import com.google.devtools.build.lib.syntax.ClassObject;
51import com.google.devtools.build.lib.syntax.Environment;
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +000052import com.google.devtools.build.lib.syntax.EvalUtils;
53import com.google.devtools.build.lib.syntax.SkylarkDict;
54import com.google.devtools.build.lib.syntax.SkylarkList.MutableList;
55import com.google.devtools.build.lib.syntax.SkylarkList.Tuple;
56import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000057import com.google.devtools.build.lib.syntax.Type;
Dmitry Lomov8ff5a872017-03-04 00:58:14 +000058import com.google.devtools.build.lib.testutil.MoreAsserts;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000059import com.google.devtools.build.lib.util.FileTypeSet;
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +000060import java.util.Collection;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000061import org.junit.Before;
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +000062import org.junit.Rule;
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +000063import org.junit.Test;
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +000064import org.junit.rules.ExpectedException;
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +000065import org.junit.runner.RunWith;
66import org.junit.runners.JUnit4;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000067
68/**
69 * Tests for SkylarkRuleClassFunctions.
70 */
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +000071@RunWith(JUnit4.class)
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000072public class SkylarkRuleClassFunctionsTest extends SkylarkTestCase {
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +000073 @Rule public ExpectedException thrown = ExpectedException.none();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000074
75 @Before
Florian Weikertb4c59042015-12-01 10:47:18 +000076 public final void createBuildFile() throws Exception {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000077 scratch.file(
78 "foo/BUILD",
79 "genrule(name = 'foo',",
80 " cmd = 'dummy_cmd',",
81 " srcs = ['a.txt', 'b.img'],",
82 " tools = ['t.exe'],",
83 " outs = ['c.txt'])",
84 "genrule(name = 'bar',",
85 " cmd = 'dummy_cmd',",
86 " srcs = [':jl', ':gl'],",
87 " outs = ['d.txt'])",
88 "java_library(name = 'jl',",
89 " srcs = ['a.java'])",
90 "genrule(name = 'gl',",
91 " cmd = 'touch $(OUTS)',",
92 " srcs = ['a.go'],",
93 " outs = [ 'gl.a', 'gl.gcgox', ],",
94 " output_to_bindir = 1,",
95 ")");
96 }
97
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +000098 @Test
Florian Weikerte96b0b82015-09-25 11:35:11 +000099 public void testCannotOverrideBuiltInAttribute() throws Exception {
Dmitry Lomov950310f2017-03-01 17:45:12 +0000100 ev.setFailFast(false);
Dmitry Lomov7b599452015-11-26 10:07:32 +0000101 try {
102 evalAndExport(
103 "def impl(ctx): return", "r = rule(impl, attrs = {'tags': attr.string_list()})");
lberki4a45ea82017-06-01 10:05:42 +0200104 fail("Expected error '"
Dmitry Lomov7b599452015-11-26 10:07:32 +0000105 + "There is already a built-in attribute 'tags' which cannot be overridden"
106 + "' but got no error");
Dmitry Lomov950310f2017-03-01 17:45:12 +0000107 } catch (AssertionError e) {
lberkiaea56b32017-05-30 12:35:33 +0200108 assertThat(e)
109 .hasMessageThat()
110 .contains("There is already a built-in attribute 'tags' which cannot be overridden");
Dmitry Lomov7b599452015-11-26 10:07:32 +0000111 }
Florian Weikerte96b0b82015-09-25 11:35:11 +0000112 }
113
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000114 @Test
Vladimir Moskvada574922016-10-05 16:36:49 +0000115 public void testCannotOverrideBuiltInAttributeName() throws Exception {
Dmitry Lomov950310f2017-03-01 17:45:12 +0000116 ev.setFailFast(false);
Vladimir Moskvada574922016-10-05 16:36:49 +0000117 try {
118 evalAndExport(
119 "def impl(ctx): return", "r = rule(impl, attrs = {'name': attr.string()})");
lberki4a45ea82017-06-01 10:05:42 +0200120 fail("Expected error '"
Vladimir Moskvada574922016-10-05 16:36:49 +0000121 + "There is already a built-in attribute 'name' which cannot be overridden"
122 + "' but got no error");
Dmitry Lomov950310f2017-03-01 17:45:12 +0000123 } catch (AssertionError e) {
lberkiaea56b32017-05-30 12:35:33 +0200124 assertThat(e)
125 .hasMessageThat()
126 .contains("There is already a built-in attribute 'name' which cannot be overridden");
Vladimir Moskvada574922016-10-05 16:36:49 +0000127 }
128 }
129
130 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000131 public void testImplicitArgsAttribute() throws Exception {
Dmitry Lomov950310f2017-03-01 17:45:12 +0000132 ev.setFailFast(false);
Dmitry Lomov7b599452015-11-26 10:07:32 +0000133 evalAndExport(
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000134 "def _impl(ctx):",
135 " pass",
136 "exec_rule = rule(implementation = _impl, executable = True)",
137 "non_exec_rule = rule(implementation = _impl)");
lberkiaea56b32017-05-30 12:35:33 +0200138 assertThat(getRuleClass("exec_rule").hasAttr("args", Type.STRING_LIST)).isTrue();
139 assertThat(getRuleClass("non_exec_rule").hasAttr("args", Type.STRING_LIST)).isFalse();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000140 }
141
142 private RuleClass getRuleClass(String name) throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000143 return ((RuleFunction) lookup(name)).getRuleClass();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000144 }
145
146 private void registerDummyUserDefinedFunction() throws Exception {
147 eval("def impl():\n" + " return 0\n");
148 }
149
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000150 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000151 public void testAttrWithOnlyType() throws Exception {
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000152 Attribute attr = buildAttribute("a1", "attr.string_list()");
lberkiaea56b32017-05-30 12:35:33 +0200153 assertThat(attr.getType()).isEqualTo(Type.STRING_LIST);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000154 }
155
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000156 private Attribute buildAttribute(String name, String... lines) throws Exception {
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000157 String[] strings = lines.clone();
158 strings[strings.length - 1] = String.format("%s = %s", name, strings[strings.length - 1]);
159 evalAndExport(strings);
160 Descriptor lookup = (Descriptor) ev.lookup(name);
161 return lookup != null ? lookup.build(name) : null;
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000162 }
163
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000164 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000165 public void testOutputListAttr() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000166 Attribute attr = buildAttribute("a1", "attr.output_list()");
lberkiaea56b32017-05-30 12:35:33 +0200167 assertThat(attr.getType()).isEqualTo(BuildType.OUTPUT_LIST);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000168 }
169
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000170 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000171 public void testIntListAttr() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000172 Attribute attr = buildAttribute("a1", "attr.int_list()");
lberkiaea56b32017-05-30 12:35:33 +0200173 assertThat(attr.getType()).isEqualTo(Type.INTEGER_LIST);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000174 }
175
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000176 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000177 public void testOutputAttr() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000178 Attribute attr = buildAttribute("a1", "attr.output()");
lberkiaea56b32017-05-30 12:35:33 +0200179 assertThat(attr.getType()).isEqualTo(BuildType.OUTPUT);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000180 }
181
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000182 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000183 public void testStringDictAttr() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000184 Attribute attr = buildAttribute("a1", "attr.string_dict(default = {'a': 'b'})");
lberkiaea56b32017-05-30 12:35:33 +0200185 assertThat(attr.getType()).isEqualTo(Type.STRING_DICT);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000186 }
187
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000188 @Test
Francois-Rene Rideau028e3192015-10-29 14:26:59 +0000189 public void testStringListDictAttr() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000190 Attribute attr = buildAttribute("a1", "attr.string_list_dict(default = {'a': ['b', 'c']})");
lberkiaea56b32017-05-30 12:35:33 +0200191 assertThat(attr.getType()).isEqualTo(Type.STRING_LIST_DICT);
Francois-Rene Rideau028e3192015-10-29 14:26:59 +0000192 }
193
194 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000195 public void testAttrAllowedFileTypesAnyFile() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000196 Attribute attr = buildAttribute("a1", "attr.label_list(allow_files = True)");
lberkiaea56b32017-05-30 12:35:33 +0200197 assertThat(attr.getAllowedFileTypesPredicate()).isEqualTo(FileTypeSet.ANY_FILE);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000198 }
199
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000200 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000201 public void testAttrAllowedFileTypesWrongType() throws Exception {
202 checkErrorContains(
Laurent Le Brun2445df12016-05-11 14:36:40 +0000203 "allow_files should be a boolean or a string list",
204 "attr.label_list(allow_files = 18)");
205 }
206
207 @Test
Laurent Le Brun50681c12016-07-05 10:08:54 +0000208 public void testAttrAllowedSingleFileTypesWrongType() throws Exception {
209 checkErrorContains(
210 "allow_single_file should be a boolean or a string list",
211 "attr.label(allow_single_file = 18)");
212 }
213
214 @Test
Laurent Le Brun2445df12016-05-11 14:36:40 +0000215 public void testAttrWithList() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000216 Attribute attr = buildAttribute("a1", "attr.label_list(allow_files = ['.xml'])");
lberkiaea56b32017-05-30 12:35:33 +0200217 assertThat(attr.getAllowedFileTypesPredicate().apply("a.xml")).isTrue();
218 assertThat(attr.getAllowedFileTypesPredicate().apply("a.txt")).isFalse();
219 assertThat(attr.isSingleArtifact()).isFalse();
Laurent Le Brun50681c12016-07-05 10:08:54 +0000220 }
221
222 @Test
223 public void testAttrSingleFileWithList() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000224 Attribute attr = buildAttribute("a1", "attr.label(allow_single_file = ['.xml'])");
lberkiaea56b32017-05-30 12:35:33 +0200225 assertThat(attr.getAllowedFileTypesPredicate().apply("a.xml")).isTrue();
226 assertThat(attr.getAllowedFileTypesPredicate().apply("a.txt")).isFalse();
227 assertThat(attr.isSingleArtifact()).isTrue();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000228 }
229
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000230 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000231 public void testAttrWithSkylarkFileType() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000232 Attribute attr = buildAttribute("a1", "attr.label_list(allow_files = FileType(['.xml']))");
lberkiaea56b32017-05-30 12:35:33 +0200233 assertThat(attr.getAllowedFileTypesPredicate().apply("a.xml")).isTrue();
234 assertThat(attr.getAllowedFileTypesPredicate().apply("a.txt")).isFalse();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000235 }
236
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000237 private static SkylarkProviderIdentifier legacy(String legacyId) {
238 return SkylarkProviderIdentifier.forLegacy(legacyId);
239 }
240
241 private static SkylarkProviderIdentifier declared(String exportedName) {
242 return SkylarkProviderIdentifier.forKey(
dslomovde965ac2017-07-31 21:07:51 +0200243 new SkylarkProvider.SkylarkKey(FAKE_LABEL, exportedName));
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000244 }
245
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000246 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000247 public void testAttrWithProviders() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000248 Attribute attr =
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000249 buildAttribute("a1",
250 "b = provider()",
251 "attr.label_list(allow_files = True, providers = ['a', b])");
dslomovc13bb392017-08-02 23:29:54 +0200252 assertThat(attr.getRequiredProviders().isSatisfiedBy(set(legacy("a"), declared("b")))).isTrue();
253 assertThat(attr.getRequiredProviders().isSatisfiedBy(set(legacy("a")))).isFalse();
254 }
255
256 @Test
257 public void testAttrWithProvidersOneEmpty() throws Exception {
258 Attribute attr =
259 buildAttribute(
260 "a1",
261 "b = provider()",
262 "attr.label_list(allow_files = True, providers = [['a', b],[]])");
263 assertThat(attr.getRequiredProviders().acceptsAny()).isTrue();
dslomovc32e1b12017-07-31 19:23:52 +0200264 }
265
dslomovc32e1b12017-07-31 19:23:52 +0200266 @Test
Yun Peng83fbb91a2016-02-23 18:37:44 +0000267 public void testAttrWithProvidersList() throws Exception {
268 Attribute attr =
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000269 buildAttribute("a1",
270 "b = provider()",
271 "attr.label_list(allow_files = True, providers = [['a', b], ['c']])");
dslomovc13bb392017-08-02 23:29:54 +0200272 assertThat(attr.getRequiredProviders().isSatisfiedBy(set(legacy("a"), declared("b")))).isTrue();
273 assertThat(attr.getRequiredProviders().isSatisfiedBy(set(legacy("c")))).isTrue();
274 assertThat(attr.getRequiredProviders().isSatisfiedBy(set(legacy("a")))).isFalse();
275 }
276
277 private static AdvertisedProviderSet set(SkylarkProviderIdentifier... ids) {
278 AdvertisedProviderSet.Builder builder = AdvertisedProviderSet.builder();
279 for (SkylarkProviderIdentifier id : ids) {
280 builder.addSkylark(id);
281 }
282 return builder.build();
Yun Peng83fbb91a2016-02-23 18:37:44 +0000283 }
284
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000285 private void checkAttributeError(String expectedMessage, String... lines) throws Exception {
286 ev.setFailFast(false);
287 buildAttribute("fakeAttribute", lines);
288 MoreAsserts.assertContainsEvent(ev.getEventCollector(), expectedMessage);
289 }
290
Yun Peng83fbb91a2016-02-23 18:37:44 +0000291 @Test
292 public void testAttrWithWrongProvidersList() throws Exception {
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000293 checkAttributeError(
294 "element in 'providers' is of unexpected type. Either all elements should be providers,"
295 + " or all elements should be lists of providers,"
296 + " but got list with an element of type int.",
Yun Pengda9410c2016-03-18 21:14:51 +0000297 "attr.label_list(allow_files = True, providers = [['a', 1], ['c']])");
Yun Peng83fbb91a2016-02-23 18:37:44 +0000298
Dmitry Lomov8ff5a872017-03-04 00:58:14 +0000299 checkAttributeError(
300 "element in 'providers' is of unexpected type. Either all elements should be providers,"
301 + " or all elements should be lists of providers,"
302 + " but got an element of type string.",
303 "b = provider()",
304 "attr.label_list(allow_files = True, providers = [['a', b], 'c'])");
305
306 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 an element of type string.",
310 "c = provider()",
311 "attr.label_list(allow_files = True, providers = [['a', b], c])");
312
Yun Peng83fbb91a2016-02-23 18:37:44 +0000313 }
314
315 @Test
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000316 public void testLabelListWithAspects() throws Exception {
Dmitry Lomov950310f2017-03-01 17:45:12 +0000317 evalAndExport(
Yun Pengda9410c2016-03-18 21:14:51 +0000318 "def _impl(target, ctx):",
319 " pass",
320 "my_aspect = aspect(implementation = _impl)",
Dmitry Lomov950310f2017-03-01 17:45:12 +0000321 "a = attr.label_list(aspects = [my_aspect])");
322 SkylarkAttr.Descriptor attr = (SkylarkAttr.Descriptor) ev.lookup("a");
323 SkylarkAspect aspect = (SkylarkAspect) ev.lookup("my_aspect");
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000324 assertThat(aspect).isNotNull();
Dmitry Lomov950310f2017-03-01 17:45:12 +0000325 assertThat(attr.build("xxx").getAspectClasses()).containsExactly(aspect.getAspectClass());
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000326 }
327
328 @Test
Dmitry Lomov950310f2017-03-01 17:45:12 +0000329 public void testLabelWithAspects() throws Exception {
330 evalAndExport(
331 "def _impl(target, ctx):",
332 " pass",
333 "my_aspect = aspect(implementation = _impl)",
334 "a = attr.label(aspects = [my_aspect])");
335 SkylarkAttr.Descriptor attr = (SkylarkAttr.Descriptor) ev.lookup("a");
336 SkylarkAspect aspect = (SkylarkAspect) ev.lookup("my_aspect");
337 assertThat(aspect).isNotNull();
338 assertThat(attr.build("xxx").getAspectClasses()).containsExactly(aspect.getAspectClass());
339 }
340
341
342 @Test
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000343 public void testLabelListWithAspectsError() throws Exception {
344 checkErrorContains(
Laurent Le Brunc31f3512016-12-29 21:41:33 +0000345 "expected type 'Aspect' for 'aspects' element but got type 'int' instead",
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000346 "def _impl(target, ctx):",
347 " pass",
348 "my_aspect = aspect(implementation = _impl)",
Laurent Le Brunc31f3512016-12-29 21:41:33 +0000349 "attr.label_list(aspects = [my_aspect, 123])");
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000350 }
351
352 @Test
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000353 public void testAspectExtraDeps() throws Exception {
354 evalAndExport(
355 "def _impl(target, ctx):",
356 " pass",
357 "my_aspect = aspect(_impl,",
Dmitry Lomovace678e2015-12-16 15:10:20 +0000358 " attrs = { '_extra_deps' : attr.label(default = Label('//foo/bar:baz')) }",
359 ")");
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000360 SkylarkAspect aspect = (SkylarkAspect) ev.lookup("my_aspect");
Googler74558fc2016-05-06 21:47:42 +0000361 Attribute attribute = Iterables.getOnlyElement(aspect.getAttributes());
362 assertThat(attribute.getName()).isEqualTo("$extra_deps");
363 assertThat(attribute.getDefaultValue(null))
Brian Silvermand7d6d622016-03-17 09:53:39 +0000364 .isEqualTo(Label.parseAbsolute("//foo/bar:baz", false));
Dmitry Lomovace678e2015-12-16 15:10:20 +0000365 }
366
367 @Test
Googler74558fc2016-05-06 21:47:42 +0000368 public void testAspectParameter() throws Exception {
369 evalAndExport(
Dmitry Lomovace678e2015-12-16 15:10:20 +0000370 "def _impl(target, ctx):",
371 " pass",
372 "my_aspect = aspect(_impl,",
Googler74558fc2016-05-06 21:47:42 +0000373 " attrs = { 'param' : attr.string(values=['a', 'b']) }",
Dmitry Lomovace678e2015-12-16 15:10:20 +0000374 ")");
Googler74558fc2016-05-06 21:47:42 +0000375 SkylarkAspect aspect = (SkylarkAspect) ev.lookup("my_aspect");
376 Attribute attribute = Iterables.getOnlyElement(aspect.getAttributes());
377 assertThat(attribute.getName()).isEqualTo("param");
378 }
Michajlo Matijkiw6d471412016-08-09 20:35:45 +0000379
Googler74558fc2016-05-06 21:47:42 +0000380 @Test
381 public void testAspectParameterRequiresValues() throws Exception {
382 checkErrorContains(
383 "Aspect parameter attribute 'param' must have type 'string' and use the 'values' "
384 + "restriction.",
385 "def _impl(target, ctx):",
386 " pass",
387 "my_aspect = aspect(_impl,",
388 " attrs = { 'param' : attr.string(default = 'c') }",
389 ")");
390 }
391
392 @Test
393 public void testAspectParameterBadType() throws Exception {
394 checkErrorContains(
395 "Aspect parameter attribute 'param' must have type 'string' and use the 'values' "
396 + "restriction.",
397 "def _impl(target, ctx):",
398 " pass",
399 "my_aspect = aspect(_impl,",
400 " attrs = { 'param' : attr.label(default = Label('//foo/bar:baz')) }",
401 ")");
402 }
403
404 @Test
405 public void testAspectParameterAndExtraDeps() throws Exception {
406 evalAndExport(
407 "def _impl(target, ctx):",
408 " pass",
409 "my_aspect = aspect(_impl,",
410 " attrs = { 'param' : attr.string(values=['a', 'b']),",
411 " '_extra' : attr.label(default = Label('//foo/bar:baz')) }",
412 ")");
413 SkylarkAspect aspect = (SkylarkAspect) ev.lookup("my_aspect");
414 assertThat(aspect.getAttributes()).hasSize(2);
415 assertThat(aspect.getParamAttributes()).containsExactly("param");
Dmitry Lomovace678e2015-12-16 15:10:20 +0000416 }
417
418 @Test
419 public void testAspectNoDefaultValueAttribute() throws Exception {
420 checkErrorContains(
421 "Aspect attribute '_extra_deps' has no default value",
422 "def _impl(target, ctx):",
423 " pass",
424 "my_aspect = aspect(_impl,",
425 " attrs = { '_extra_deps' : attr.label() }",
426 ")");
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000427 }
428
429 @Test
John Cater9a8d16e2017-07-05 16:12:07 -0400430 public void testAspectAddToolchain() throws Exception {
431 scratch.file("test/BUILD", "toolchain_type(name = 'my_toolchain_type')");
432 evalAndExport(
433 "def _impl(ctx): pass", "a1 = aspect(_impl, toolchains=['//test:my_toolchain_type'])");
434 SkylarkAspect a = (SkylarkAspect) lookup("a1");
435 assertThat(a.getRequiredToolchains()).containsExactly(makeLabel("//test:my_toolchain_type"));
436 }
437
438 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000439 public void testNonLabelAttrWithProviders() throws Exception {
440 checkErrorContains(
441 "unexpected keyword 'providers' in call to string", "attr.string(providers = ['a'])");
442 }
443
444 private static final RuleClass.ConfiguredTargetFactory<Object, Object>
445 DUMMY_CONFIGURED_TARGET_FACTORY =
dslomovc13bb392017-08-02 23:29:54 +0200446 ruleContext -> {
447 throw new IllegalStateException();
448 };
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000449
450 private RuleClass ruleClass(String name) {
451 return new RuleClass.Builder(name, RuleClassType.NORMAL, false)
452 .factory(DUMMY_CONFIGURED_TARGET_FACTORY)
453 .add(Attribute.attr("tags", Type.STRING_LIST))
454 .build();
455 }
vladmos9c787fa2017-07-04 11:45:22 -0400456
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000457 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000458 public void testAttrAllowedRuleClassesSpecificRuleClasses() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000459 Attribute attr = buildAttribute("a",
460 "attr.label_list(allow_rules = ['java_binary'], allow_files = True)");
lberkiaea56b32017-05-30 12:35:33 +0200461 assertThat(attr.getAllowedRuleClassesPredicate().apply(ruleClass("java_binary"))).isTrue();
462 assertThat(attr.getAllowedRuleClassesPredicate().apply(ruleClass("genrule"))).isFalse();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000463 }
vladmos9c787fa2017-07-04 11:45:22 -0400464
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000465 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000466 public void testAttrDefaultValue() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000467 Attribute attr = buildAttribute("a1", "attr.string(default = 'some value')");
lberkiaea56b32017-05-30 12:35:33 +0200468 assertThat(attr.getDefaultValueForTesting()).isEqualTo("some value");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000469 }
470
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000471 @Test
vladmos9c787fa2017-07-04 11:45:22 -0400472 public void testLabelAttrDefaultValueAsString() throws Exception {
473 Attribute sligleAttr = buildAttribute("a1", "attr.label(default = '//foo:bar')");
474 assertThat(sligleAttr.getDefaultValueForTesting())
475 .isEqualTo(Label.parseAbsolute("//foo:bar", false));
476
477 Attribute listAttr =
478 buildAttribute("a2", "attr.label_list(default = ['//foo:bar', '//bar:foo'])");
479 assertThat(listAttr.getDefaultValueForTesting())
480 .isEqualTo(
481 ImmutableList.of(
482 Label.parseAbsolute("//foo:bar", false), Label.parseAbsolute("//bar:foo", false)));
483
484 Attribute dictAttr =
485 buildAttribute("a3", "attr.label_keyed_string_dict(default = {'//foo:bar': 'my value'})");
486 assertThat(dictAttr.getDefaultValueForTesting())
487 .isEqualTo(ImmutableMap.of(Label.parseAbsolute("//foo:bar", false), "my value"));
488 }
489
490 @Test
491 public void testLabelAttrDefaultValueAsStringBadValue() throws Exception {
492 checkErrorContains(
493 "invalid label '/foo:bar' in parameter 'default' of attribute 'label': "
494 + "invalid label: /foo:bar",
495 "attr.label(default = '/foo:bar')");
496
497 checkErrorContains(
498 "invalid label '/bar:foo' in element 1 of parameter 'default' of attribute "
499 + "'label_list': invalid label: /bar:foo",
500 "attr.label_list(default = ['//foo:bar', '/bar:foo'])");
501
502 checkErrorContains(
503 "invalid label '/bar:foo' in dict key element: invalid label: /bar:foo",
504 "attr.label_keyed_string_dict(default = {'//foo:bar': 'a', '/bar:foo': 'b'})");
505 }
506
507 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000508 public void testAttrDefaultValueBadType() throws Exception {
509 checkErrorContains(
laurentlb9e540882017-07-07 06:58:45 -0400510 "argument 'default' has type 'int', but should be 'string'\n"
allevato45b79e52017-07-07 21:40:50 +0200511 + "in call to builtin function attr.string(*, default, doc, mandatory, values)",
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000512 "attr.string(default = 1)");
513 }
514
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000515 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000516 public void testAttrMandatory() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000517 Attribute attr = buildAttribute("a1", "attr.string(mandatory=True)");
lberkiaea56b32017-05-30 12:35:33 +0200518 assertThat(attr.isMandatory()).isTrue();
519 assertThat(attr.isNonEmpty()).isFalse();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000520 }
521
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000522 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000523 public void testAttrNonEmpty() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000524 Attribute attr = buildAttribute("a1", "attr.string_list(non_empty=True)");
lberkiaea56b32017-05-30 12:35:33 +0200525 assertThat(attr.isNonEmpty()).isTrue();
526 assertThat(attr.isMandatory()).isFalse();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000527 }
528
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000529 @Test
Laurent Le Brunbc16f722016-07-06 13:40:24 +0000530 public void testAttrAllowEmpty() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000531 Attribute attr = buildAttribute("a1", "attr.string_list(allow_empty=False)");
lberkiaea56b32017-05-30 12:35:33 +0200532 assertThat(attr.isNonEmpty()).isTrue();
533 assertThat(attr.isMandatory()).isFalse();
Laurent Le Brunbc16f722016-07-06 13:40:24 +0000534 }
535
536 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000537 public void testAttrBadKeywordArguments() throws Exception {
538 checkErrorContains(
539 "unexpected keyword 'bad_keyword' in call to string", "attr.string(bad_keyword = '')");
540 }
541
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000542 @Test
Laurent Le Brunc4ddf6f2016-07-04 13:38:38 +0000543 public void testAttrCfg() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000544 Attribute attr = buildAttribute("a1", "attr.label(cfg = 'host', allow_files = True)");
lberkiaea56b32017-05-30 12:35:33 +0200545 assertThat(attr.getConfigurationTransition()).isEqualTo(ConfigurationTransition.HOST);
Laurent Le Brunc4ddf6f2016-07-04 13:38:38 +0000546 }
547
548 @Test
549 public void testAttrCfgData() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000550 Attribute attr = buildAttribute("a1", "attr.label(cfg = 'data', allow_files = True)");
lberkiaea56b32017-05-30 12:35:33 +0200551 assertThat(attr.getConfigurationTransition()).isEqualTo(ConfigurationTransition.DATA);
Laurent Le Brunc4ddf6f2016-07-04 13:38:38 +0000552 }
553
554 @Test
Vladimir Moskva5a510772016-11-23 19:03:38 +0000555 public void testAttrCfgTarget() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000556 Attribute attr = buildAttribute("a1", "attr.label(cfg = 'target', allow_files = True)");
lberkiaea56b32017-05-30 12:35:33 +0200557 assertThat(attr.getConfigurationTransition()).isEqualTo(ConfigurationTransition.NONE);
Vladimir Moskva5a510772016-11-23 19:03:38 +0000558 }
559
560 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000561 public void testAttrValues() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000562 Attribute attr = buildAttribute("a1", "attr.string(values = ['ab', 'cd'])");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000563 PredicateWithMessage<Object> predicate = attr.getAllowedValues();
564 assertThat(predicate.apply("ab")).isTrue();
565 assertThat(predicate.apply("xy")).isFalse();
566 }
567
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000568 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000569 public void testAttrIntValues() throws Exception {
Dmitry Lomov460db0f2016-11-24 10:54:19 +0000570 Attribute attr = buildAttribute("a1", "attr.int(values = [1, 2])");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000571 PredicateWithMessage<Object> predicate = attr.getAllowedValues();
572 assertThat(predicate.apply(2)).isTrue();
573 assertThat(predicate.apply(3)).isFalse();
574 }
575
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000576 @Test
allevato45b79e52017-07-07 21:40:50 +0200577 public void testAttrDoc() throws Exception {
578 // We don't actually store the doc in the attr definition; right now it's just meant to be
579 // extracted by documentation generating tools. So we don't have anything to assert and we just
580 // verify that no exceptions were thrown from building them.
581 buildAttribute("a1", "attr.bool(doc='foo')");
582 buildAttribute("a2", "attr.int(doc='foo')");
583 buildAttribute("a3", "attr.int_list(doc='foo')");
584 buildAttribute("a4", "attr.label(doc='foo')");
585 buildAttribute("a5", "attr.label_keyed_string_dict(doc='foo')");
586 buildAttribute("a6", "attr.label_list(doc='foo')");
587 buildAttribute("a7", "attr.license(doc='foo')");
588 buildAttribute("a8", "attr.output(doc='foo')");
589 buildAttribute("a9", "attr.output_list(doc='foo')");
590 buildAttribute("a10", "attr.string(doc='foo')");
591 buildAttribute("a11", "attr.string_dict(doc='foo')");
592 buildAttribute("a12", "attr.string_list(doc='foo')");
593 buildAttribute("a13", "attr.string_list_dict(doc='foo')");
594 }
595
596 @Test
597 public void testAttrDocValueBadType() throws Exception {
598 checkErrorContains(
599 "argument 'doc' has type 'int', but should be 'string'\n"
600 + "in call to builtin function attr.string(*, default, doc, mandatory, values)",
601 "attr.string(doc = 1)");
602 }
603
604 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000605 public void testRuleImplementation() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000606 evalAndExport("def impl(ctx): return None", "rule1 = rule(impl)");
607 RuleClass c = ((RuleFunction) lookup("rule1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200608 assertThat(c.getConfiguredTargetFunction().getName()).isEqualTo("impl");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000609 }
610
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000611 @Test
allevato45b79e52017-07-07 21:40:50 +0200612 public void testRuleDoc() throws Exception {
613 evalAndExport("def impl(ctx): return None", "rule1 = rule(impl, doc='foo')");
614 }
615
616 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000617 public void testLateBoundAttrWorksWithOnlyLabel() throws Exception {
618 checkEvalError(
laurentlb9e540882017-07-07 06:58:45 -0400619 "argument 'default' has type 'function', but should be 'string'\n"
allevato45b79e52017-07-07 21:40:50 +0200620 + "in call to builtin function attr.string(*, default, doc, mandatory, values)",
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000621 "def attr_value(cfg): return 'a'",
622 "attr.string(default=attr_value)");
623 }
624
Dmitry Lomov7b599452015-11-26 10:07:32 +0000625 private static final Label FAKE_LABEL = Label.parseAbsoluteUnchecked("//fake/label.bzl");
626
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000627 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000628 public void testRuleAddAttribute() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000629 evalAndExport("def impl(ctx): return None", "r1 = rule(impl, attrs={'a1': attr.string()})");
630 RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200631 assertThat(c.hasAttr("a1", Type.STRING)).isTrue();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000632 }
633
Dmitry Lomov7b599452015-11-26 10:07:32 +0000634 protected void evalAndExport(String... lines) throws Exception {
Dmitry Lomov950310f2017-03-01 17:45:12 +0000635 BuildFileAST buildFileAST = BuildFileAST.parseAndValidateSkylarkString(
636 ev.getEnvironment(), lines);
637 SkylarkImportLookupFunction.execAndExport(
638 buildFileAST, FAKE_LABEL, ev.getEventHandler(), ev.getEnvironment());
Dmitry Lomov7b599452015-11-26 10:07:32 +0000639 }
640
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000641 @Test
Jon Brandveinded4fbb2017-01-18 22:21:04 +0000642 public void testExportAliasedName() throws Exception {
643 // When there are multiple names aliasing the same SkylarkExportable, the first one to be
644 // declared should be used. Make sure we're not using lexicographical order, hash order,
645 // non-deterministic order, or anything else.
646 evalAndExport(
647 "def _impl(ctx): pass",
648 "d = rule(implementation = _impl)",
649 "a = d",
650 // Having more names improves the chance that non-determinism will be caught.
651 "b = d",
652 "c = d",
653 "e = d",
654 "f = d",
655 "foo = d",
656 "bar = d",
657 "baz = d",
658 "x = d",
659 "y = d",
660 "z = d");
661 String dName = ((RuleFunction) lookup("d")).getRuleClass().getName();
662 String fooName = ((RuleFunction) lookup("foo")).getRuleClass().getName();
663 assertThat(dName).isEqualTo("d");
664 assertThat(fooName).isEqualTo("d");
665 }
666
667 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000668 public void testOutputToGenfiles() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000669 evalAndExport("def impl(ctx): pass", "r1 = rule(impl, output_to_genfiles=True)");
670 RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200671 assertThat(c.hasBinaryOutput()).isFalse();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000672 }
673
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000674 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000675 public void testRuleAddMultipleAttributes() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000676 evalAndExport(
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000677 "def impl(ctx): return None",
678 "r1 = rule(impl,",
679 " attrs = {",
680 " 'a1': attr.label_list(allow_files=True),",
681 " 'a2': attr.int()",
682 "})");
Dmitry Lomov7b599452015-11-26 10:07:32 +0000683 RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200684 assertThat(c.hasAttr("a1", BuildType.LABEL_LIST)).isTrue();
685 assertThat(c.hasAttr("a2", Type.INTEGER)).isTrue();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000686 }
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000687 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000688 public void testRuleAttributeFlag() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000689 evalAndExport(
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000690 "def impl(ctx): return None",
691 "r1 = rule(impl, attrs = {'a1': attr.string(mandatory=True)})");
Dmitry Lomov7b599452015-11-26 10:07:32 +0000692 RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200693 assertThat(c.getAttributeByName("a1").isMandatory()).isTrue();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000694 }
695
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000696 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000697 public void testRuleOutputs() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000698 evalAndExport(
699 "def impl(ctx): return None",
700 "r1 = rule(impl, outputs = {'a': 'a.txt'})");
701 RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
Michajlo Matijkiw6d471412016-08-09 20:35:45 +0000702 ImplicitOutputsFunction function = c.getDefaultImplicitOutputsFunction();
lberkiaea56b32017-05-30 12:35:33 +0200703 assertThat(function.getImplicitOutputs(null)).containsExactly("a.txt");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000704 }
705
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000706 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000707 public void testRuleUnknownKeyword() throws Exception {
708 registerDummyUserDefinedFunction();
709 checkErrorContains(
710 "unexpected keyword 'bad_keyword' in call to " + "rule(implementation: function, ",
711 "rule(impl, bad_keyword = 'some text')");
712 }
713
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000714 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000715 public void testRuleImplementationMissing() throws Exception {
716 checkErrorContains(
717 "missing mandatory positional argument 'implementation' while calling "
718 + "rule(implementation",
719 "rule(attrs = {})");
720 }
721
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000722 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000723 public void testRuleBadTypeForAdd() throws Exception {
724 registerDummyUserDefinedFunction();
725 checkErrorContains(
726 "expected dict or NoneType for 'attrs' while calling rule but got string instead: "
727 + "some text",
728 "rule(impl, attrs = 'some text')");
729 }
730
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000731 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000732 public void testRuleBadTypeInAdd() throws Exception {
733 registerDummyUserDefinedFunction();
734 checkErrorContains(
Laurent Le Brunc31f3512016-12-29 21:41:33 +0000735 "expected <String, Descriptor> type for 'attrs' but got <string, string> instead",
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000736 "rule(impl, attrs = {'a1': 'some text'})");
737 }
738
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000739 @Test
allevato45b79e52017-07-07 21:40:50 +0200740 public void testRuleBadTypeForDoc() throws Exception {
741 registerDummyUserDefinedFunction();
742 checkErrorContains(
743 "argument 'doc' has type 'int', but should be 'string'",
744 "rule(impl, doc = 1)");
745 }
746
747 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000748 public void testLabel() throws Exception {
749 Object result = evalRuleClassCode("Label('//foo/foo:foo')");
Dmitry Lomov7b599452015-11-26 10:07:32 +0000750 assertThat(result).isInstanceOf(Label.class);
lberkiaea56b32017-05-30 12:35:33 +0200751 assertThat(result.toString()).isEqualTo("//foo/foo:foo");
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 testLabelSameInstance() throws Exception {
756 Object l1 = evalRuleClassCode("Label('//foo/foo:foo')");
757 // Implicitly creates a new pkgContext and environment, yet labels should be the same.
758 Object l2 = evalRuleClassCode("Label('//foo/foo:foo')");
lberkiaea56b32017-05-30 12:35:33 +0200759 assertThat(l1).isSameAs(l2);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000760 }
761
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000762 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000763 public void testLabelNameAndPackage() throws Exception {
764 Object result = evalRuleClassCode("Label('//foo/bar:baz').name");
lberkiaea56b32017-05-30 12:35:33 +0200765 assertThat(result).isEqualTo("baz");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000766 // NB: implicitly creates a new pkgContext and environments, yet labels should be the same.
767 result = evalRuleClassCode("Label('//foo/bar:baz').package");
lberkiaea56b32017-05-30 12:35:33 +0200768 assertThat(result).isEqualTo("foo/bar");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000769 }
770
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000771 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000772 public void testRuleLabelDefaultValue() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000773 evalAndExport(
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000774 "def impl(ctx): return None\n"
775 + "r1 = rule(impl, attrs = {'a1': "
776 + "attr.label(default = Label('//foo:foo'), allow_files=True)})");
Dmitry Lomov7b599452015-11-26 10:07:32 +0000777 RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000778 Attribute a = c.getAttributeByName("a1");
Dmitry Lomov7b599452015-11-26 10:07:32 +0000779 assertThat(a.getDefaultValueForTesting()).isInstanceOf(Label.class);
lberkiaea56b32017-05-30 12:35:33 +0200780 assertThat(a.getDefaultValueForTesting().toString()).isEqualTo("//foo:foo");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000781 }
782
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000783 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000784 public void testIntDefaultValue() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000785 evalAndExport(
786 "def impl(ctx): return None",
787 "r1 = rule(impl, attrs = {'a1': attr.int(default = 40+2)})");
788 RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000789 Attribute a = c.getAttributeByName("a1");
lberkiaea56b32017-05-30 12:35:33 +0200790 assertThat(a.getDefaultValueForTesting()).isEqualTo(42);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000791 }
792
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000793 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000794 public void testFileType() throws Exception {
795 Object result = evalRuleClassCode("FileType(['.css'])");
796 SkylarkFileType fts = (SkylarkFileType) result;
lberkiaea56b32017-05-30 12:35:33 +0200797 assertThat(fts.getExtensions()).isEqualTo(ImmutableList.of(".css"));
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000798 }
799
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000800 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000801 public void testRuleInheritsBaseRuleAttributes() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000802 evalAndExport("def impl(ctx): return None", "r1 = rule(impl)");
803 RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
lberkiaea56b32017-05-30 12:35:33 +0200804 assertThat(c.hasAttr("tags", Type.STRING_LIST)).isTrue();
805 assertThat(c.hasAttr("visibility", BuildType.NODEP_LABEL_LIST)).isTrue();
806 assertThat(c.hasAttr("deprecation", Type.STRING)).isTrue();
807 assertThat(c.hasAttr(":action_listener", BuildType.LABEL_LIST))
808 .isTrue(); // required for extra actions
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000809 }
810
811 private void checkTextMessage(String from, String... lines) throws Exception {
812 Object result = evalRuleClassCode(from);
lberkiaea56b32017-05-30 12:35:33 +0200813 assertThat(result).isEqualTo(Joiner.on("\n").join(lines) + "\n");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000814 }
815
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000816 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000817 public void testSimpleTextMessagesBooleanFields() throws Exception {
818 checkTextMessage("struct(name=True).to_proto()", "name: true");
819 checkTextMessage("struct(name=False).to_proto()", "name: false");
820 }
821
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000822 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000823 public void testSimpleTextMessages() throws Exception {
824 checkTextMessage("struct(name='value').to_proto()", "name: \"value\"");
825 checkTextMessage("struct(name=['a', 'b']).to_proto()", "name: \"a\"", "name: \"b\"");
826 checkTextMessage("struct(name=123).to_proto()", "name: 123");
827 checkTextMessage("struct(name=[1, 2, 3]).to_proto()", "name: 1", "name: 2", "name: 3");
828 checkTextMessage("struct(a=struct(b='b')).to_proto()", "a {", " b: \"b\"", "}");
829 checkTextMessage(
830 "struct(a=[struct(b='x'), struct(b='y')]).to_proto()",
831 "a {",
832 " b: \"x\"",
833 "}",
834 "a {",
835 " b: \"y\"",
836 "}");
837 checkTextMessage(
838 "struct(a=struct(b=struct(c='c'))).to_proto()", "a {", " b {", " c: \"c\"", " }", "}");
839 }
840
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000841 @Test
Vladimir Moskva76e31d12016-12-05 16:28:37 +0000842 public void testProtoFieldsOrder() throws Exception {
843 checkTextMessage("struct(d=4, b=2, c=3, a=1).to_proto()", "a: 1", "b: 2", "c: 3", "d: 4");
844 }
845
846 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000847 public void testTextMessageEscapes() throws Exception {
848 checkTextMessage("struct(name='a\"b').to_proto()", "name: \"a\\\"b\"");
849 checkTextMessage("struct(name='a\\'b').to_proto()", "name: \"a'b\"");
850 checkTextMessage("struct(name='a\\nb').to_proto()", "name: \"a\\nb\"");
Googler4489aaf2016-06-17 15:17:37 +0000851
852 // struct(name="a\\\"b") -> name: "a\\\"b"
853 checkTextMessage("struct(name='a\\\\\\\"b').to_proto()", "name: \"a\\\\\\\"b\"");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000854 }
855
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000856 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000857 public void testTextMessageInvalidElementInListStructure() throws Exception {
858 checkErrorContains(
859 "Invalid text format, expected a struct, a string, a bool, or "
860 + "an int but got a list for list element in struct field 'a'",
861 "struct(a=[['b']]).to_proto()");
862 }
863
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000864 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000865 public void testTextMessageInvalidStructure() throws Exception {
866 checkErrorContains(
867 "Invalid text format, expected a struct, a string, a bool, or an int "
Vladimir Moskvacd12f772017-01-10 12:47:06 +0000868 + "but got a function for struct field 'a'",
869 "struct(a=rule).to_proto()");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000870 }
871
Erik Abair927f4592016-02-29 18:57:22 +0000872 private void checkJson(String from, String expected) throws Exception {
873 Object result = evalRuleClassCode(from);
lberkiaea56b32017-05-30 12:35:33 +0200874 assertThat(result).isEqualTo(expected);
Erik Abair927f4592016-02-29 18:57:22 +0000875 }
876
877 @Test
878 public void testJsonBooleanFields() throws Exception {
879 checkJson("struct(name=True).to_json()", "{\"name\":true}");
880 checkJson("struct(name=False).to_json()", "{\"name\":false}");
881 }
882
883 @Test
884 public void testJsonEncoding() throws Exception {
885 checkJson("struct(name='value').to_json()", "{\"name\":\"value\"}");
886 checkJson("struct(name=['a', 'b']).to_json()", "{\"name\":[\"a\",\"b\"]}");
887 checkJson("struct(name=123).to_json()", "{\"name\":123}");
888 checkJson("struct(name=[1, 2, 3]).to_json()", "{\"name\":[1,2,3]}");
889 checkJson("struct(a=struct(b='b')).to_json()", "{\"a\":{\"b\":\"b\"}}");
890 checkJson("struct(a=[struct(b='x'), struct(b='y')]).to_json()",
891 "{\"a\":[{\"b\":\"x\"},{\"b\":\"y\"}]}");
892 checkJson("struct(a=struct(b=struct(c='c'))).to_json()",
893 "{\"a\":{\"b\":{\"c\":\"c\"}}}");
894 }
895
896 @Test
897 public void testJsonEscapes() throws Exception {
898 checkJson("struct(name='a\"b').to_json()", "{\"name\":\"a\\\"b\"}");
899 checkJson("struct(name='a\\'b').to_json()", "{\"name\":\"a'b\"}");
Erik Abairec1f2b92016-03-01 00:45:33 +0000900 checkJson("struct(name='a\\\\b').to_json()", "{\"name\":\"a\\\\b\"}");
Erik Abair927f4592016-02-29 18:57:22 +0000901 checkJson("struct(name='a\\nb').to_json()", "{\"name\":\"a\\nb\"}");
Erik Abairec1f2b92016-03-01 00:45:33 +0000902 checkJson("struct(name='a\\rb').to_json()", "{\"name\":\"a\\rb\"}");
903 checkJson("struct(name='a\\tb').to_json()", "{\"name\":\"a\\tb\"}");
Erik Abair927f4592016-02-29 18:57:22 +0000904 }
905
906 @Test
907 public void testJsonNestedListStructure() throws Exception {
908 checkJson("struct(a=[['b']]).to_json()", "{\"a\":[[\"b\"]]}");
909 }
910
911 @Test
912 public void testJsonInvalidStructure() throws Exception {
913 checkErrorContains(
914 "Invalid text format, expected a struct, a string, a bool, or an int but got a "
Vladimir Moskvacd12f772017-01-10 12:47:06 +0000915 + "function for struct field 'a'",
916 "struct(a=rule).to_json()");
Erik Abair927f4592016-02-29 18:57:22 +0000917 }
918
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000919 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000920 public void testLabelAttrWrongDefault() throws Exception {
921 checkErrorContains(
vladmos9c787fa2017-07-04 11:45:22 -0400922 "expected value of type 'string' for parameter 'default' of attribute 'label', "
923 + "but got 123 (int)",
924 "attr.label(default = 123)");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000925 }
926
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000927 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000928 public void testLabelGetRelative() throws Exception {
lberkiaea56b32017-05-30 12:35:33 +0200929 assertThat(eval("Label('//foo:bar').relative('baz')").toString()).isEqualTo("//foo:baz");
930 assertThat(eval("Label('//foo:bar').relative('//baz:qux')").toString()).isEqualTo("//baz:qux");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000931 }
932
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000933 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000934 public void testLabelGetRelativeSyntaxError() throws Exception {
935 checkErrorContains(
Damien Martin-Guillerez934c1d52017-03-03 14:44:56 +0000936 "invalid target name 'bad//syntax': target names may not contain '//' path separators",
937 "Label('//foo:bar').relative('bad//syntax')");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000938 }
Greg Estren223976c2016-02-04 22:40:56 +0000939
940 @Test
941 public void testLicenseAttributesNonconfigurable() throws Exception {
942 scratch.file("test/BUILD");
943 scratch.file("test/rule.bzl",
944 "def _impl(ctx):",
945 " return",
946 "some_rule = rule(",
947 " implementation = _impl,",
948 " attrs = {",
949 " 'licenses': attr.license()",
950 " }",
951 ")");
952 scratch.file("third_party/foo/BUILD",
953 "load('/test/rule', 'some_rule')",
954 "some_rule(",
955 " name='r',",
956 " licenses = ['unencumbered']",
957 ")");
958 invalidatePackages();
959 // Should succeed without a "licenses attribute is potentially configurable" loading error:
960 createRuleContext("//third_party/foo:r");
961 }
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +0000962
963 @Test
964 public void testStructCreation() throws Exception {
965 // TODO(fwe): cannot be handled by current testing suite
966 eval("x = struct(a = 1, b = 2)");
967 assertThat(lookup("x")).isInstanceOf(ClassObject.class);
968 }
969
970 @Test
971 public void testStructFields() throws Exception {
972 // TODO(fwe): cannot be handled by current testing suite
973 eval("x = struct(a = 1, b = 2)");
974 ClassObject x = (ClassObject) lookup("x");
lberkiaea56b32017-05-30 12:35:33 +0200975 assertThat(x.getValue("a")).isEqualTo(1);
976 assertThat(x.getValue("b")).isEqualTo(2);
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +0000977 }
978
979 @Test
Vladimir Moskvafdfa9882016-11-18 16:24:20 +0000980 public void testStructEquality() throws Exception {
lberkiaea56b32017-05-30 12:35:33 +0200981 assertThat((Boolean) eval("struct(a = 1, b = 2) == struct(b = 2, a = 1)")).isTrue();
982 assertThat((Boolean) eval("struct(a = 1) == struct(a = 1, b = 2)")).isFalse();
983 assertThat((Boolean) eval("struct(a = 1, b = 2) == struct(a = 1)")).isFalse();
Vladimir Moskvafdfa9882016-11-18 16:24:20 +0000984 // Compare a recursive object to itself to make sure reference equality is checked
lberkiaea56b32017-05-30 12:35:33 +0200985 assertThat((Boolean) eval("s = (struct(a = 1, b = [])); s.b.append(s); s == s")).isTrue();
986 assertThat((Boolean) eval("struct(a = 1, b = 2) == struct(a = 1, b = 3)")).isFalse();
987 assertThat((Boolean) eval("struct(a = 1) == [1]")).isFalse();
988 assertThat((Boolean) eval("[1] == struct(a = 1)")).isFalse();
989 assertThat((Boolean) eval("struct() == struct()")).isTrue();
990 assertThat((Boolean) eval("struct() == struct(a = 1)")).isFalse();
Vladimir Moskvafdfa9882016-11-18 16:24:20 +0000991
992 eval("foo = provider(); bar = provider()");
lberkiaea56b32017-05-30 12:35:33 +0200993 assertThat((Boolean) eval("struct(a = 1) == foo(a = 1)")).isFalse();
994 assertThat((Boolean) eval("foo(a = 1) == struct(a = 1)")).isFalse();
995 assertThat((Boolean) eval("foo(a = 1) == bar(a = 1)")).isFalse();
996 assertThat((Boolean) eval("foo(a = 1) == foo(a = 1)")).isTrue();
Vladimir Moskvafdfa9882016-11-18 16:24:20 +0000997 }
998
999 @Test
Vladimir Moskva76e31d12016-12-05 16:28:37 +00001000 public void testStructIncomparability() throws Exception {
1001 checkErrorContains("Cannot compare structs", "struct(a = 1) < struct(a = 2)");
1002 checkErrorContains("Cannot compare structs", "struct(a = 1) > struct(a = 2)");
1003 checkErrorContains("Cannot compare structs", "struct(a = 1) <= struct(a = 2)");
1004 checkErrorContains("Cannot compare structs", "struct(a = 1) >= struct(a = 2)");
1005 }
1006
1007 @Test
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001008 public void testStructAccessingFieldsFromSkylark() throws Exception {
1009 eval("x = struct(a = 1, b = 2)", "x1 = x.a", "x2 = x.b");
1010 assertThat(lookup("x1")).isEqualTo(1);
1011 assertThat(lookup("x2")).isEqualTo(2);
1012 }
1013
1014 @Test
1015 public void testStructAccessingUnknownField() throws Exception {
1016 checkErrorContains(
1017 "'struct' object has no attribute 'c'\n" + "Available attributes: a, b",
1018 "x = struct(a = 1, b = 2)",
1019 "y = x.c");
1020 }
1021
1022 @Test
1023 public void testStructAccessingUnknownFieldWithArgs() throws Exception {
1024 checkErrorContains(
1025 "struct has no method 'c'", "x = struct(a = 1, b = 2)", "y = x.c()");
1026 }
1027
1028 @Test
1029 public void testStructAccessingNonFunctionFieldWithArgs() throws Exception {
1030 checkErrorContains(
1031 "struct field 'a' is not a function", "x = struct(a = 1, b = 2)", "x1 = x.a(1)");
1032 }
1033
1034 @Test
1035 public void testStructAccessingFunctionFieldWithArgs() throws Exception {
1036 eval("def f(x): return x+5", "x = struct(a = f, b = 2)", "x1 = x.a(1)");
1037 assertThat(lookup("x1")).isEqualTo(6);
1038 }
1039
1040 @Test
1041 public void testStructPosArgs() throws Exception {
1042 checkErrorContains(
1043 "struct(**kwargs) does not accept positional arguments, but got 1", "x = struct(1, b = 2)");
1044 }
1045
1046 @Test
1047 public void testStructConcatenationFieldNames() throws Exception {
1048 // TODO(fwe): cannot be handled by current testing suite
1049 eval("x = struct(a = 1, b = 2)",
1050 "y = struct(c = 1, d = 2)",
1051 "z = x + y\n");
dslomovde965ac2017-07-31 21:07:51 +02001052 Info z = (Info) lookup("z");
lberkiaea56b32017-05-30 12:35:33 +02001053 assertThat(z.getKeys()).isEqualTo(ImmutableSet.of("a", "b", "c", "d"));
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001054 }
1055
1056 @Test
1057 public void testStructConcatenationFieldValues() throws Exception {
1058 // TODO(fwe): cannot be handled by current testing suite
1059 eval("x = struct(a = 1, b = 2)",
1060 "y = struct(c = 1, d = 2)",
1061 "z = x + y\n");
dslomovde965ac2017-07-31 21:07:51 +02001062 Info z = (Info) lookup("z");
lberkiaea56b32017-05-30 12:35:33 +02001063 assertThat(z.getValue("a")).isEqualTo(1);
1064 assertThat(z.getValue("b")).isEqualTo(2);
1065 assertThat(z.getValue("c")).isEqualTo(1);
1066 assertThat(z.getValue("d")).isEqualTo(2);
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001067 }
1068
1069 @Test
1070 public void testStructConcatenationCommonFields() throws Exception {
1071 checkErrorContains("Cannot concat structs with common field(s): a",
1072 "x = struct(a = 1, b = 2)", "y = struct(c = 1, a = 2)", "z = x + y\n");
1073 }
1074
1075 @Test
1076 public void testConditionalStructConcatenation() throws Exception {
1077 // TODO(fwe): cannot be handled by current testing suite
1078 eval("def func():",
1079 " x = struct(a = 1, b = 2)",
1080 " if True:",
1081 " x += struct(c = 1, d = 2)",
1082 " return x",
1083 "x = func()");
dslomovde965ac2017-07-31 21:07:51 +02001084 Info x = (Info) lookup("x");
lberkiaea56b32017-05-30 12:35:33 +02001085 assertThat(x.getValue("a")).isEqualTo(1);
1086 assertThat(x.getValue("b")).isEqualTo(2);
1087 assertThat(x.getValue("c")).isEqualTo(1);
1088 assertThat(x.getValue("d")).isEqualTo(2);
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001089 }
1090
1091 @Test
1092 public void testGetattrNoAttr() throws Exception {
Laurent Le Brunc31f3512016-12-29 21:41:33 +00001093 checkErrorContains(
1094 "object of type 'struct' has no attribute \"b\"", "s = struct(a='val')", "getattr(s, 'b')");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001095 }
1096
1097 @Test
1098 public void testGetattr() throws Exception {
1099 eval(
1100 "s = struct(a='val')",
1101 "x = getattr(s, 'a')",
1102 "y = getattr(s, 'b', 'def')",
1103 "z = getattr(s, 'b', default = 'def')",
1104 "w = getattr(s, 'a', default='ignored')");
1105 assertThat(lookup("x")).isEqualTo("val");
1106 assertThat(lookup("y")).isEqualTo("def");
1107 assertThat(lookup("z")).isEqualTo("def");
1108 assertThat(lookup("w")).isEqualTo("val");
1109 }
1110
1111 @Test
1112 public void testHasattr() throws Exception {
1113 eval("s = struct(a=1)",
1114 "x = hasattr(s, 'a')",
1115 "y = hasattr(s, 'b')\n");
1116 assertThat(lookup("x")).isEqualTo(true);
1117 assertThat(lookup("y")).isEqualTo(false);
1118 }
1119
1120 @Test
1121 public void testStructStr() throws Exception {
1122 assertThat(eval("str(struct(x = 2, y = 3, z = 4))"))
1123 .isEqualTo("struct(x = 2, y = 3, z = 4)");
1124 }
1125
1126 @Test
1127 public void testStructsInSets() throws Exception {
Vladimir Moskvad200daf2016-12-23 16:35:37 +00001128 eval("depset([struct(a='a')])");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001129 }
1130
1131 @Test
Vladimir Moskva76e31d12016-12-05 16:28:37 +00001132 public void testStructsInDicts() throws Exception {
1133 eval("d = {struct(a = 1): 'aa', struct(b = 2): 'bb'}");
1134 assertThat(eval("d[struct(a = 1)]")).isEqualTo("aa");
1135 assertThat(eval("d[struct(b = 2)]")).isEqualTo("bb");
1136 assertThat(eval("str([d[k] for k in d])")).isEqualTo("[\"aa\", \"bb\"]");
1137
1138 checkErrorContains(
1139 "unhashable type: 'struct'",
1140 "{struct(a = []): 'foo'}");
1141 }
1142
1143 @Test
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001144 public void testStructMembersAreImmutable() throws Exception {
1145 checkErrorContains(
Jon Brandvein162f9eb2016-11-10 00:22:43 +00001146 "cannot assign to 's.x'",
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001147 "s = struct(x = 'a')",
1148 "s.x = 'b'\n");
1149 }
1150
1151 @Test
Vladimir Moskva10770382016-08-23 15:04:54 +00001152 public void testStructDictMembersAreMutable() throws Exception {
1153 eval(
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001154 "s = struct(x = {'a' : 1})",
1155 "s.x['b'] = 2\n");
dslomovde965ac2017-07-31 21:07:51 +02001156 assertThat(((Info) lookup("s")).getValue("x")).isEqualTo(ImmutableMap.of("a", 1, "b", 2));
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001157 }
1158
1159 @Test
1160 public void testNsetGoodCompositeItem() throws Exception {
Vladimir Moskvad200daf2016-12-23 16:35:37 +00001161 eval("def func():", " return depset([struct(a='a')])", "s = func()");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001162 Collection<Object> result = ((SkylarkNestedSet) lookup("s")).toCollection();
1163 assertThat(result).hasSize(1);
dslomovde965ac2017-07-31 21:07:51 +02001164 assertThat(result.iterator().next()).isInstanceOf(Info.class);
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001165 }
1166
1167 @Test
1168 public void testNsetBadMutableItem() throws Exception {
Vladimir Moskvad200daf2016-12-23 16:35:37 +00001169 checkEvalError("depsets cannot contain mutable items", "depset([([],)])");
1170 checkEvalError("depsets cannot contain mutable items", "depset([struct(a=[])])");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001171 }
1172
dslomovde965ac2017-07-31 21:07:51 +02001173 private static Info makeStruct(String field, Object value) {
1174 return NativeProvider.STRUCT.create(ImmutableMap.of(field, value), "no field '%'");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001175 }
1176
dslomovde965ac2017-07-31 21:07:51 +02001177 private static Info makeBigStruct(Environment env) {
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001178 // struct(a=[struct(x={1:1}), ()], b=(), c={2:2})
dslomovde965ac2017-07-31 21:07:51 +02001179 return NativeProvider.STRUCT.create(
Dmitry Lomovea9de072016-08-09 09:35:40 +00001180 ImmutableMap.<String, Object>of(
dslomovde965ac2017-07-31 21:07:51 +02001181 "a",
1182 MutableList.<Object>of(
1183 env,
1184 NativeProvider.STRUCT.create(
1185 ImmutableMap.<String, Object>of(
1186 "x", SkylarkDict.<Object, Object>of(env, 1, 1)),
1187 "no field '%s'"),
1188 Tuple.of()),
Dmitry Lomovea9de072016-08-09 09:35:40 +00001189 "b", Tuple.of(),
1190 "c", SkylarkDict.<Object, Object>of(env, 2, 2)),
1191 "no field '%s'");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001192 }
1193
1194 @Test
1195 public void testStructMutabilityShallow() throws Exception {
lberkiaea56b32017-05-30 12:35:33 +02001196 assertThat(EvalUtils.isImmutable(makeStruct("a", 1))).isTrue();
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001197 }
1198
1199 private static MutableList<Object> makeList(Environment env) {
1200 return MutableList.<Object>of(env, 1, 2, 3);
1201 }
1202
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001203 @Test
1204 public void testStructMutabilityDeep() throws Exception {
lberkiaea56b32017-05-30 12:35:33 +02001205 assertThat(EvalUtils.isImmutable(Tuple.<Object>of(makeList(null)))).isTrue();
1206 assertThat(EvalUtils.isImmutable(makeStruct("a", makeList(null)))).isTrue();
1207 assertThat(EvalUtils.isImmutable(makeBigStruct(null))).isTrue();
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001208
lberkiaea56b32017-05-30 12:35:33 +02001209 assertThat(EvalUtils.isImmutable(Tuple.<Object>of(makeList(ev.getEnvironment())))).isFalse();
1210 assertThat(EvalUtils.isImmutable(makeStruct("a", makeList(ev.getEnvironment())))).isFalse();
1211 assertThat(EvalUtils.isImmutable(makeBigStruct(ev.getEnvironment()))).isFalse();
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001212 }
1213
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001214 @Test
1215 public void declaredProviders() throws Exception {
1216 evalAndExport(
1217 "data = provider()",
1218 "d = data(x = 1, y ='abc')",
1219 "d_x = d.x",
1220 "d_y = d.y"
1221 );
1222 assertThat(lookup("d_x")).isEqualTo(1);
1223 assertThat(lookup("d_y")).isEqualTo("abc");
dslomovde965ac2017-07-31 21:07:51 +02001224 SkylarkProvider dataConstructor = (SkylarkProvider) lookup("data");
1225 Info data = (Info) lookup("d");
1226 assertThat(data.getProvider()).isEqualTo(dataConstructor);
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001227 assertThat(dataConstructor.isExported()).isTrue();
1228 assertThat(dataConstructor.getPrintableName()).isEqualTo("data");
dslomovde965ac2017-07-31 21:07:51 +02001229 assertThat(dataConstructor.getKey())
1230 .isEqualTo(new SkylarkProvider.SkylarkKey(FAKE_LABEL, "data"));
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001231 }
1232
1233 @Test
1234 public void declaredProvidersConcatSuccess() throws Exception {
1235 evalAndExport(
1236 "data = provider()",
1237 "dx = data(x = 1)",
1238 "dy = data(y = 'abc')",
1239 "dxy = dx + dy",
1240 "x = dxy.x",
1241 "y = dxy.y"
1242 );
1243 assertThat(lookup("x")).isEqualTo(1);
1244 assertThat(lookup("y")).isEqualTo("abc");
dslomovde965ac2017-07-31 21:07:51 +02001245 SkylarkProvider dataConstructor = (SkylarkProvider) lookup("data");
1246 Info dx = (Info) lookup("dx");
1247 assertThat(dx.getProvider()).isEqualTo(dataConstructor);
1248 Info dy = (Info) lookup("dy");
1249 assertThat(dy.getProvider()).isEqualTo(dataConstructor);
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001250 }
1251
1252 @Test
1253 public void declaredProvidersConcatError() throws Exception {
1254 evalAndExport(
1255 "data1 = provider()",
1256 "data2 = provider()"
1257 );
1258
1259 checkEvalError("Cannot concat data1 with data2",
1260 "d1 = data1(x = 1)",
1261 "d2 = data2(y = 2)",
1262 "d = d1 + d2"
1263 );
1264 }
Dmitry Lomov94b2c882016-09-25 20:24:24 +00001265
1266 @Test
1267 public void structsAsDeclaredProvidersTest() throws Exception {
1268 evalAndExport(
1269 "data = struct(x = 1)"
1270 );
dslomovde965ac2017-07-31 21:07:51 +02001271 Info data = (Info) lookup("data");
1272 assertThat(NativeProvider.STRUCT.isExported()).isTrue();
1273 assertThat(data.getProvider()).isEqualTo(NativeProvider.STRUCT);
1274 assertThat(data.getProvider().getKey()).isEqualTo(NativeProvider.STRUCT.getKey());
Dmitry Lomov94b2c882016-09-25 20:24:24 +00001275 }
Dmitry Lomov0692c7f2016-09-30 16:43:30 +00001276
1277 @Test
allevato45b79e52017-07-07 21:40:50 +02001278 public void declaredProvidersDoc() throws Exception {
1279 evalAndExport("data1 = provider(doc='foo')");
1280 }
1281
1282 @Test
1283 public void declaredProvidersBadTypeForDoc() throws Exception {
1284 checkErrorContains(
1285 "argument 'doc' has type 'int', but should be 'string'",
1286 "provider(doc = 1)");
1287 }
1288
1289 @Test
Dmitry Lomov0692c7f2016-09-30 16:43:30 +00001290 public void aspectAllAttrs() throws Exception {
1291 evalAndExport(
1292 "def _impl(target, ctx):",
1293 " pass",
1294 "my_aspect = aspect(_impl, attr_aspects=['*'])");
1295
1296 SkylarkAspect myAspect = (SkylarkAspect) lookup("my_aspect");
Dmitry Lomov0d380642017-01-20 14:39:55 +00001297 assertThat(myAspect.getDefinition(AspectParameters.EMPTY).propagateAlong(
Dmitry Lomov0692c7f2016-09-30 16:43:30 +00001298 Attribute.attr("foo", BuildType.LABEL).allowedFileTypes().build()
Dmitry Lomov0d380642017-01-20 14:39:55 +00001299 )).isTrue();
Dmitry Lomov0692c7f2016-09-30 16:43:30 +00001300 }
1301
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001302 @Test
1303 public void aspectRequiredAspectProvidersSingle() throws Exception {
1304 evalAndExport(
1305 "def _impl(target, ctx):",
1306 " pass",
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001307 "cc = provider()",
1308 "my_aspect = aspect(_impl, required_aspect_providers=['java', cc])"
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001309 );
1310 SkylarkAspect myAspect = (SkylarkAspect) lookup("my_aspect");
1311 RequiredProviders requiredProviders = myAspect.getDefinition(AspectParameters.EMPTY)
1312 .getRequiredProvidersForAspects();
1313 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.ANY)).isTrue();
1314 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.EMPTY)).isFalse();
1315 assertThat(requiredProviders.isSatisfiedBy(
1316 AdvertisedProviderSet.builder()
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001317 .addSkylark(declared("cc"))
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001318 .addSkylark("java")
1319 .build()))
1320 .isTrue();
1321 assertThat(requiredProviders.isSatisfiedBy(
1322 AdvertisedProviderSet.builder()
1323 .addSkylark("cc")
1324 .build()))
1325 .isFalse();
1326 }
1327
1328 @Test
1329 public void aspectRequiredAspectProvidersAlternatives() throws Exception {
1330 evalAndExport(
1331 "def _impl(target, ctx):",
1332 " pass",
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001333 "cc = provider()",
1334 "my_aspect = aspect(_impl, required_aspect_providers=[['java'], [cc]])"
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001335 );
1336 SkylarkAspect myAspect = (SkylarkAspect) lookup("my_aspect");
1337 RequiredProviders requiredProviders = myAspect.getDefinition(AspectParameters.EMPTY)
1338 .getRequiredProvidersForAspects();
1339 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.ANY)).isTrue();
1340 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.EMPTY)).isFalse();
1341 assertThat(requiredProviders.isSatisfiedBy(
1342 AdvertisedProviderSet.builder()
1343 .addSkylark("java")
1344 .build()))
1345 .isTrue();
1346 assertThat(requiredProviders.isSatisfiedBy(
1347 AdvertisedProviderSet.builder()
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001348 .addSkylark(declared("cc"))
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001349 .build()))
1350 .isTrue();
1351 assertThat(requiredProviders.isSatisfiedBy(
1352 AdvertisedProviderSet.builder()
1353 .addSkylark("prolog")
1354 .build()))
1355 .isFalse();
1356 }
1357
1358 @Test
1359 public void aspectRequiredAspectProvidersEmpty() throws Exception {
1360 evalAndExport(
1361 "def _impl(target, ctx):",
1362 " pass",
1363 "my_aspect = aspect(_impl, required_aspect_providers=[])"
1364 );
1365 SkylarkAspect myAspect = (SkylarkAspect) lookup("my_aspect");
1366 RequiredProviders requiredProviders = myAspect.getDefinition(AspectParameters.EMPTY)
1367 .getRequiredProvidersForAspects();
1368 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.ANY)).isFalse();
1369 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.EMPTY)).isFalse();
1370 }
1371
1372 @Test
1373 public void aspectRequiredAspectProvidersDefault() throws Exception {
1374 evalAndExport(
1375 "def _impl(target, ctx):",
1376 " pass",
1377 "my_aspect = aspect(_impl)"
1378 );
1379 SkylarkAspect myAspect = (SkylarkAspect) lookup("my_aspect");
1380 RequiredProviders requiredProviders = myAspect.getDefinition(AspectParameters.EMPTY)
1381 .getRequiredProvidersForAspects();
1382 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.ANY)).isFalse();
1383 assertThat(requiredProviders.isSatisfiedBy(AdvertisedProviderSet.EMPTY)).isFalse();
1384 }
1385
Dmitry Lomov950310f2017-03-01 17:45:12 +00001386 @Test
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001387 public void aspectProvides() throws Exception {
1388 evalAndExport(
1389 "def _impl(target, ctx):",
1390 " pass",
1391 "y = provider()",
1392 "my_aspect = aspect(_impl, provides = ['x', y])"
1393 );
1394 SkylarkAspect myAspect = (SkylarkAspect) lookup("my_aspect");
1395 AdvertisedProviderSet advertisedProviders = myAspect.getDefinition(AspectParameters.EMPTY)
1396 .getAdvertisedProviders();
1397 assertThat(advertisedProviders.canHaveAnyProvider()).isFalse();
1398 assertThat(advertisedProviders.getSkylarkProviders())
1399 .containsExactly(legacy("x"), declared("y"));
1400 }
1401
1402 @Test
1403 public void aspectProvidesError() throws Exception {
1404 ev.setFailFast(false);
1405 evalAndExport(
1406 "def _impl(target, ctx):",
1407 " pass",
1408 "y = provider()",
1409 "my_aspect = aspect(_impl, provides = ['x', 1])"
1410 );
1411 MoreAsserts.assertContainsEvent(ev.getEventCollector(),
1412 " Illegal argument: element in 'provides' is of unexpected type."
1413 + " Should be list of providers, but got int. ");
1414 }
1415
allevato45b79e52017-07-07 21:40:50 +02001416 @Test
1417 public void aspectDoc() throws Exception {
1418 evalAndExport(
1419 "def _impl(target, ctx):",
1420 " pass",
1421 "my_aspect = aspect(_impl, doc='foo')");
1422 }
1423
1424 @Test
1425 public void aspectBadTypeForDoc() throws Exception {
1426 registerDummyUserDefinedFunction();
1427 checkErrorContains(
1428 "argument 'doc' has type 'int', but should be 'string'",
1429 "aspect(impl, doc = 1)");
1430 }
1431
Dmitry Lomov8ff5a872017-03-04 00:58:14 +00001432
1433
1434 @Test
Dmitry Lomov950310f2017-03-01 17:45:12 +00001435 public void fancyExports() throws Exception {
1436 evalAndExport(
1437 "def _impla(target, ctx): pass",
1438 "p, (a, p1) = [",
1439 " provider(),",
1440 " [ aspect(_impla),",
1441 " provider() ]",
1442 "]"
1443 );
dslomovde965ac2017-07-31 21:07:51 +02001444 SkylarkProvider p = (SkylarkProvider) lookup("p");
Dmitry Lomov950310f2017-03-01 17:45:12 +00001445 SkylarkAspect a = (SkylarkAspect) lookup("a");
dslomovde965ac2017-07-31 21:07:51 +02001446 SkylarkProvider p1 = (SkylarkProvider) lookup("p1");
Dmitry Lomov950310f2017-03-01 17:45:12 +00001447 assertThat(p.getPrintableName()).isEqualTo("p");
dslomovde965ac2017-07-31 21:07:51 +02001448 assertThat(p.getKey()).isEqualTo(new SkylarkProvider.SkylarkKey(FAKE_LABEL, "p"));
Dmitry Lomov950310f2017-03-01 17:45:12 +00001449 assertThat(p1.getPrintableName()).isEqualTo("p1");
dslomovde965ac2017-07-31 21:07:51 +02001450 assertThat(p1.getKey()).isEqualTo(new SkylarkProvider.SkylarkKey(FAKE_LABEL, "p1"));
Dmitry Lomov950310f2017-03-01 17:45:12 +00001451 assertThat(a.getAspectClass()).isEqualTo(
1452 new SkylarkAspectClass(FAKE_LABEL, "a")
1453 );
1454 }
1455
1456 @Test
1457 public void multipleTopLevels() throws Exception {
1458 evalAndExport(
1459 "p = provider()",
1460 "p1 = p"
1461 );
dslomovde965ac2017-07-31 21:07:51 +02001462 SkylarkProvider p = (SkylarkProvider) lookup("p");
1463 SkylarkProvider p1 = (SkylarkProvider) lookup("p1");
Dmitry Lomov950310f2017-03-01 17:45:12 +00001464 assertThat(p).isEqualTo(p1);
dslomovde965ac2017-07-31 21:07:51 +02001465 assertThat(p.getKey()).isEqualTo(new SkylarkProvider.SkylarkKey(FAKE_LABEL, "p"));
1466 assertThat(p1.getKey()).isEqualTo(new SkylarkProvider.SkylarkKey(FAKE_LABEL, "p"));
Dmitry Lomov950310f2017-03-01 17:45:12 +00001467 }
Dmitry Lomovf868b3e2017-01-17 10:25:28 +00001468
Dmitry Lomov0692c7f2016-09-30 16:43:30 +00001469
1470 @Test
1471 public void starTheOnlyAspectArg() throws Exception {
1472 checkEvalError("'*' must be the only string in 'attr_aspects' list",
1473 "def _impl(target, ctx):",
1474 " pass",
1475 "aspect(_impl, attr_aspects=['*', 'foo'])");
1476 }
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +00001477
1478 @Test
1479 public void testMandatoryConfigParameterForExecutableLabels() throws Exception {
1480 scratch.file("third_party/foo/extension.bzl",
1481 "def _main_rule_impl(ctx):",
1482 " pass",
1483 "my_rule = rule(_main_rule_impl,",
1484 " attrs = { ",
1485 " 'exe' : attr.label(executable = True, allow_files = True),",
1486 " },",
1487 ")"
1488 );
1489 scratch.file("third_party/foo/BUILD",
1490 "load('extension', 'my_rule')",
1491 "my_rule(name = 'main', exe = ':tool.sh')"
1492 );
1493
1494 try {
1495 createRuleContext("//third_party/foo:main");
lberki4a45ea82017-06-01 10:05:42 +02001496 fail();
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +00001497 } catch (AssertionError e) {
lberkiaea56b32017-05-30 12:35:33 +02001498 assertThat(e)
1499 .hasMessageThat()
1500 .contains("cfg parameter is mandatory when executable=True is " + "provided.");
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +00001501 }
1502 }
Vladimir Moskvaf7c552c2017-01-12 17:17:15 +00001503
John Catereca28402017-05-17 21:44:12 +02001504 @Test
1505 public void testRuleAddToolchain() throws Exception {
John Cater9a8d16e2017-07-05 16:12:07 -04001506 scratch.file("test/BUILD", "toolchain_type(name = 'my_toolchain_type')");
John Catereca28402017-05-17 21:44:12 +02001507 evalAndExport(
John Cater9a8d16e2017-07-05 16:12:07 -04001508 "def impl(ctx): return None", "r1 = rule(impl, toolchains=['//test:my_toolchain_type'])");
John Catereca28402017-05-17 21:44:12 +02001509 RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
John Cater9a8d16e2017-07-05 16:12:07 -04001510 assertThat(c.getRequiredToolchains()).containsExactly(makeLabel("//test:my_toolchain_type"));
John Catereca28402017-05-17 21:44:12 +02001511 }
vladmos2f32e382017-06-19 12:44:21 -04001512
1513 @Test
1514 public void testRuleFunctionReturnsNone() throws Exception {
1515 scratch.file("test/rule.bzl",
1516 "def _impl(ctx):",
1517 " pass",
1518 "foo_rule = rule(",
1519 " implementation = _impl,",
1520 " attrs = {'params': attr.string_list()},",
1521 ")");
1522 scratch.file("test/BUILD",
1523 "load(':rule.bzl', 'foo_rule')",
1524 "r = foo_rule(name='foo')", // Custom rule should return None
1525 "c = cc_library(name='cc')", // Native rule should return None
1526 "",
1527 "foo_rule(",
1528 " name='check',",
1529 " params = [type(r), type(c)]",
1530 ")");
1531 invalidatePackages();
1532 SkylarkRuleContext context = createRuleContext("//test:check");
1533 @SuppressWarnings("unchecked")
1534 MutableList<Object> params = (MutableList<Object>) context.getAttr().getValue("params");
1535 assertThat(params.get(0)).isEqualTo("NoneType");
1536 assertThat(params.get(1)).isEqualTo("NoneType");
1537 }
John Catereca28402017-05-17 21:44:12 +02001538}