blob: c5cf37a0c1a2181ff9124d311148ba1e8ea00499 [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;
Florian Weikertb4c59042015-12-01 10:47:18 +000018import static org.junit.Assert.assertEquals;
19import static org.junit.Assert.assertFalse;
20import static org.junit.Assert.assertSame;
21import static org.junit.Assert.assertTrue;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000022
23import com.google.common.base.Joiner;
24import com.google.common.collect.ImmutableList;
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +000025import com.google.common.collect.ImmutableMap;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000026import com.google.common.collect.ImmutableSet;
27import com.google.common.collect.Iterables;
28import com.google.devtools.build.lib.cmdline.Label;
Dmitry Lomov0692c7f2016-09-30 16:43:30 +000029import com.google.devtools.build.lib.packages.AspectParameters;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000030import com.google.devtools.build.lib.packages.Attribute;
31import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition;
32import com.google.devtools.build.lib.packages.BuildType;
33import com.google.devtools.build.lib.packages.ImplicitOutputsFunction;
34import com.google.devtools.build.lib.packages.PredicateWithMessage;
35import 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 Lomovcdb6ef52016-08-05 08:38:26 +000038import com.google.devtools.build.lib.packages.SkylarkClassObject;
Dmitry Lomovea9de072016-08-09 09:35:40 +000039import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
Dmitry Lomovfd2bdc32016-10-07 08:52:10 +000040import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier;
Dmitry Lomov7b599452015-11-26 10:07:32 +000041import com.google.devtools.build.lib.rules.SkylarkAttr;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000042import com.google.devtools.build.lib.rules.SkylarkFileType;
Dmitry Lomov7b599452015-11-26 10:07:32 +000043import com.google.devtools.build.lib.rules.SkylarkRuleClassFunctions;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000044import com.google.devtools.build.lib.rules.SkylarkRuleClassFunctions.RuleFunction;
45import com.google.devtools.build.lib.skylark.util.SkylarkTestCase;
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +000046import com.google.devtools.build.lib.syntax.ClassObject;
47import com.google.devtools.build.lib.syntax.Environment;
Dmitry Lomov7b599452015-11-26 10:07:32 +000048import com.google.devtools.build.lib.syntax.EvalException;
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +000049import com.google.devtools.build.lib.syntax.EvalUtils;
50import com.google.devtools.build.lib.syntax.SkylarkDict;
51import com.google.devtools.build.lib.syntax.SkylarkList.MutableList;
52import com.google.devtools.build.lib.syntax.SkylarkList.Tuple;
53import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000054import com.google.devtools.build.lib.syntax.Type;
55import com.google.devtools.build.lib.util.FileTypeSet;
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +000056import java.util.Collection;
Dmitry Lomov7b599452015-11-26 10:07:32 +000057import org.junit.Assert;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000058import org.junit.Before;
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +000059import org.junit.Test;
60import org.junit.runner.RunWith;
61import org.junit.runners.JUnit4;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000062
63/**
64 * Tests for SkylarkRuleClassFunctions.
65 */
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +000066@RunWith(JUnit4.class)
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000067public class SkylarkRuleClassFunctionsTest extends SkylarkTestCase {
68
69 @Before
Florian Weikertb4c59042015-12-01 10:47:18 +000070 public final void createBuildFile() throws Exception {
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000071 scratch.file(
72 "foo/BUILD",
73 "genrule(name = 'foo',",
74 " cmd = 'dummy_cmd',",
75 " srcs = ['a.txt', 'b.img'],",
76 " tools = ['t.exe'],",
77 " outs = ['c.txt'])",
78 "genrule(name = 'bar',",
79 " cmd = 'dummy_cmd',",
80 " srcs = [':jl', ':gl'],",
81 " outs = ['d.txt'])",
82 "java_library(name = 'jl',",
83 " srcs = ['a.java'])",
84 "genrule(name = 'gl',",
85 " cmd = 'touch $(OUTS)',",
86 " srcs = ['a.go'],",
87 " outs = [ 'gl.a', 'gl.gcgox', ],",
88 " output_to_bindir = 1,",
89 ")");
90 }
91
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +000092 @Test
Florian Weikerte96b0b82015-09-25 11:35:11 +000093 public void testCannotOverrideBuiltInAttribute() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +000094 ev.setFailFast(true);
95 try {
96 evalAndExport(
97 "def impl(ctx): return", "r = rule(impl, attrs = {'tags': attr.string_list()})");
98 Assert.fail("Expected error '"
99 + "There is already a built-in attribute 'tags' which cannot be overridden"
100 + "' but got no error");
Vladimir Moskvada574922016-10-05 16:36:49 +0000101 } catch (EvalException e) {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000102 assertThat(e).hasMessage(
103 "There is already a built-in attribute 'tags' which cannot be overridden");
104 }
Florian Weikerte96b0b82015-09-25 11:35:11 +0000105 }
106
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000107 @Test
Vladimir Moskvada574922016-10-05 16:36:49 +0000108 public void testCannotOverrideBuiltInAttributeName() throws Exception {
109 ev.setFailFast(true);
110 try {
111 evalAndExport(
112 "def impl(ctx): return", "r = rule(impl, attrs = {'name': attr.string()})");
113 Assert.fail("Expected error '"
114 + "There is already a built-in attribute 'name' which cannot be overridden"
115 + "' but got no error");
116 } catch (EvalException e) {
117 assertThat(e).hasMessage(
118 "There is already a built-in attribute 'name' which cannot be overridden");
119 }
120 }
121
122 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000123 public void testImplicitArgsAttribute() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000124 evalAndExport(
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000125 "def _impl(ctx):",
126 " pass",
127 "exec_rule = rule(implementation = _impl, executable = True)",
128 "non_exec_rule = rule(implementation = _impl)");
129 assertTrue(getRuleClass("exec_rule").hasAttr("args", Type.STRING_LIST));
130 assertFalse(getRuleClass("non_exec_rule").hasAttr("args", Type.STRING_LIST));
131 }
132
133 private RuleClass getRuleClass(String name) throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000134 return ((RuleFunction) lookup(name)).getRuleClass();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000135 }
136
137 private void registerDummyUserDefinedFunction() throws Exception {
138 eval("def impl():\n" + " return 0\n");
139 }
140
Dmitry Lomov7b599452015-11-26 10:07:32 +0000141 private Attribute.Builder<?> evalAttributeDefinition(String... lines) throws Exception {
142 return ((SkylarkAttr.Descriptor) evalRuleClassCode(lines)).getAttributeBuilder();
143 }
144
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000145 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000146 public void testAttrWithOnlyType() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000147 Attribute attr = evalAttributeDefinition("attr.string_list()").build("a1");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000148 assertEquals(Type.STRING_LIST, attr.getType());
149 }
150
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000151 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000152 public void testOutputListAttr() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000153 Attribute attr = evalAttributeDefinition("attr.output_list()").build("a1");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000154 assertEquals(BuildType.OUTPUT_LIST, attr.getType());
155 }
156
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000157 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000158 public void testIntListAttr() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000159 Attribute attr = evalAttributeDefinition("attr.int_list()").build("a1");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000160 assertEquals(Type.INTEGER_LIST, attr.getType());
161 }
162
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000163 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000164 public void testOutputAttr() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000165 Attribute attr = evalAttributeDefinition("attr.output()").build("a1");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000166 assertEquals(BuildType.OUTPUT, attr.getType());
167 }
168
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000169 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000170 public void testStringDictAttr() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000171 Attribute attr = evalAttributeDefinition("attr.string_dict(default = {'a': 'b'})").build("a1");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000172 assertEquals(Type.STRING_DICT, attr.getType());
173 }
174
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000175 @Test
Francois-Rene Rideau028e3192015-10-29 14:26:59 +0000176 public void testStringListDictAttr() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000177 Attribute attr = evalAttributeDefinition("attr.string_list_dict(default = {'a': ['b', 'c']})")
178 .build("a1");
Francois-Rene Rideau028e3192015-10-29 14:26:59 +0000179 assertEquals(Type.STRING_LIST_DICT, attr.getType());
180 }
181
182 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000183 public void testAttrAllowedFileTypesAnyFile() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000184 Attribute attr = evalAttributeDefinition("attr.label_list(allow_files = True)").build("a1");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000185 assertEquals(FileTypeSet.ANY_FILE, attr.getAllowedFileTypesPredicate());
186 }
187
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000188 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000189 public void testAttrAllowedFileTypesWrongType() throws Exception {
190 checkErrorContains(
Laurent Le Brun2445df12016-05-11 14:36:40 +0000191 "allow_files should be a boolean or a string list",
192 "attr.label_list(allow_files = 18)");
193 }
194
195 @Test
Laurent Le Brun50681c12016-07-05 10:08:54 +0000196 public void testAttrAllowedSingleFileTypesWrongType() throws Exception {
197 checkErrorContains(
198 "allow_single_file should be a boolean or a string list",
199 "attr.label(allow_single_file = 18)");
200 }
201
202 @Test
Laurent Le Brun2445df12016-05-11 14:36:40 +0000203 public void testAttrWithList() throws Exception {
204 Attribute attr = evalAttributeDefinition("attr.label_list(allow_files = ['.xml'])")
205 .build("a1");
206 assertTrue(attr.getAllowedFileTypesPredicate().apply("a.xml"));
207 assertFalse(attr.getAllowedFileTypesPredicate().apply("a.txt"));
Laurent Le Brun50681c12016-07-05 10:08:54 +0000208 assertFalse(attr.isSingleArtifact());
209 }
210
211 @Test
212 public void testAttrSingleFileWithList() throws Exception {
213 Attribute attr = evalAttributeDefinition("attr.label(allow_single_file = ['.xml'])")
214 .build("a1");
215 assertTrue(attr.getAllowedFileTypesPredicate().apply("a.xml"));
216 assertFalse(attr.getAllowedFileTypesPredicate().apply("a.txt"));
217 assertTrue(attr.isSingleArtifact());
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000218 }
219
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000220 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000221 public void testAttrWithSkylarkFileType() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000222 Attribute attr = evalAttributeDefinition("attr.label_list(allow_files = FileType(['.xml']))")
223 .build("a1");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000224 assertTrue(attr.getAllowedFileTypesPredicate().apply("a.xml"));
225 assertFalse(attr.getAllowedFileTypesPredicate().apply("a.txt"));
226 }
227
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000228 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000229 public void testAttrWithProviders() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000230 Attribute attr =
231 evalAttributeDefinition("attr.label_list(allow_files = True, providers = ['a', 'b'])")
Yun Pengda9410c2016-03-18 21:14:51 +0000232 .build("a1");
Dmitry Lomovfd2bdc32016-10-07 08:52:10 +0000233 assertThat(attr.getMandatoryProvidersList())
234 .containsExactly(ImmutableSet.of(legacy("a"), legacy("b")));
235 }
236
237 private static SkylarkProviderIdentifier legacy(String legacyId) {
238 return SkylarkProviderIdentifier.forLegacy(legacyId);
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000239 }
240
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000241 @Test
Yun Peng83fbb91a2016-02-23 18:37:44 +0000242 public void testAttrWithProvidersList() throws Exception {
243 Attribute attr =
Yun Pengda9410c2016-03-18 21:14:51 +0000244 evalAttributeDefinition("attr.label_list(allow_files = True,"
245 + " providers = [['a', 'b'], ['c']])")
246 .build("a1");
Dmitry Lomovfd2bdc32016-10-07 08:52:10 +0000247 assertThat(attr.getMandatoryProvidersList()).containsExactly(
248 ImmutableSet.of(legacy("a"), legacy("b")),
249 ImmutableSet.of(legacy("c")));
Yun Peng83fbb91a2016-02-23 18:37:44 +0000250 }
251
252 @Test
253 public void testAttrWithWrongProvidersList() throws Exception {
254 checkErrorContains("Illegal argument: element in 'providers' is of unexpected type."
255 + " Should be list of string, but got list with an element of type int.",
Yun Pengda9410c2016-03-18 21:14:51 +0000256 "attr.label_list(allow_files = True, providers = [['a', 1], ['c']])");
Yun Peng83fbb91a2016-02-23 18:37:44 +0000257
258 checkErrorContains("Illegal argument: element in 'providers' is of unexpected type."
259 + " Should be list of string, but got string.",
Yun Pengda9410c2016-03-18 21:14:51 +0000260 "attr.label_list(allow_files = True, providers = [['a', 'b'], 'c'])");
Yun Peng83fbb91a2016-02-23 18:37:44 +0000261 }
262
263 @Test
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000264 public void testLabelListWithAspects() throws Exception {
265 SkylarkAttr.Descriptor attr =
266 (SkylarkAttr.Descriptor) evalRuleClassCode(
Yun Pengda9410c2016-03-18 21:14:51 +0000267 "def _impl(target, ctx):",
268 " pass",
269 "my_aspect = aspect(implementation = _impl)",
270 "attr.label_list(aspects = [my_aspect])");
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000271 Object aspect = ev.lookup("my_aspect");
272 assertThat(aspect).isNotNull();
273 assertThat(attr.getAspects()).containsExactly(aspect);
274 }
275
276 @Test
277 public void testLabelListWithAspectsError() throws Exception {
278 checkErrorContains(
Florian Weikerte57eb2a2016-06-21 08:58:08 +0000279 "Illegal argument: expected type Aspect for 'aspects' element but got type int instead",
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000280 "def _impl(target, ctx):",
281 " pass",
282 "my_aspect = aspect(implementation = _impl)",
283 "attr.label_list(aspects = [my_aspect, 123])"
284 );
285 }
286
287 @Test
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000288 public void testAspectExtraDeps() throws Exception {
289 evalAndExport(
290 "def _impl(target, ctx):",
291 " pass",
292 "my_aspect = aspect(_impl,",
Dmitry Lomovace678e2015-12-16 15:10:20 +0000293 " attrs = { '_extra_deps' : attr.label(default = Label('//foo/bar:baz')) }",
294 ")");
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000295 SkylarkAspect aspect = (SkylarkAspect) ev.lookup("my_aspect");
Googler74558fc2016-05-06 21:47:42 +0000296 Attribute attribute = Iterables.getOnlyElement(aspect.getAttributes());
297 assertThat(attribute.getName()).isEqualTo("$extra_deps");
298 assertThat(attribute.getDefaultValue(null))
Brian Silvermand7d6d622016-03-17 09:53:39 +0000299 .isEqualTo(Label.parseAbsolute("//foo/bar:baz", false));
Dmitry Lomovace678e2015-12-16 15:10:20 +0000300 }
301
302 @Test
Googler74558fc2016-05-06 21:47:42 +0000303 public void testAspectParameter() throws Exception {
304 evalAndExport(
Dmitry Lomovace678e2015-12-16 15:10:20 +0000305 "def _impl(target, ctx):",
306 " pass",
307 "my_aspect = aspect(_impl,",
Googler74558fc2016-05-06 21:47:42 +0000308 " attrs = { 'param' : attr.string(values=['a', 'b']) }",
Dmitry Lomovace678e2015-12-16 15:10:20 +0000309 ")");
Googler74558fc2016-05-06 21:47:42 +0000310 SkylarkAspect aspect = (SkylarkAspect) ev.lookup("my_aspect");
311 Attribute attribute = Iterables.getOnlyElement(aspect.getAttributes());
312 assertThat(attribute.getName()).isEqualTo("param");
313 }
Michajlo Matijkiw6d471412016-08-09 20:35:45 +0000314
Googler74558fc2016-05-06 21:47:42 +0000315 @Test
316 public void testAspectParameterRequiresValues() throws Exception {
317 checkErrorContains(
318 "Aspect parameter attribute 'param' must have type 'string' and use the 'values' "
319 + "restriction.",
320 "def _impl(target, ctx):",
321 " pass",
322 "my_aspect = aspect(_impl,",
323 " attrs = { 'param' : attr.string(default = 'c') }",
324 ")");
325 }
326
327 @Test
328 public void testAspectParameterBadType() throws Exception {
329 checkErrorContains(
330 "Aspect parameter attribute 'param' must have type 'string' and use the 'values' "
331 + "restriction.",
332 "def _impl(target, ctx):",
333 " pass",
334 "my_aspect = aspect(_impl,",
335 " attrs = { 'param' : attr.label(default = Label('//foo/bar:baz')) }",
336 ")");
337 }
338
339 @Test
340 public void testAspectParameterAndExtraDeps() throws Exception {
341 evalAndExport(
342 "def _impl(target, ctx):",
343 " pass",
344 "my_aspect = aspect(_impl,",
345 " attrs = { 'param' : attr.string(values=['a', 'b']),",
346 " '_extra' : attr.label(default = Label('//foo/bar:baz')) }",
347 ")");
348 SkylarkAspect aspect = (SkylarkAspect) ev.lookup("my_aspect");
349 assertThat(aspect.getAttributes()).hasSize(2);
350 assertThat(aspect.getParamAttributes()).containsExactly("param");
Dmitry Lomovace678e2015-12-16 15:10:20 +0000351 }
352
353 @Test
354 public void testAspectNoDefaultValueAttribute() throws Exception {
355 checkErrorContains(
356 "Aspect attribute '_extra_deps' has no default value",
357 "def _impl(target, ctx):",
358 " pass",
359 "my_aspect = aspect(_impl,",
360 " attrs = { '_extra_deps' : attr.label() }",
361 ")");
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000362 }
363
364 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000365 public void testNonLabelAttrWithProviders() throws Exception {
366 checkErrorContains(
367 "unexpected keyword 'providers' in call to string", "attr.string(providers = ['a'])");
368 }
369
370 private static final RuleClass.ConfiguredTargetFactory<Object, Object>
371 DUMMY_CONFIGURED_TARGET_FACTORY =
Yun Pengda9410c2016-03-18 21:14:51 +0000372 new RuleClass.ConfiguredTargetFactory<Object, Object>() {
373 @Override
374 public Object create(Object ruleContext) throws InterruptedException {
375 throw new IllegalStateException();
376 }
377 };
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000378
379 private RuleClass ruleClass(String name) {
380 return new RuleClass.Builder(name, RuleClassType.NORMAL, false)
381 .factory(DUMMY_CONFIGURED_TARGET_FACTORY)
382 .add(Attribute.attr("tags", Type.STRING_LIST))
383 .build();
384 }
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000385 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000386 public void testAttrAllowedRuleClassesSpecificRuleClasses() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000387 Attribute attr = evalAttributeDefinition(
388 "attr.label_list(allow_rules = ['java_binary'], allow_files = True)").build("a");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000389 assertTrue(attr.getAllowedRuleClassesPredicate().apply(ruleClass("java_binary")));
390 assertFalse(attr.getAllowedRuleClassesPredicate().apply(ruleClass("genrule")));
391 }
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000392 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000393 public void testAttrDefaultValue() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000394 Attribute attr = evalAttributeDefinition("attr.string(default = 'some value')").build("a1");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000395 assertEquals("some value", attr.getDefaultValueForTesting());
396 }
397
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000398 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000399 public void testAttrDefaultValueBadType() throws Exception {
400 checkErrorContains(
401 "Method attr.string(*, default: string, mandatory: bool, values: sequence of strings) "
402 + "is not applicable for arguments (int, bool, list): 'default' is int, "
403 + "but should be string",
404 "attr.string(default = 1)");
405 }
406
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000407 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000408 public void testAttrMandatory() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000409 Attribute attr = evalAttributeDefinition("attr.string(mandatory=True)").build("a1");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000410 assertTrue(attr.isMandatory());
411 assertFalse(attr.isNonEmpty());
412 }
413
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000414 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000415 public void testAttrNonEmpty() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000416 Attribute attr = evalAttributeDefinition("attr.string_list(non_empty=True)").build("a1");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000417 assertTrue(attr.isNonEmpty());
418 assertFalse(attr.isMandatory());
419 }
420
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000421 @Test
Laurent Le Brunbc16f722016-07-06 13:40:24 +0000422 public void testAttrAllowEmpty() throws Exception {
423 Attribute attr = evalAttributeDefinition("attr.string_list(allow_empty=False)").build("a1");
424 assertTrue(attr.isNonEmpty());
425 assertFalse(attr.isMandatory());
426 }
427
428 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000429 public void testAttrBadKeywordArguments() throws Exception {
430 checkErrorContains(
431 "unexpected keyword 'bad_keyword' in call to string", "attr.string(bad_keyword = '')");
432 }
433
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000434 @Test
Laurent Le Brunc4ddf6f2016-07-04 13:38:38 +0000435 public void testAttrCfg() throws Exception {
436 Attribute attr = evalAttributeDefinition("attr.label(cfg = 'host', allow_files = True)")
437 .build("a1");
438 assertEquals(ConfigurationTransition.HOST, attr.getConfigurationTransition());
439 }
440
441 @Test
442 public void testAttrCfgData() throws Exception {
443 Attribute attr = evalAttributeDefinition("attr.label(cfg = 'data', allow_files = True)")
444 .build("a1");
445 assertEquals(ConfigurationTransition.DATA, attr.getConfigurationTransition());
446 }
447
448 @Test
Vladimir Moskva5a510772016-11-23 19:03:38 +0000449 public void testAttrCfgTarget() throws Exception {
450 Attribute attr = evalAttributeDefinition("attr.label(cfg = 'target', allow_files = True)")
451 .build("a1");
452 assertEquals(ConfigurationTransition.NONE, attr.getConfigurationTransition());
453 }
454
455 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000456 public void testAttrValues() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000457 Attribute attr = evalAttributeDefinition("attr.string(values = ['ab', 'cd'])").build("a1");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000458 PredicateWithMessage<Object> predicate = attr.getAllowedValues();
459 assertThat(predicate.apply("ab")).isTrue();
460 assertThat(predicate.apply("xy")).isFalse();
461 }
462
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000463 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000464 public void testAttrIntValues() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000465 Attribute attr = evalAttributeDefinition("attr.int(values = [1, 2])").build("a1");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000466 PredicateWithMessage<Object> predicate = attr.getAllowedValues();
467 assertThat(predicate.apply(2)).isTrue();
468 assertThat(predicate.apply(3)).isFalse();
469 }
470
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000471 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000472 public void testRuleImplementation() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000473 evalAndExport("def impl(ctx): return None", "rule1 = rule(impl)");
474 RuleClass c = ((RuleFunction) lookup("rule1")).getRuleClass();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000475 assertEquals("impl", c.getConfiguredTargetFunction().getName());
476 }
477
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000478 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000479 public void testLateBoundAttrWorksWithOnlyLabel() throws Exception {
480 checkEvalError(
481 "Method attr.string(*, default: string, mandatory: bool, values: sequence of strings) "
482 + "is not applicable for arguments (function, bool, list): 'default' is function, "
483 + "but should be string",
484 "def attr_value(cfg): return 'a'",
485 "attr.string(default=attr_value)");
486 }
487
Dmitry Lomov7b599452015-11-26 10:07:32 +0000488 private static final Label FAKE_LABEL = Label.parseAbsoluteUnchecked("//fake/label.bzl");
489
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000490 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000491 public void testRuleAddAttribute() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000492 evalAndExport("def impl(ctx): return None", "r1 = rule(impl, attrs={'a1': attr.string()})");
493 RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000494 assertTrue(c.hasAttr("a1", Type.STRING));
495 }
496
Dmitry Lomov7b599452015-11-26 10:07:32 +0000497 protected void evalAndExport(String... lines) throws Exception {
498 eval(lines);
499 SkylarkRuleClassFunctions.exportRuleFunctionsAndAspects(ev.getEnvironment(), FAKE_LABEL);
500 }
501
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000502 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000503 public void testOutputToGenfiles() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000504 evalAndExport("def impl(ctx): pass", "r1 = rule(impl, output_to_genfiles=True)");
505 RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000506 assertFalse(c.hasBinaryOutput());
507 }
508
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000509 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000510 public void testRuleAddMultipleAttributes() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000511 evalAndExport(
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000512 "def impl(ctx): return None",
513 "r1 = rule(impl,",
514 " attrs = {",
515 " 'a1': attr.label_list(allow_files=True),",
516 " 'a2': attr.int()",
517 "})");
Dmitry Lomov7b599452015-11-26 10:07:32 +0000518 RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000519 assertTrue(c.hasAttr("a1", BuildType.LABEL_LIST));
520 assertTrue(c.hasAttr("a2", Type.INTEGER));
521 }
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000522 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000523 public void testRuleAttributeFlag() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000524 evalAndExport(
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000525 "def impl(ctx): return None",
526 "r1 = rule(impl, attrs = {'a1': attr.string(mandatory=True)})");
Dmitry Lomov7b599452015-11-26 10:07:32 +0000527 RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000528 assertTrue(c.getAttributeByName("a1").isMandatory());
529 }
530
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000531 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000532 public void testRuleOutputs() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000533 evalAndExport(
534 "def impl(ctx): return None",
535 "r1 = rule(impl, outputs = {'a': 'a.txt'})");
536 RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
Michajlo Matijkiw6d471412016-08-09 20:35:45 +0000537 ImplicitOutputsFunction function = c.getDefaultImplicitOutputsFunction();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000538 assertEquals("a.txt", Iterables.getOnlyElement(function.getImplicitOutputs(null)));
539 }
540
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000541 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000542 public void testRuleUnknownKeyword() throws Exception {
543 registerDummyUserDefinedFunction();
544 checkErrorContains(
545 "unexpected keyword 'bad_keyword' in call to " + "rule(implementation: function, ",
546 "rule(impl, bad_keyword = 'some text')");
547 }
548
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000549 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000550 public void testRuleImplementationMissing() throws Exception {
551 checkErrorContains(
552 "missing mandatory positional argument 'implementation' while calling "
553 + "rule(implementation",
554 "rule(attrs = {})");
555 }
556
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000557 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000558 public void testRuleBadTypeForAdd() throws Exception {
559 registerDummyUserDefinedFunction();
560 checkErrorContains(
561 "expected dict or NoneType for 'attrs' while calling rule but got string instead: "
562 + "some text",
563 "rule(impl, attrs = 'some text')");
564 }
565
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000566 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000567 public void testRuleBadTypeInAdd() throws Exception {
568 registerDummyUserDefinedFunction();
569 checkErrorContains(
570 "Illegal argument: "
Dmitry Lomov7b599452015-11-26 10:07:32 +0000571 + "expected <String, Descriptor> type for 'attrs' but got <string, string> instead",
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000572 "rule(impl, attrs = {'a1': 'some text'})");
573 }
574
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000575 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000576 public void testLabel() throws Exception {
577 Object result = evalRuleClassCode("Label('//foo/foo:foo')");
Dmitry Lomov7b599452015-11-26 10:07:32 +0000578 assertThat(result).isInstanceOf(Label.class);
579 assertEquals("//foo/foo:foo", result.toString());
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000580 }
581
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000582 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000583 public void testLabelSameInstance() throws Exception {
584 Object l1 = evalRuleClassCode("Label('//foo/foo:foo')");
585 // Implicitly creates a new pkgContext and environment, yet labels should be the same.
586 Object l2 = evalRuleClassCode("Label('//foo/foo:foo')");
587 assertSame(l2, l1);
588 }
589
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000590 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000591 public void testLabelNameAndPackage() throws Exception {
592 Object result = evalRuleClassCode("Label('//foo/bar:baz').name");
593 assertEquals("baz", result);
594 // NB: implicitly creates a new pkgContext and environments, yet labels should be the same.
595 result = evalRuleClassCode("Label('//foo/bar:baz').package");
596 assertEquals("foo/bar", result);
597 }
598
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000599 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000600 public void testRuleLabelDefaultValue() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000601 evalAndExport(
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000602 "def impl(ctx): return None\n"
603 + "r1 = rule(impl, attrs = {'a1': "
604 + "attr.label(default = Label('//foo:foo'), allow_files=True)})");
Dmitry Lomov7b599452015-11-26 10:07:32 +0000605 RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000606 Attribute a = c.getAttributeByName("a1");
Dmitry Lomov7b599452015-11-26 10:07:32 +0000607 assertThat(a.getDefaultValueForTesting()).isInstanceOf(Label.class);
608 assertEquals("//foo:foo", a.getDefaultValueForTesting().toString());
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000609 }
610
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000611 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000612 public void testIntDefaultValue() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000613 evalAndExport(
614 "def impl(ctx): return None",
615 "r1 = rule(impl, attrs = {'a1': attr.int(default = 40+2)})");
616 RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000617 Attribute a = c.getAttributeByName("a1");
618 assertEquals(42, a.getDefaultValueForTesting());
619 }
620
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000621 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000622 public void testFileType() throws Exception {
623 Object result = evalRuleClassCode("FileType(['.css'])");
624 SkylarkFileType fts = (SkylarkFileType) result;
625 assertEquals(ImmutableList.of(".css"), fts.getExtensions());
626 }
627
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000628 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000629 public void testRuleInheritsBaseRuleAttributes() throws Exception {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000630 evalAndExport("def impl(ctx): return None", "r1 = rule(impl)");
631 RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000632 assertTrue(c.hasAttr("tags", Type.STRING_LIST));
633 assertTrue(c.hasAttr("visibility", BuildType.NODEP_LABEL_LIST));
634 assertTrue(c.hasAttr("deprecation", Type.STRING));
635 assertTrue(c.hasAttr(":action_listener", BuildType.LABEL_LIST)); // required for extra actions
636 }
637
638 private void checkTextMessage(String from, String... lines) throws Exception {
639 Object result = evalRuleClassCode(from);
640 assertEquals(Joiner.on("\n").join(lines) + "\n", result);
641 }
642
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000643 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000644 public void testSimpleTextMessagesBooleanFields() throws Exception {
645 checkTextMessage("struct(name=True).to_proto()", "name: true");
646 checkTextMessage("struct(name=False).to_proto()", "name: false");
647 }
648
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000649 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000650 public void testSimpleTextMessages() throws Exception {
651 checkTextMessage("struct(name='value').to_proto()", "name: \"value\"");
652 checkTextMessage("struct(name=['a', 'b']).to_proto()", "name: \"a\"", "name: \"b\"");
653 checkTextMessage("struct(name=123).to_proto()", "name: 123");
654 checkTextMessage("struct(name=[1, 2, 3]).to_proto()", "name: 1", "name: 2", "name: 3");
655 checkTextMessage("struct(a=struct(b='b')).to_proto()", "a {", " b: \"b\"", "}");
656 checkTextMessage(
657 "struct(a=[struct(b='x'), struct(b='y')]).to_proto()",
658 "a {",
659 " b: \"x\"",
660 "}",
661 "a {",
662 " b: \"y\"",
663 "}");
664 checkTextMessage(
665 "struct(a=struct(b=struct(c='c'))).to_proto()", "a {", " b {", " c: \"c\"", " }", "}");
666 }
667
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000668 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000669 public void testTextMessageEscapes() throws Exception {
670 checkTextMessage("struct(name='a\"b').to_proto()", "name: \"a\\\"b\"");
671 checkTextMessage("struct(name='a\\'b').to_proto()", "name: \"a'b\"");
672 checkTextMessage("struct(name='a\\nb').to_proto()", "name: \"a\\nb\"");
Googler4489aaf2016-06-17 15:17:37 +0000673
674 // struct(name="a\\\"b") -> name: "a\\\"b"
675 checkTextMessage("struct(name='a\\\\\\\"b').to_proto()", "name: \"a\\\\\\\"b\"");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000676 }
677
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000678 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000679 public void testTextMessageInvalidElementInListStructure() throws Exception {
680 checkErrorContains(
681 "Invalid text format, expected a struct, a string, a bool, or "
682 + "an int but got a list for list element in struct field 'a'",
683 "struct(a=[['b']]).to_proto()");
684 }
685
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000686 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000687 public void testTextMessageInvalidStructure() throws Exception {
688 checkErrorContains(
689 "Invalid text format, expected a struct, a string, a bool, or an int "
Damien Martin-Guillerez6022b7b2016-09-13 15:57:52 +0000690 + "but got a ConfigurationTransition for struct field 'a'",
691 "struct(a=DATA_CFG).to_proto()");
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000692 }
693
Erik Abair927f4592016-02-29 18:57:22 +0000694 private void checkJson(String from, String expected) throws Exception {
695 Object result = evalRuleClassCode(from);
696 assertEquals(expected, result);
697 }
698
699 @Test
700 public void testJsonBooleanFields() throws Exception {
701 checkJson("struct(name=True).to_json()", "{\"name\":true}");
702 checkJson("struct(name=False).to_json()", "{\"name\":false}");
703 }
704
705 @Test
706 public void testJsonEncoding() throws Exception {
707 checkJson("struct(name='value').to_json()", "{\"name\":\"value\"}");
708 checkJson("struct(name=['a', 'b']).to_json()", "{\"name\":[\"a\",\"b\"]}");
709 checkJson("struct(name=123).to_json()", "{\"name\":123}");
710 checkJson("struct(name=[1, 2, 3]).to_json()", "{\"name\":[1,2,3]}");
711 checkJson("struct(a=struct(b='b')).to_json()", "{\"a\":{\"b\":\"b\"}}");
712 checkJson("struct(a=[struct(b='x'), struct(b='y')]).to_json()",
713 "{\"a\":[{\"b\":\"x\"},{\"b\":\"y\"}]}");
714 checkJson("struct(a=struct(b=struct(c='c'))).to_json()",
715 "{\"a\":{\"b\":{\"c\":\"c\"}}}");
716 }
717
718 @Test
719 public void testJsonEscapes() throws Exception {
720 checkJson("struct(name='a\"b').to_json()", "{\"name\":\"a\\\"b\"}");
721 checkJson("struct(name='a\\'b').to_json()", "{\"name\":\"a'b\"}");
Erik Abairec1f2b92016-03-01 00:45:33 +0000722 checkJson("struct(name='a\\\\b').to_json()", "{\"name\":\"a\\\\b\"}");
Erik Abair927f4592016-02-29 18:57:22 +0000723 checkJson("struct(name='a\\nb').to_json()", "{\"name\":\"a\\nb\"}");
Erik Abairec1f2b92016-03-01 00:45:33 +0000724 checkJson("struct(name='a\\rb').to_json()", "{\"name\":\"a\\rb\"}");
725 checkJson("struct(name='a\\tb').to_json()", "{\"name\":\"a\\tb\"}");
Erik Abair927f4592016-02-29 18:57:22 +0000726 }
727
728 @Test
729 public void testJsonNestedListStructure() throws Exception {
730 checkJson("struct(a=[['b']]).to_json()", "{\"a\":[[\"b\"]]}");
731 }
732
733 @Test
734 public void testJsonInvalidStructure() throws Exception {
735 checkErrorContains(
736 "Invalid text format, expected a struct, a string, a bool, or an int but got a "
Damien Martin-Guillerez6022b7b2016-09-13 15:57:52 +0000737 + "ConfigurationTransition for struct field 'a'",
738 "struct(a=DATA_CFG).to_json()");
Erik Abair927f4592016-02-29 18:57:22 +0000739 }
740
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000741 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000742 public void testLabelAttrWrongDefault() throws Exception {
743 checkErrorContains(
744 "expected Label or Label-returning function or NoneType for 'default' "
745 + "while calling label but got string instead: //foo:bar",
746 "attr.label(default = '//foo:bar')");
747 }
748
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000749 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000750 public void testLabelGetRelative() throws Exception {
751 assertEquals("//foo:baz", eval("Label('//foo:bar').relative('baz')").toString());
752 assertEquals("//baz:qux", eval("Label('//foo:bar').relative('//baz:qux')").toString());
753 }
754
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +0000755 @Test
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000756 public void testLabelGetRelativeSyntaxError() throws Exception {
757 checkErrorContains(
758 "invalid target name 'bad syntax': target names may not contain ' '",
759 "Label('//foo:bar').relative('bad syntax')");
760 }
Greg Estren223976c2016-02-04 22:40:56 +0000761
762 @Test
763 public void testLicenseAttributesNonconfigurable() throws Exception {
764 scratch.file("test/BUILD");
765 scratch.file("test/rule.bzl",
766 "def _impl(ctx):",
767 " return",
768 "some_rule = rule(",
769 " implementation = _impl,",
770 " attrs = {",
771 " 'licenses': attr.license()",
772 " }",
773 ")");
774 scratch.file("third_party/foo/BUILD",
775 "load('/test/rule', 'some_rule')",
776 "some_rule(",
777 " name='r',",
778 " licenses = ['unencumbered']",
779 ")");
780 invalidatePackages();
781 // Should succeed without a "licenses attribute is potentially configurable" loading error:
782 createRuleContext("//third_party/foo:r");
783 }
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +0000784
785 @Test
786 public void testStructCreation() throws Exception {
787 // TODO(fwe): cannot be handled by current testing suite
788 eval("x = struct(a = 1, b = 2)");
789 assertThat(lookup("x")).isInstanceOf(ClassObject.class);
790 }
791
792 @Test
793 public void testStructFields() throws Exception {
794 // TODO(fwe): cannot be handled by current testing suite
795 eval("x = struct(a = 1, b = 2)");
796 ClassObject x = (ClassObject) lookup("x");
797 assertEquals(1, x.getValue("a"));
798 assertEquals(2, x.getValue("b"));
799 }
800
801 @Test
Vladimir Moskvafdfa9882016-11-18 16:24:20 +0000802 public void testStructEquality() throws Exception {
803 assertTrue((Boolean) eval("struct(a = 1, b = 2) == struct(b = 2, a = 1)"));
804 assertFalse((Boolean) eval("struct(a = 1) == struct(a = 1, b = 2)"));
805 assertFalse((Boolean) eval("struct(a = 1, b = 2) == struct(a = 1)"));
806 // Compare a recursive object to itself to make sure reference equality is checked
807 assertTrue((Boolean) eval("s = (struct(a = 1, b = [])); s.b.append(s); s == s"));
808 assertFalse((Boolean) eval("struct(a = 1, b = 2) == struct(a = 1, b = 3)"));
809 assertFalse((Boolean) eval("struct(a = 1) == [1]"));
810 assertFalse((Boolean) eval("[1] == struct(a = 1)"));
811 assertTrue((Boolean) eval("struct() == struct()"));
812 assertFalse((Boolean) eval("struct() == struct(a = 1)"));
813
814 eval("foo = provider(); bar = provider()");
815 assertFalse((Boolean) eval("struct(a = 1) == foo(a = 1)"));
816 assertFalse((Boolean) eval("foo(a = 1) == struct(a = 1)"));
817 assertFalse((Boolean) eval("foo(a = 1) == bar(a = 1)"));
818 assertTrue((Boolean) eval("foo(a = 1) == foo(a = 1)"));
819 }
820
821 @Test
Vladimir Moskva984d6d42016-11-22 16:51:59 +0000822 public void testStructIncomparability() throws Exception {
823 checkErrorContains("Cannot compare structs", "struct(a = 1) < struct(a = 2)");
824 checkErrorContains("Cannot compare structs", "struct(a = 1) > struct(a = 2)");
825 checkErrorContains("Cannot compare structs", "struct(a = 1) <= struct(a = 2)");
826 checkErrorContains("Cannot compare structs", "struct(a = 1) >= struct(a = 2)");
827 }
828
829 @Test
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +0000830 public void testStructAccessingFieldsFromSkylark() throws Exception {
831 eval("x = struct(a = 1, b = 2)", "x1 = x.a", "x2 = x.b");
832 assertThat(lookup("x1")).isEqualTo(1);
833 assertThat(lookup("x2")).isEqualTo(2);
834 }
835
836 @Test
837 public void testStructAccessingUnknownField() throws Exception {
838 checkErrorContains(
839 "'struct' object has no attribute 'c'\n" + "Available attributes: a, b",
840 "x = struct(a = 1, b = 2)",
841 "y = x.c");
842 }
843
844 @Test
845 public void testStructAccessingUnknownFieldWithArgs() throws Exception {
846 checkErrorContains(
847 "struct has no method 'c'", "x = struct(a = 1, b = 2)", "y = x.c()");
848 }
849
850 @Test
851 public void testStructAccessingNonFunctionFieldWithArgs() throws Exception {
852 checkErrorContains(
853 "struct field 'a' is not a function", "x = struct(a = 1, b = 2)", "x1 = x.a(1)");
854 }
855
856 @Test
857 public void testStructAccessingFunctionFieldWithArgs() throws Exception {
858 eval("def f(x): return x+5", "x = struct(a = f, b = 2)", "x1 = x.a(1)");
859 assertThat(lookup("x1")).isEqualTo(6);
860 }
861
862 @Test
863 public void testStructPosArgs() throws Exception {
864 checkErrorContains(
865 "struct(**kwargs) does not accept positional arguments, but got 1", "x = struct(1, b = 2)");
866 }
867
868 @Test
869 public void testStructConcatenationFieldNames() throws Exception {
870 // TODO(fwe): cannot be handled by current testing suite
871 eval("x = struct(a = 1, b = 2)",
872 "y = struct(c = 1, d = 2)",
873 "z = x + y\n");
874 SkylarkClassObject z = (SkylarkClassObject) lookup("z");
875 assertEquals(ImmutableSet.of("a", "b", "c", "d"), z.getKeys());
876 }
877
878 @Test
879 public void testStructConcatenationFieldValues() throws Exception {
880 // TODO(fwe): cannot be handled by current testing suite
881 eval("x = struct(a = 1, b = 2)",
882 "y = struct(c = 1, d = 2)",
883 "z = x + y\n");
884 SkylarkClassObject z = (SkylarkClassObject) lookup("z");
885 assertEquals(1, z.getValue("a"));
886 assertEquals(2, z.getValue("b"));
887 assertEquals(1, z.getValue("c"));
888 assertEquals(2, z.getValue("d"));
889 }
890
891 @Test
892 public void testStructConcatenationCommonFields() throws Exception {
893 checkErrorContains("Cannot concat structs with common field(s): a",
894 "x = struct(a = 1, b = 2)", "y = struct(c = 1, a = 2)", "z = x + y\n");
895 }
896
897 @Test
898 public void testConditionalStructConcatenation() throws Exception {
899 // TODO(fwe): cannot be handled by current testing suite
900 eval("def func():",
901 " x = struct(a = 1, b = 2)",
902 " if True:",
903 " x += struct(c = 1, d = 2)",
904 " return x",
905 "x = func()");
906 SkylarkClassObject x = (SkylarkClassObject) lookup("x");
907 assertEquals(1, x.getValue("a"));
908 assertEquals(2, x.getValue("b"));
909 assertEquals(1, x.getValue("c"));
910 assertEquals(2, x.getValue("d"));
911 }
912
913 @Test
914 public void testGetattrNoAttr() throws Exception {
915 checkErrorContains("Object of type 'struct' has no attribute \"b\"",
916 "s = struct(a='val')", "getattr(s, 'b')");
917 }
918
919 @Test
920 public void testGetattr() throws Exception {
921 eval(
922 "s = struct(a='val')",
923 "x = getattr(s, 'a')",
924 "y = getattr(s, 'b', 'def')",
925 "z = getattr(s, 'b', default = 'def')",
926 "w = getattr(s, 'a', default='ignored')");
927 assertThat(lookup("x")).isEqualTo("val");
928 assertThat(lookup("y")).isEqualTo("def");
929 assertThat(lookup("z")).isEqualTo("def");
930 assertThat(lookup("w")).isEqualTo("val");
931 }
932
933 @Test
934 public void testHasattr() throws Exception {
935 eval("s = struct(a=1)",
936 "x = hasattr(s, 'a')",
937 "y = hasattr(s, 'b')\n");
938 assertThat(lookup("x")).isEqualTo(true);
939 assertThat(lookup("y")).isEqualTo(false);
940 }
941
942 @Test
943 public void testStructStr() throws Exception {
944 assertThat(eval("str(struct(x = 2, y = 3, z = 4))"))
945 .isEqualTo("struct(x = 2, y = 3, z = 4)");
946 }
947
948 @Test
949 public void testStructsInSets() throws Exception {
950 eval("set([struct(a='a')])");
951 }
952
953 @Test
Vladimir Moskva984d6d42016-11-22 16:51:59 +0000954 public void testStructsInDicts() throws Exception {
955 eval("d = {struct(a = 1): 'aa', struct(b = 2): 'bb'}");
956 assertThat(eval("d[struct(a = 1)]")).isEqualTo("aa");
957 assertThat(eval("d[struct(b = 2)]")).isEqualTo("bb");
958
959 checkErrorContains(
960 "unhashable type: 'struct'",
961 "{struct(a = []): 'foo'}");
962 }
963
964 @Test
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +0000965 public void testStructMembersAreImmutable() throws Exception {
966 checkErrorContains(
Jon Brandvein162f9eb2016-11-10 00:22:43 +0000967 "cannot assign to 's.x'",
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +0000968 "s = struct(x = 'a')",
969 "s.x = 'b'\n");
970 }
971
972 @Test
Vladimir Moskva10770382016-08-23 15:04:54 +0000973 public void testStructDictMembersAreMutable() throws Exception {
974 eval(
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +0000975 "s = struct(x = {'a' : 1})",
976 "s.x['b'] = 2\n");
Vladimir Moskva10770382016-08-23 15:04:54 +0000977 assertThat(((SkylarkClassObject) lookup("s")).getValue("x"))
978 .isEqualTo(ImmutableMap.of("a", 1, "b", 2));
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +0000979 }
980
981 @Test
982 public void testNsetGoodCompositeItem() throws Exception {
983 eval("def func():",
984 " return set([struct(a='a')])",
985 "s = func()");
986 Collection<Object> result = ((SkylarkNestedSet) lookup("s")).toCollection();
987 assertThat(result).hasSize(1);
988 assertThat(result.iterator().next()).isInstanceOf(SkylarkClassObject.class);
989 }
990
991 @Test
992 public void testNsetBadMutableItem() throws Exception {
993 checkEvalError("sets cannot contain mutable items", "set([([],)])");
994 checkEvalError("sets cannot contain mutable items", "set([struct(a=[])])");
995 }
996
997 private static SkylarkClassObject makeStruct(String field, Object value) {
Dmitry Lomovea9de072016-08-09 09:35:40 +0000998 return SkylarkClassObjectConstructor.STRUCT.create(
999 ImmutableMap.of(field, value),
1000 "no field '%'");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001001 }
1002
1003 private static SkylarkClassObject makeBigStruct(Environment env) {
1004 // struct(a=[struct(x={1:1}), ()], b=(), c={2:2})
Dmitry Lomovea9de072016-08-09 09:35:40 +00001005 return SkylarkClassObjectConstructor.STRUCT.create(
1006 ImmutableMap.<String, Object>of(
1007 "a", MutableList.<Object>of(env,
1008 SkylarkClassObjectConstructor.STRUCT.create(ImmutableMap.<String, Object>of(
1009 "x", SkylarkDict.<Object, Object>of(env, 1, 1)),
1010 "no field '%s'"),
1011 Tuple.of()),
1012 "b", Tuple.of(),
1013 "c", SkylarkDict.<Object, Object>of(env, 2, 2)),
1014 "no field '%s'");
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001015 }
1016
1017 @Test
1018 public void testStructMutabilityShallow() throws Exception {
1019 assertTrue(EvalUtils.isImmutable(makeStruct("a", 1)));
1020 }
1021
1022 private static MutableList<Object> makeList(Environment env) {
1023 return MutableList.<Object>of(env, 1, 2, 3);
1024 }
1025
Dmitry Lomovcdb6ef52016-08-05 08:38:26 +00001026 @Test
1027 public void testStructMutabilityDeep() throws Exception {
1028 assertTrue(EvalUtils.isImmutable(Tuple.<Object>of(makeList(null))));
1029 assertTrue(EvalUtils.isImmutable(makeStruct("a", makeList(null))));
1030 assertTrue(EvalUtils.isImmutable(makeBigStruct(null)));
1031
1032 assertFalse(EvalUtils.isImmutable(Tuple.<Object>of(makeList(ev.getEnvironment()))));
1033 assertFalse(EvalUtils.isImmutable(makeStruct("a", makeList(ev.getEnvironment()))));
1034 assertFalse(EvalUtils.isImmutable(makeBigStruct(ev.getEnvironment())));
1035 }
1036
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001037 @Test
1038 public void declaredProviders() throws Exception {
1039 evalAndExport(
1040 "data = provider()",
1041 "d = data(x = 1, y ='abc')",
1042 "d_x = d.x",
1043 "d_y = d.y"
1044 );
1045 assertThat(lookup("d_x")).isEqualTo(1);
1046 assertThat(lookup("d_y")).isEqualTo("abc");
1047 SkylarkClassObjectConstructor dataConstructor = (SkylarkClassObjectConstructor) lookup("data");
1048 SkylarkClassObject data = (SkylarkClassObject) lookup("d");
1049 assertThat(data.getConstructor()).isEqualTo(dataConstructor);
1050 assertThat(dataConstructor.isExported()).isTrue();
1051 assertThat(dataConstructor.getPrintableName()).isEqualTo("data");
1052 assertThat(dataConstructor.getKey()).isEqualTo(
Dmitry Lomov94b2c882016-09-25 20:24:24 +00001053 new SkylarkClassObjectConstructor.SkylarkKey(FAKE_LABEL, "data")
Dmitry Lomov8a07a952016-08-10 18:29:04 +00001054 );
1055 }
1056
1057 @Test
1058 public void declaredProvidersConcatSuccess() throws Exception {
1059 evalAndExport(
1060 "data = provider()",
1061 "dx = data(x = 1)",
1062 "dy = data(y = 'abc')",
1063 "dxy = dx + dy",
1064 "x = dxy.x",
1065 "y = dxy.y"
1066 );
1067 assertThat(lookup("x")).isEqualTo(1);
1068 assertThat(lookup("y")).isEqualTo("abc");
1069 SkylarkClassObjectConstructor dataConstructor = (SkylarkClassObjectConstructor) lookup("data");
1070 SkylarkClassObject dx = (SkylarkClassObject) lookup("dx");
1071 assertThat(dx.getConstructor()).isEqualTo(dataConstructor);
1072 SkylarkClassObject dy = (SkylarkClassObject) lookup("dy");
1073 assertThat(dy.getConstructor()).isEqualTo(dataConstructor);
1074 }
1075
1076 @Test
1077 public void declaredProvidersConcatError() throws Exception {
1078 evalAndExport(
1079 "data1 = provider()",
1080 "data2 = provider()"
1081 );
1082
1083 checkEvalError("Cannot concat data1 with data2",
1084 "d1 = data1(x = 1)",
1085 "d2 = data2(y = 2)",
1086 "d = d1 + d2"
1087 );
1088 }
Dmitry Lomov94b2c882016-09-25 20:24:24 +00001089
1090 @Test
1091 public void structsAsDeclaredProvidersTest() throws Exception {
1092 evalAndExport(
1093 "data = struct(x = 1)"
1094 );
1095 SkylarkClassObject data = (SkylarkClassObject) lookup("data");
1096 assertThat(SkylarkClassObjectConstructor.STRUCT.isExported()).isTrue();
1097 assertThat(data.getConstructor()).isEqualTo(SkylarkClassObjectConstructor.STRUCT);
1098 assertThat(data.getConstructor().getKey())
1099 .isEqualTo(SkylarkClassObjectConstructor.STRUCT.getKey());
1100 }
Dmitry Lomov0692c7f2016-09-30 16:43:30 +00001101
1102 @Test
1103 public void aspectAllAttrs() throws Exception {
1104 evalAndExport(
1105 "def _impl(target, ctx):",
1106 " pass",
1107 "my_aspect = aspect(_impl, attr_aspects=['*'])");
1108
1109 SkylarkAspect myAspect = (SkylarkAspect) lookup("my_aspect");
1110 assertThat(myAspect.getDefinition(AspectParameters.EMPTY).getAttributeAspects(
1111 Attribute.attr("foo", BuildType.LABEL).allowedFileTypes().build()
1112 )).containsExactly(myAspect.getAspectClass());
1113 }
1114
1115
1116 @Test
1117 public void starTheOnlyAspectArg() throws Exception {
1118 checkEvalError("'*' must be the only string in 'attr_aspects' list",
1119 "def _impl(target, ctx):",
1120 " pass",
1121 "aspect(_impl, attr_aspects=['*', 'foo'])");
1122 }
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +00001123}