blob: 37e28b4a5f279d017fa43a785f45d16ecefff58e [file] [log] [blame]
Benjamin Peterson8610d972017-11-27 03:14:43 -08001// Copyright 2017 The Bazel Authors. All rights reserved.
2//
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.
brandjonb178e892018-01-12 16:17:00 -080014
Benjamin Peterson8610d972017-11-27 03:14:43 -080015package com.google.devtools.build.lib.packages;
16
17import static com.google.common.truth.Truth.assertThat;
brandjonb178e892018-01-12 16:17:00 -080018import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows;
Benjamin Peterson8610d972017-11-27 03:14:43 -080019
20import com.google.common.collect.ImmutableList;
21import com.google.common.collect.ImmutableMap;
22import com.google.common.testing.EqualsTester;
23import com.google.devtools.build.lib.cmdline.Label;
24import com.google.devtools.build.lib.events.Location;
brandjonb178e892018-01-12 16:17:00 -080025import com.google.devtools.build.lib.packages.SkylarkInfo.Layout;
brandjon04b5ab22018-01-11 09:09:56 -080026import com.google.devtools.build.lib.syntax.EvalException;
brandjonb178e892018-01-12 16:17:00 -080027import java.util.Map;
brandjon04b5ab22018-01-11 09:09:56 -080028import javax.annotation.Nullable;
Benjamin Peterson8610d972017-11-27 03:14:43 -080029import org.junit.Test;
30import org.junit.runner.RunWith;
31import org.junit.runners.JUnit4;
32
33/** Test class for {@link SkylarkInfo} and its subclasses. */
34@RunWith(JUnit4.class)
35public class SkylarkInfoTest {
36
brandjonb178e892018-01-12 16:17:00 -080037 private static final Layout layoutF1F2 = new Layout(ImmutableList.of("f1", "f2"));
38 private static final Layout invertedLayoutF2F1 = new Layout(ImmutableList.of("f2", "f1"));
39
40 @Test
41 public void layoutAccessors() {
42 Layout layout = new Layout(ImmutableList.of("x", "y", "z"));
43 assertThat(layout.size()).isEqualTo(3);
44 assertThat(layout.hasField("x")).isTrue();
45 assertThat(layout.hasField("q")).isFalse();
46 assertThat(layout.getFieldIndex("z")).isEqualTo(2);
47 assertThat(layout.getFields()).containsExactly("x", "y", "z").inOrder();
48 assertThat(
49 layout.entrySet().stream()
50 .map(Map.Entry::getKey)
51 .collect(ImmutableList.toImmutableList()))
52 .containsExactly("x", "y", "z").inOrder();
53 }
54
55 @Test
56 public void layoutDisallowsDuplicates() {
57 assertThrows(
58 IllegalArgumentException.class,
59 () -> new Layout(ImmutableList.of("x", "y", "x")));
60 }
61
62 @Test
63 public void layoutEquality() {
64 new EqualsTester()
65 .addEqualityGroup(
66 new Layout(ImmutableList.of("a", "b", "c")),
67 new Layout(ImmutableList.of("a", "b", "c")))
68 .addEqualityGroup(
69 new Layout(ImmutableList.of("x", "y", "z")))
70 .addEqualityGroup(
71 new Layout(ImmutableList.of("c", "b", "a")))
72 .testEquals();
73 }
74
Benjamin Peterson8610d972017-11-27 03:14:43 -080075 @Test
brandjon04b5ab22018-01-11 09:09:56 -080076 public void nullLocationDefaultsToBuiltin() throws Exception {
brandjonb178e892018-01-12 16:17:00 -080077 SkylarkInfo info = SkylarkInfo.createSchemaless(makeProvider(), ImmutableMap.of(), null);
brandjon04b5ab22018-01-11 09:09:56 -080078 assertThat(info.getCreationLoc()).isEqualTo(Location.BUILTIN);
Benjamin Peterson8610d972017-11-27 03:14:43 -080079 }
80
81 @Test
brandjonb178e892018-01-12 16:17:00 -080082 public void givenLayoutTakesPrecedenceOverProviderLayout() throws Exception {
83 SkylarkProvider provider =
84 SkylarkProvider.createUnexportedSchemaful(ImmutableList.of("f1", "f2"), Location.BUILTIN);
85 SkylarkInfo info =
86 SkylarkInfo.createSchemaful(
87 provider, invertedLayoutF2F1, new Object[]{5, 4}, Location.BUILTIN);
88 assertThat(info.getLayout()).isEqualTo(invertedLayoutF2F1); // not the one in the provider
brandjon04b5ab22018-01-11 09:09:56 -080089 }
90
91 @Test
92 public void schemafulValuesMustMatchLayoutArity() throws Exception {
brandjonb178e892018-01-12 16:17:00 -080093 SkylarkProvider provider = makeProvider();
cushon978cb002018-02-24 14:05:37 -080094 IllegalArgumentException expected =
95 assertThrows(
96 IllegalArgumentException.class,
97 () ->
98 SkylarkInfo.createSchemaful(
99 provider, layoutF1F2, new Object[] {4}, Location.BUILTIN));
brandjon04b5ab22018-01-11 09:09:56 -0800100 assertThat(expected).hasMessageThat()
101 .contains("Layout has length 2, but number of given values was 1");
102 }
103
104 @Test
105 public void instancesOfUnexportedProvidersAreMutable() throws Exception {
brandjonb178e892018-01-12 16:17:00 -0800106 SkylarkProvider provider = makeProvider();
brandjon04b5ab22018-01-11 09:09:56 -0800107 SkylarkInfo mapInfo = makeSchemalessInfoWithF1F2Values(provider, 5, null);
108 SkylarkInfo compactInfo = makeSchemafulInfoWithF1F2Values(provider, 5, null);
Benjamin Peterson8610d972017-11-27 03:14:43 -0800109 assertThat(mapInfo.isImmutable()).isFalse();
brandjon04b5ab22018-01-11 09:09:56 -0800110 assertThat(compactInfo.isImmutable()).isFalse();
111 }
112
113 @Test
114 public void instancesOfExportedProvidersMayBeImmutable() throws Exception {
brandjonb178e892018-01-12 16:17:00 -0800115 SkylarkProvider provider = makeExportedProvider();
brandjon04b5ab22018-01-11 09:09:56 -0800116 SkylarkInfo mapInfo = makeSchemalessInfoWithF1F2Values(provider, 5, null);
117 SkylarkInfo compactInfo = makeSchemafulInfoWithF1F2Values(provider, 5, null);
Benjamin Peterson8610d972017-11-27 03:14:43 -0800118 assertThat(mapInfo.isImmutable()).isTrue();
brandjon04b5ab22018-01-11 09:09:56 -0800119 assertThat(compactInfo.isImmutable()).isTrue();
Benjamin Peterson8610d972017-11-27 03:14:43 -0800120 }
121
122 @Test
brandjon04b5ab22018-01-11 09:09:56 -0800123 public void mutableIfContentsAreMutable() throws Exception {
brandjonb178e892018-01-12 16:17:00 -0800124 SkylarkProvider provider = makeExportedProvider();
brandjon04b5ab22018-01-11 09:09:56 -0800125 SkylarkInfo mapInfo = makeSchemalessInfoWithF1F2Values(provider, 5, new Object());
126 SkylarkInfo compactInfo = makeSchemafulInfoWithF1F2Values(provider, 5, new Object());
127 assertThat(mapInfo.isImmutable()).isFalse();
128 assertThat(compactInfo.isImmutable()).isFalse();
129 }
130
131 @Test
132 public void equality_DifferentProviders() throws Exception {
brandjonb178e892018-01-12 16:17:00 -0800133 SkylarkProvider provider1 = makeProvider();
134 SkylarkProvider provider2 = makeProvider();
Benjamin Peterson8610d972017-11-27 03:14:43 -0800135 new EqualsTester()
136 .addEqualityGroup(
brandjon04b5ab22018-01-11 09:09:56 -0800137 makeSchemalessInfoWithF1F2Values(provider1, 4, 5),
138 makeSchemafulInfoWithF1F2Values(provider1, 4, 5),
139 makeInvertedSchemafulInfoWithF1F2Values(provider1, 4, 5))
Benjamin Peterson8610d972017-11-27 03:14:43 -0800140 .addEqualityGroup(
brandjon04b5ab22018-01-11 09:09:56 -0800141 makeSchemalessInfoWithF1F2Values(provider2, 4, 5),
142 makeSchemafulInfoWithF1F2Values(provider2, 4, 5),
143 makeInvertedSchemafulInfoWithF1F2Values(provider2, 4, 5))
Benjamin Peterson8610d972017-11-27 03:14:43 -0800144 .testEquals();
145 }
146
147 @Test
brandjon04b5ab22018-01-11 09:09:56 -0800148 public void equality_DifferentValues() throws Exception {
brandjonb178e892018-01-12 16:17:00 -0800149 SkylarkProvider provider = makeProvider();
brandjon04b5ab22018-01-11 09:09:56 -0800150 // These comparisons include the case where the physical array is {4, 5} on both instances but
151 // they compare different due to different layouts.
152 new EqualsTester()
153 .addEqualityGroup(
154 makeSchemalessInfoWithF1F2Values(provider, 4, 5),
155 makeSchemafulInfoWithF1F2Values(provider, 4, 5),
156 makeInvertedSchemafulInfoWithF1F2Values(provider, 4, 5))
157 .addEqualityGroup(
158 makeSchemalessInfoWithF1F2Values(provider, 5, 4),
159 makeSchemafulInfoWithF1F2Values(provider, 5, 4),
160 makeInvertedSchemafulInfoWithF1F2Values(provider, 5, 4))
161 .addEqualityGroup(
162 makeSchemalessInfoWithF1F2Values(provider, 4, null),
163 makeSchemafulInfoWithF1F2Values(provider, 4, null),
164 makeInvertedSchemafulInfoWithF1F2Values(provider, 4, null))
165 .testEquals();
Benjamin Peterson8610d972017-11-27 03:14:43 -0800166 }
167
168 @Test
brandjon04b5ab22018-01-11 09:09:56 -0800169 public void concatWithDifferentProvidersFails() throws Exception {
brandjonb178e892018-01-12 16:17:00 -0800170 SkylarkProvider provider1 = makeProvider();
171 SkylarkProvider provider2 = makeProvider();
brandjon04b5ab22018-01-11 09:09:56 -0800172 SkylarkInfo info1 = makeSchemalessInfoWithF1F2Values(provider1, 4, 5);
173 SkylarkInfo info2 = makeSchemalessInfoWithF1F2Values(provider2, 4, 5);
174 EvalException expected =
cushon978cb002018-02-24 14:05:37 -0800175 assertThrows(
176 EvalException.class, () -> info1.getConcatter().concat(info1, info2, Location.BUILTIN));
brandjon04b5ab22018-01-11 09:09:56 -0800177 assertThat(expected).hasMessageThat()
178 .contains("Cannot use '+' operator on instances of different providers");
179 }
180
181 @Test
182 public void concatWithOverlappingFieldsFails() throws Exception {
brandjonb178e892018-01-12 16:17:00 -0800183 SkylarkProvider provider1 = makeProvider();
brandjon04b5ab22018-01-11 09:09:56 -0800184 SkylarkInfo info1 = makeSchemalessInfoWithF1F2Values(provider1, 4, 5);
185 SkylarkInfo info2 = makeSchemalessInfoWithF1F2Values(provider1, 4, null);
186 EvalException expected =
cushon978cb002018-02-24 14:05:37 -0800187 assertThrows(
188 EvalException.class, () -> info1.getConcatter().concat(info1, info2, Location.BUILTIN));
brandjon04b5ab22018-01-11 09:09:56 -0800189 assertThat(expected).hasMessageThat()
190 .contains("Cannot use '+' operator on provider instances with overlapping field(s): f1");
191 }
192
193 @Test
194 public void compactConcatReturnsCompact() throws Exception {
brandjonb178e892018-01-12 16:17:00 -0800195 SkylarkProvider provider = makeProvider();
196 SkylarkInfo info1 = makeSchemafulInfoWithF1F2Values(provider, 4, null);
197 SkylarkInfo info2 = makeSchemafulInfoWithF1F2Values(provider, null, 5);
198 SkylarkInfo result = (SkylarkInfo) info1.getConcatter().concat(info1, info2, Location.BUILTIN);
brandjon04b5ab22018-01-11 09:09:56 -0800199 assertThat(result.isCompact()).isTrue();
Googler98eab102018-10-11 12:28:22 -0700200 assertThat(result.getFieldNames()).containsExactly("f1", "f2");
201 assertThat(result.getValue("f1")).isEqualTo(4);
202 assertThat(result.getValue("f2")).isEqualTo(5);
brandjon04b5ab22018-01-11 09:09:56 -0800203 }
204
205 @Test
206 public void compactConcatWithDifferentLayoutsReturnsMap() throws Exception {
brandjonb178e892018-01-12 16:17:00 -0800207 SkylarkProvider provider = makeProvider();
brandjon04b5ab22018-01-11 09:09:56 -0800208 SkylarkInfo info1 = makeSchemafulInfoWithF1F2Values(provider, 4, null);
209 SkylarkInfo info2 = makeInvertedSchemafulInfoWithF1F2Values(provider, null, 5);
210 SkylarkInfo result = (SkylarkInfo) info1.getConcatter().concat(info1, info2, Location.BUILTIN);
211 assertThat(result.isCompact()).isFalse();
Googler98eab102018-10-11 12:28:22 -0700212 assertThat(result.getFieldNames()).containsExactly("f1", "f2");
brandjon04b5ab22018-01-11 09:09:56 -0800213 assertThat(result.getValue("f1")).isEqualTo(4);
214 assertThat(result.getValue("f2")).isEqualTo(5);
215 }
216
217 @Test
218 public void allOtherConcatReturnsMap() throws Exception {
brandjonb178e892018-01-12 16:17:00 -0800219 SkylarkProvider provider = makeProvider();
220 SkylarkInfo info1 = makeSchemalessInfoWithF1F2Values(provider, 4, null);
221 SkylarkInfo info2 = makeSchemafulInfoWithF1F2Values(provider, null, 5);
222 SkylarkInfo result = (SkylarkInfo) info1.getConcatter().concat(info1, info2, Location.BUILTIN);
brandjon04b5ab22018-01-11 09:09:56 -0800223 assertThat(result.isCompact()).isFalse();
Googler98eab102018-10-11 12:28:22 -0700224 assertThat(result.getFieldNames()).containsExactly("f1", "f2");
225 assertThat(result.getValue("f1")).isEqualTo(4);
226 assertThat(result.getValue("f2")).isEqualTo(5);
brandjon04b5ab22018-01-11 09:09:56 -0800227 }
228
brandjonb178e892018-01-12 16:17:00 -0800229 /** Creates an unexported schemaless provider type with builtin location. */
230 private static SkylarkProvider makeProvider() {
231 return SkylarkProvider.createUnexportedSchemaless(Location.BUILTIN);
232 }
233
234 /** Creates an exported schemaless provider type with builtin location. */
235 private static SkylarkProvider makeExportedProvider() {
236 SkylarkProvider.SkylarkKey key = new SkylarkProvider.SkylarkKey(
237 Label.parseAbsoluteUnchecked("//package:target"), "provider");
238 return SkylarkProvider.createExportedSchemaless(key, Location.BUILTIN);
brandjon04b5ab22018-01-11 09:09:56 -0800239 }
240
241 /**
242 * Creates a schemaless instance of a provider with the given values for fields f1 and f2. Either
243 * field value may be null, in which case it is omitted.
244 */
245 private static SkylarkInfo makeSchemalessInfoWithF1F2Values(
246 SkylarkProvider provider, @Nullable Object v1, @Nullable Object v2) {
247 ImmutableMap.Builder<String, Object> values = ImmutableMap.builder();
248 if (v1 != null) {
249 values.put("f1", v1);
250 }
251 if (v2 != null) {
252 values.put("f2", v2);
253 }
254 return SkylarkInfo.createSchemaless(provider, values.build(), Location.BUILTIN);
255 }
256
257 /**
258 * Creates a schemaful instance of a provider with the given values for fields f1 and f2. Either
259 * field value may be null, in which case it is omitted.
260 */
261 private static SkylarkInfo makeSchemafulInfoWithF1F2Values(
262 SkylarkProvider provider, @Nullable Object v1, @Nullable Object v2) {
brandjonb178e892018-01-12 16:17:00 -0800263 return SkylarkInfo.createSchemaful(
264 provider, layoutF1F2, new Object[]{v1, v2}, Location.BUILTIN);
brandjon04b5ab22018-01-11 09:09:56 -0800265 }
266
267 /**
268 * Same as {@link #makeSchemafulInfoWithF1F2Values}, except the layout in the resulting
269 * CompactSkylarkInfo is reversed.
270 */
271 private static SkylarkInfo makeInvertedSchemafulInfoWithF1F2Values(
272 SkylarkProvider provider, @Nullable Object v1, @Nullable Object v2) {
brandjonb178e892018-01-12 16:17:00 -0800273 return SkylarkInfo.createSchemaful(
274 provider, invertedLayoutF2F1, new Object[]{v2, v1}, Location.BUILTIN);
Benjamin Peterson8610d972017-11-27 03:14:43 -0800275 }
276}