Benjamin Peterson | 8610d97 | 2017-11-27 03:14:43 -0800 | [diff] [blame] | 1 | // 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. |
brandjon | b178e89 | 2018-01-12 16:17:00 -0800 | [diff] [blame] | 14 | |
Benjamin Peterson | 8610d97 | 2017-11-27 03:14:43 -0800 | [diff] [blame] | 15 | package com.google.devtools.build.lib.packages; |
| 16 | |
| 17 | import static com.google.common.truth.Truth.assertThat; |
brandjon | b178e89 | 2018-01-12 16:17:00 -0800 | [diff] [blame] | 18 | import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows; |
Benjamin Peterson | 8610d97 | 2017-11-27 03:14:43 -0800 | [diff] [blame] | 19 | |
| 20 | import com.google.common.collect.ImmutableList; |
| 21 | import com.google.common.collect.ImmutableMap; |
| 22 | import com.google.common.testing.EqualsTester; |
| 23 | import com.google.devtools.build.lib.cmdline.Label; |
| 24 | import com.google.devtools.build.lib.events.Location; |
brandjon | b178e89 | 2018-01-12 16:17:00 -0800 | [diff] [blame] | 25 | import com.google.devtools.build.lib.packages.SkylarkInfo.Layout; |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 26 | import com.google.devtools.build.lib.syntax.EvalException; |
brandjon | b178e89 | 2018-01-12 16:17:00 -0800 | [diff] [blame] | 27 | import java.util.Map; |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 28 | import javax.annotation.Nullable; |
Benjamin Peterson | 8610d97 | 2017-11-27 03:14:43 -0800 | [diff] [blame] | 29 | import org.junit.Test; |
| 30 | import org.junit.runner.RunWith; |
| 31 | import org.junit.runners.JUnit4; |
| 32 | |
| 33 | /** Test class for {@link SkylarkInfo} and its subclasses. */ |
| 34 | @RunWith(JUnit4.class) |
| 35 | public class SkylarkInfoTest { |
| 36 | |
brandjon | b178e89 | 2018-01-12 16:17:00 -0800 | [diff] [blame] | 37 | 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 Peterson | 8610d97 | 2017-11-27 03:14:43 -0800 | [diff] [blame] | 75 | @Test |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 76 | public void nullLocationDefaultsToBuiltin() throws Exception { |
brandjon | b178e89 | 2018-01-12 16:17:00 -0800 | [diff] [blame] | 77 | SkylarkInfo info = SkylarkInfo.createSchemaless(makeProvider(), ImmutableMap.of(), null); |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 78 | assertThat(info.getCreationLoc()).isEqualTo(Location.BUILTIN); |
Benjamin Peterson | 8610d97 | 2017-11-27 03:14:43 -0800 | [diff] [blame] | 79 | } |
| 80 | |
| 81 | @Test |
brandjon | b178e89 | 2018-01-12 16:17:00 -0800 | [diff] [blame] | 82 | 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 |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 89 | } |
| 90 | |
| 91 | @Test |
| 92 | public void schemafulValuesMustMatchLayoutArity() throws Exception { |
brandjon | b178e89 | 2018-01-12 16:17:00 -0800 | [diff] [blame] | 93 | SkylarkProvider provider = makeProvider(); |
cushon | 978cb00 | 2018-02-24 14:05:37 -0800 | [diff] [blame] | 94 | IllegalArgumentException expected = |
| 95 | assertThrows( |
| 96 | IllegalArgumentException.class, |
| 97 | () -> |
| 98 | SkylarkInfo.createSchemaful( |
| 99 | provider, layoutF1F2, new Object[] {4}, Location.BUILTIN)); |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 100 | 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 { |
brandjon | b178e89 | 2018-01-12 16:17:00 -0800 | [diff] [blame] | 106 | SkylarkProvider provider = makeProvider(); |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 107 | SkylarkInfo mapInfo = makeSchemalessInfoWithF1F2Values(provider, 5, null); |
| 108 | SkylarkInfo compactInfo = makeSchemafulInfoWithF1F2Values(provider, 5, null); |
Benjamin Peterson | 8610d97 | 2017-11-27 03:14:43 -0800 | [diff] [blame] | 109 | assertThat(mapInfo.isImmutable()).isFalse(); |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 110 | assertThat(compactInfo.isImmutable()).isFalse(); |
| 111 | } |
| 112 | |
| 113 | @Test |
| 114 | public void instancesOfExportedProvidersMayBeImmutable() throws Exception { |
brandjon | b178e89 | 2018-01-12 16:17:00 -0800 | [diff] [blame] | 115 | SkylarkProvider provider = makeExportedProvider(); |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 116 | SkylarkInfo mapInfo = makeSchemalessInfoWithF1F2Values(provider, 5, null); |
| 117 | SkylarkInfo compactInfo = makeSchemafulInfoWithF1F2Values(provider, 5, null); |
Benjamin Peterson | 8610d97 | 2017-11-27 03:14:43 -0800 | [diff] [blame] | 118 | assertThat(mapInfo.isImmutable()).isTrue(); |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 119 | assertThat(compactInfo.isImmutable()).isTrue(); |
Benjamin Peterson | 8610d97 | 2017-11-27 03:14:43 -0800 | [diff] [blame] | 120 | } |
| 121 | |
| 122 | @Test |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 123 | public void mutableIfContentsAreMutable() throws Exception { |
brandjon | b178e89 | 2018-01-12 16:17:00 -0800 | [diff] [blame] | 124 | SkylarkProvider provider = makeExportedProvider(); |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 125 | 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 { |
brandjon | b178e89 | 2018-01-12 16:17:00 -0800 | [diff] [blame] | 133 | SkylarkProvider provider1 = makeProvider(); |
| 134 | SkylarkProvider provider2 = makeProvider(); |
Benjamin Peterson | 8610d97 | 2017-11-27 03:14:43 -0800 | [diff] [blame] | 135 | new EqualsTester() |
| 136 | .addEqualityGroup( |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 137 | makeSchemalessInfoWithF1F2Values(provider1, 4, 5), |
| 138 | makeSchemafulInfoWithF1F2Values(provider1, 4, 5), |
| 139 | makeInvertedSchemafulInfoWithF1F2Values(provider1, 4, 5)) |
Benjamin Peterson | 8610d97 | 2017-11-27 03:14:43 -0800 | [diff] [blame] | 140 | .addEqualityGroup( |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 141 | makeSchemalessInfoWithF1F2Values(provider2, 4, 5), |
| 142 | makeSchemafulInfoWithF1F2Values(provider2, 4, 5), |
| 143 | makeInvertedSchemafulInfoWithF1F2Values(provider2, 4, 5)) |
Benjamin Peterson | 8610d97 | 2017-11-27 03:14:43 -0800 | [diff] [blame] | 144 | .testEquals(); |
| 145 | } |
| 146 | |
| 147 | @Test |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 148 | public void equality_DifferentValues() throws Exception { |
brandjon | b178e89 | 2018-01-12 16:17:00 -0800 | [diff] [blame] | 149 | SkylarkProvider provider = makeProvider(); |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 150 | // 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 Peterson | 8610d97 | 2017-11-27 03:14:43 -0800 | [diff] [blame] | 166 | } |
| 167 | |
| 168 | @Test |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 169 | public void concatWithDifferentProvidersFails() throws Exception { |
brandjon | b178e89 | 2018-01-12 16:17:00 -0800 | [diff] [blame] | 170 | SkylarkProvider provider1 = makeProvider(); |
| 171 | SkylarkProvider provider2 = makeProvider(); |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 172 | SkylarkInfo info1 = makeSchemalessInfoWithF1F2Values(provider1, 4, 5); |
| 173 | SkylarkInfo info2 = makeSchemalessInfoWithF1F2Values(provider2, 4, 5); |
| 174 | EvalException expected = |
cushon | 978cb00 | 2018-02-24 14:05:37 -0800 | [diff] [blame] | 175 | assertThrows( |
| 176 | EvalException.class, () -> info1.getConcatter().concat(info1, info2, Location.BUILTIN)); |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 177 | assertThat(expected).hasMessageThat() |
| 178 | .contains("Cannot use '+' operator on instances of different providers"); |
| 179 | } |
| 180 | |
| 181 | @Test |
| 182 | public void concatWithOverlappingFieldsFails() throws Exception { |
brandjon | b178e89 | 2018-01-12 16:17:00 -0800 | [diff] [blame] | 183 | SkylarkProvider provider1 = makeProvider(); |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 184 | SkylarkInfo info1 = makeSchemalessInfoWithF1F2Values(provider1, 4, 5); |
| 185 | SkylarkInfo info2 = makeSchemalessInfoWithF1F2Values(provider1, 4, null); |
| 186 | EvalException expected = |
cushon | 978cb00 | 2018-02-24 14:05:37 -0800 | [diff] [blame] | 187 | assertThrows( |
| 188 | EvalException.class, () -> info1.getConcatter().concat(info1, info2, Location.BUILTIN)); |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 189 | 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 { |
brandjon | b178e89 | 2018-01-12 16:17:00 -0800 | [diff] [blame] | 195 | 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); |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 199 | assertThat(result.isCompact()).isTrue(); |
Googler | 98eab10 | 2018-10-11 12:28:22 -0700 | [diff] [blame] | 200 | assertThat(result.getFieldNames()).containsExactly("f1", "f2"); |
| 201 | assertThat(result.getValue("f1")).isEqualTo(4); |
| 202 | assertThat(result.getValue("f2")).isEqualTo(5); |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 203 | } |
| 204 | |
| 205 | @Test |
| 206 | public void compactConcatWithDifferentLayoutsReturnsMap() throws Exception { |
brandjon | b178e89 | 2018-01-12 16:17:00 -0800 | [diff] [blame] | 207 | SkylarkProvider provider = makeProvider(); |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 208 | 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(); |
Googler | 98eab10 | 2018-10-11 12:28:22 -0700 | [diff] [blame] | 212 | assertThat(result.getFieldNames()).containsExactly("f1", "f2"); |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 213 | assertThat(result.getValue("f1")).isEqualTo(4); |
| 214 | assertThat(result.getValue("f2")).isEqualTo(5); |
| 215 | } |
| 216 | |
| 217 | @Test |
| 218 | public void allOtherConcatReturnsMap() throws Exception { |
brandjon | b178e89 | 2018-01-12 16:17:00 -0800 | [diff] [blame] | 219 | 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); |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 223 | assertThat(result.isCompact()).isFalse(); |
Googler | 98eab10 | 2018-10-11 12:28:22 -0700 | [diff] [blame] | 224 | assertThat(result.getFieldNames()).containsExactly("f1", "f2"); |
| 225 | assertThat(result.getValue("f1")).isEqualTo(4); |
| 226 | assertThat(result.getValue("f2")).isEqualTo(5); |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 227 | } |
| 228 | |
brandjon | b178e89 | 2018-01-12 16:17:00 -0800 | [diff] [blame] | 229 | /** 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); |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 239 | } |
| 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) { |
brandjon | b178e89 | 2018-01-12 16:17:00 -0800 | [diff] [blame] | 263 | return SkylarkInfo.createSchemaful( |
| 264 | provider, layoutF1F2, new Object[]{v1, v2}, Location.BUILTIN); |
brandjon | 04b5ab2 | 2018-01-11 09:09:56 -0800 | [diff] [blame] | 265 | } |
| 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) { |
brandjon | b178e89 | 2018-01-12 16:17:00 -0800 | [diff] [blame] | 273 | return SkylarkInfo.createSchemaful( |
| 274 | provider, invertedLayoutF2F1, new Object[]{v2, v1}, Location.BUILTIN); |
Benjamin Peterson | 8610d97 | 2017-11-27 03:14:43 -0800 | [diff] [blame] | 275 | } |
| 276 | } |