aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/test')
-rw-r--r--src/test/java/com/google/devtools/build/lib/analysis/AspectCollectionTest.java452
1 files changed, 452 insertions, 0 deletions
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/AspectCollectionTest.java b/src/test/java/com/google/devtools/build/lib/analysis/AspectCollectionTest.java
new file mode 100644
index 0000000000..2175ae5fec
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/analysis/AspectCollectionTest.java
@@ -0,0 +1,452 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.analysis;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.analysis.AspectCollection.AspectCycleOnPathException;
+import com.google.devtools.build.lib.analysis.AspectCollection.AspectDeps;
+import com.google.devtools.build.lib.packages.Aspect;
+import com.google.devtools.build.lib.packages.AspectDefinition;
+import com.google.devtools.build.lib.packages.AspectDescriptor;
+import com.google.devtools.build.lib.packages.AspectParameters;
+import com.google.devtools.build.lib.packages.NativeAspectClass;
+import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier;
+import com.google.devtools.build.lib.util.Pair;
+import java.util.HashMap;
+import java.util.HashSet;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for {@link AspectCollection}
+ */
+@RunWith(JUnit4.class)
+public class AspectCollectionTest {
+
+ private static final Function<Aspect, AspectDescriptor> ASPECT_TO_DESCRIPTOR =
+ new Function<Aspect, AspectDescriptor>() {
+ @Override
+ public AspectDescriptor apply(Aspect aspect) {
+ return aspect.getDescriptor();
+ }
+ };
+
+ private static final Function<AspectDeps, AspectDescriptor> ASPECT_PATH_TO_DESCRIPTOR =
+ new Function<AspectDeps, AspectDescriptor>() {
+ @Override
+ public AspectDescriptor apply(AspectDeps aspectPath) {
+ return aspectPath.getAspect();
+ }
+ };
+
+ /**
+ * a3 wants a1 and a2, a1 and a2 want no one, path is a1, a2, a3.
+ */
+ @Test
+ public void linearAspectPath1() throws Exception {
+ Aspect a1 = createAspect("a1");
+ Aspect a2 = createAspect("a2");
+ Aspect a3 = createAspect("a3", "a1", "a2");
+ AspectCollection collection = AspectCollection
+ .create(ImmutableList.of(a1, a2, a3), ImmutableSet.of(a3.getDescriptor()));
+ validateAspectCollection(
+ collection,
+ ImmutableList.of(a1, a2, a3),
+ ImmutableList.of(a3),
+ expectDeps(a3, a1, a2),
+ expectDeps(a1),
+ expectDeps(a2)
+ );
+ }
+
+ /**
+ * a3 wants a2, a2 wants a1, a1 wants no one, path is a1, a2, a3.
+ */
+ @Test
+ public void linearAspectPath2() throws Exception {
+ Aspect a1 = createAspect("a1");
+ Aspect a2 = createAspect("a2", "a1");
+ Aspect a3 = createAspect("a3", "a2");
+ AspectCollection collection = AspectCollection
+ .create(ImmutableList.of(a1, a2, a3), ImmutableSet.of(a3.getDescriptor()));
+ validateAspectCollection(
+ collection,
+ ImmutableList.of(a1, a2, a3),
+ ImmutableList.of(a3),
+ expectDeps(a3, a2),
+ expectDeps(a2, a1),
+ expectDeps(a1)
+ );
+ }
+
+ /**
+ * a3 wants a1, a1 wants a2, path is a1, a2, a3, so a2 comes after a1.
+ */
+ @Test
+ public void validateOrder() throws Exception {
+ Aspect a1 = createAspect("a1", "a2");
+ Aspect a2 = createAspect("a2");
+ Aspect a3 = createAspect("a3", "a1");
+ AspectCollection collection = AspectCollection
+ .create(ImmutableList.of(a1, a2, a3), ImmutableSet.of(a3.getDescriptor()));
+ validateAspectCollection(
+ collection,
+ ImmutableList.of(a1, a3),
+ ImmutableList.of(a3),
+ expectDeps(a3, a1),
+ expectDeps(a1)
+ );
+ }
+
+ /**
+ * a3 wants a1, a1 wants a2, a2 wants a1, path is a1, a2, a3, so a2 comes after a1.
+ */
+ @Test
+ public void validateOrder2() throws Exception {
+ Aspect a1 = createAspect("a1", "a2");
+ Aspect a2 = createAspect("a2", "a1");
+ Aspect a3 = createAspect("a3", "a1");
+ AspectCollection collection = AspectCollection
+ .create(ImmutableList.of(a1, a2, a3), ImmutableSet.of(a3.getDescriptor()));
+ validateAspectCollection(
+ collection,
+ ImmutableList.of(a1, a3),
+ ImmutableList.of(a3),
+ expectDeps(a3, a1),
+ expectDeps(a1)
+ );
+ }
+
+ /**
+ * a3 wants no one => a1 and a2 must be removed.
+ */
+ @Test
+ public void unneededRemoved() throws Exception {
+ Aspect a1 = createAspect("a1");
+ Aspect a2 = createAspect("a2");
+ Aspect a3 = createAspect("a3");
+ AspectCollection collection = AspectCollection
+ .create(ImmutableList.of(a1, a2, a3), ImmutableSet.of(a3.getDescriptor()));
+ validateAspectCollection(
+ collection,
+ ImmutableList.of(a3),
+ ImmutableList.of(a3),
+ expectDeps(a3)
+ );
+ }
+
+ /**
+ * a3 wants itself.
+ */
+ @Test
+ public void recursive() throws Exception {
+ Aspect a1 = createAspect("a1");
+ Aspect a2 = createAspect("a2");
+ Aspect a3 = createAspect("a3", "a3");
+ AspectCollection collection = AspectCollection
+ .create(ImmutableList.of(a1, a2, a3), ImmutableSet.of(a3.getDescriptor()));
+ validateAspectCollection(
+ collection,
+ ImmutableList.of(a3),
+ ImmutableList.of(a3),
+ expectDeps(a3)
+ );
+ }
+
+ /**
+ * a2 (non-visible aspect) wants itself, a3 wants a2.
+ */
+ @Test
+ public void recursiveNonVisible() throws Exception{
+ Aspect a1 = createAspect("a1");
+ Aspect a2 = createAspect("a2", "a2");
+ Aspect a3 = createAspect("a3", "a2");
+ AspectCollection collection = AspectCollection
+ .create(ImmutableList.of(a1, a2, a3), ImmutableSet.of(a3.getDescriptor()));
+ validateAspectCollection(
+ collection,
+ ImmutableList.of(a2, a3),
+ ImmutableList.of(a3),
+ expectDeps(a3, a2),
+ expectDeps(a2)
+ );
+ }
+
+ /**
+ * Both a2 and a3 are visible, a2 wants a1, a3 wants nothing.
+ */
+ @Test
+ public void twoVisibleAspects() throws Exception {
+ Aspect a1 = createAspect("a1");
+ Aspect a2 = createAspect("a2", "a1");
+ Aspect a3 = createAspect("a3");
+ AspectCollection collection = AspectCollection
+ .create(
+ ImmutableList.of(a1, a2, a3),
+ ImmutableSet.of(a2.getDescriptor(), a3.getDescriptor()));
+ validateAspectCollection(
+ collection,
+ ImmutableList.of(a1, a2, a3),
+ ImmutableList.of(a2, a3),
+ expectDeps(a3),
+ expectDeps(a2, a1),
+ expectDeps(a1)
+ );
+ }
+
+ /**
+ * a2 wants a1, a3 wants a1 and a2, the path is [a2, a1, a2, a3], so a2 occurs twice.
+ *
+ * First occurrence of a2 would not see a1, but the second would: that is an error.
+ */
+ @Test
+ public void duplicateAspect() throws Exception {
+ Aspect a1 = createAspect("a1");
+ Aspect a2 = createAspect("a2", "a1");
+ Aspect a3 = createAspect("a3", "a2", "a1");
+ try {
+ AspectCollection
+ .create(
+ ImmutableList.of(a2, a1, a2, a3),
+ ImmutableSet.of(a3.getDescriptor()));
+ Assert.fail();
+ } catch (AspectCycleOnPathException e) {
+ assertThat(e.getAspect()).isEqualTo(a2.getDescriptor());
+ assertThat(e.getPreviousAspect()).isEqualTo(a1.getDescriptor());
+ }
+ }
+
+ /**
+ * a2 wants a1, a3 wants a2, the path is [a2, a1, a2, a3], so a2 occurs twice.
+ *
+ * First occurrence of a2 would not see a1, but the second would: that is an error.
+ */
+ @Test
+ public void duplicateAspect2() throws Exception {
+ Aspect a1 = createAspect("a1");
+ Aspect a2 = createAspect("a2", "a1");
+ Aspect a3 = createAspect("a3", "a2");
+ try {
+ AspectCollection
+ .create(
+ ImmutableList.of(a2, a1, a2, a3),
+ ImmutableSet.of(a3.getDescriptor()));
+ Assert.fail();
+ } catch (AspectCycleOnPathException e) {
+ assertThat(e.getAspect()).isEqualTo(a2.getDescriptor());
+ assertThat(e.getPreviousAspect()).isEqualTo(a1.getDescriptor());
+ }
+ }
+
+ /**
+ * a3 wants a1 and a2, a2 does not want a1.
+ * The path is [a2, a1, a2, a3], so a2 occurs twice.
+ * Second occurrence of a2 is consistent with the first.
+ */
+ @Test
+ public void duplicateAspect2a() throws Exception {
+ Aspect a1 = createAspect("a1");
+ Aspect a2 = createAspect("a2");
+ Aspect a3 = createAspect("a3", "a1", "a2");
+
+ AspectCollection collection = AspectCollection.create(
+ ImmutableList.of(a2, a1, a2, a3),
+ ImmutableSet.of(a3.getDescriptor())
+ );
+
+ validateAspectCollection(
+ collection,
+ ImmutableList.of(a2, a1, a3),
+ ImmutableList.of(a3),
+ expectDeps(a3, a2, a1),
+ expectDeps(a2),
+ expectDeps(a1)
+ );
+ }
+
+
+ /**
+ * a2 wants a1, a3 wants a1 and a2, a1 wants a2. the path is [a2, a1, a2, a3], so a2 occurs twice.
+ * First occurrence of a2 does not see a1, but the second does => error.
+ */
+ @Test
+ public void duplicateAspect3() throws Exception {
+ Aspect a1 = createAspect("a1", "a2");
+ Aspect a2 = createAspect("a2", "a1");
+ Aspect a3 = createAspect("a3", "a1", "a2");
+ try {
+ AspectCollection
+ .create(
+ ImmutableList.of(a2, a1, a2, a3),
+ ImmutableSet.of(a3.getDescriptor()));
+ Assert.fail();
+ } catch (AspectCycleOnPathException e) {
+ assertThat(e.getAspect()).isEqualTo(a2.getDescriptor());
+ assertThat(e.getPreviousAspect()).isEqualTo(a1.getDescriptor());
+ }
+ }
+
+ /**
+ * a2 wants a1, a3 wants a2, a1 wants a2. the path is [a2, a1, a2, a3], so a2 occurs twice.
+ * First occurrence of a2 does not see a1, but the second does => error.
+ * a1 disappears.
+ */
+ @Test
+ public void duplicateAspect4() throws Exception {
+ Aspect a1 = createAspect("a1", "a2");
+ Aspect a2 = createAspect("a2", "a1");
+ Aspect a3 = createAspect("a3", "a2");
+ try {
+ AspectCollection
+ .create(
+ ImmutableList.of(a2, a1, a2, a3),
+ ImmutableSet.of(a3.getDescriptor()));
+ Assert.fail();
+ } catch (AspectCycleOnPathException e) {
+ assertThat(e.getAspect()).isEqualTo(a2.getDescriptor());
+ assertThat(e.getPreviousAspect()).isEqualTo(a1.getDescriptor());
+ }
+ }
+
+ /**
+ * a2 and a3 are visible.
+ * a3 wants a2, a1 wants a2. The path is [a2, a1, a2, a3], so a2 occurs twice.
+ * First occurrence of a2 is consistent with the second.
+ * a1 disappears.
+ */
+ @Test
+ public void duplicateAspectVisible() throws Exception {
+ Aspect a1 = createAspect("a1", "a2");
+ Aspect a2 = createAspect("a2");
+ Aspect a3 = createAspect("a3", "a2");
+ AspectCollection collection = AspectCollection
+ .create(
+ ImmutableList.of(a2, a1, a2, a3),
+ ImmutableSet.of(a2.getDescriptor(), a3.getDescriptor()));
+ validateAspectCollection(
+ collection,
+ ImmutableList.of(a2, a3),
+ ImmutableList.of(a2, a3),
+ expectDeps(a3, a2),
+ expectDeps(a2)
+ );
+ }
+
+
+ private static Pair<Aspect, ImmutableList<Aspect>> expectDeps(Aspect a, Aspect... deps) {
+ return Pair.of(a, ImmutableList.copyOf(deps));
+ }
+
+ @SafeVarargs
+ private static void validateAspectCollection(AspectCollection collection,
+ ImmutableList<Aspect> allAspects,
+ ImmutableList<Aspect> visibleAspects,
+ Pair<Aspect, ImmutableList<Aspect>>... expectedPaths) {
+
+ assertThat(collection.getAllAspects())
+ .containsExactlyElementsIn(Iterables.transform(allAspects, ASPECT_TO_DESCRIPTOR))
+ .inOrder();
+ assertThat(Iterables.transform(collection.getVisibleAspects(), ASPECT_PATH_TO_DESCRIPTOR))
+ .containsExactlyElementsIn(Iterables.transform(visibleAspects, ASPECT_TO_DESCRIPTOR))
+ .inOrder();
+ validateAspectPaths(
+ collection,
+ ImmutableList.copyOf(expectedPaths)
+ );
+ }
+
+ private static void validateAspectPaths(AspectCollection collection,
+ ImmutableList<Pair<Aspect, ImmutableList<Aspect>>> expectedList) {
+ HashMap<AspectDescriptor, AspectDeps> allPaths = new HashMap<>();
+ for (AspectDeps aspectPath : collection.getVisibleAspects()) {
+ collectAndValidateAspectDeps(aspectPath, allPaths);
+ }
+
+ HashSet<AspectDescriptor> expectedKeys = new HashSet<>();
+
+ for (Pair<Aspect, ImmutableList<Aspect>> expected : expectedList) {
+ assertThat(allPaths).containsKey(expected.first.getDescriptor());
+ AspectDeps aspectPath = allPaths.get(expected.first.getDescriptor());
+ assertThat(Iterables.transform(aspectPath.getDependentAspects(), ASPECT_PATH_TO_DESCRIPTOR))
+ .containsExactlyElementsIn(Iterables.transform(expected.second, ASPECT_TO_DESCRIPTOR))
+ .inOrder();
+ expectedKeys.add(expected.first.getDescriptor());
+ }
+ assertThat(allPaths.keySet())
+ .containsExactlyElementsIn(expectedKeys);
+ }
+
+ /**
+ * Collects all aspect paths transitively visible from {@code aspectDeps}.
+ * Validates that {@link AspectDeps} instance corresponding to a given {@link AspectDescriptor}
+ * is unique.
+ */
+ private static void collectAndValidateAspectDeps(AspectDeps aspectDeps,
+ HashMap<AspectDescriptor, AspectDeps> allDeps) {
+ if (allDeps.containsKey(aspectDeps.getAspect())) {
+ assertWithMessage(
+ String.format("Two different deps for aspect %s", aspectDeps.getAspect()))
+ .that(allDeps.get(aspectDeps.getAspect()))
+ .isSameAs(aspectDeps);
+ return;
+ }
+ allDeps.put(aspectDeps.getAspect(), aspectDeps);
+ for (AspectDeps path : aspectDeps.getDependentAspects()) {
+ collectAndValidateAspectDeps(path, allDeps);
+ }
+ }
+
+
+ /**
+ * Creates an aspect wiht a class named {@code className} advertizing a provider
+ * {@code className} that requires any of providers {@code requiredAspects}.
+ */
+ private Aspect createAspect(final String className, String... requiredAspects) {
+ ImmutableList.Builder<ImmutableSet<SkylarkProviderIdentifier>> requiredProvidersBuilder =
+ ImmutableList.builder();
+
+ for (String requiredAspect : requiredAspects) {
+ requiredProvidersBuilder.add(
+ ImmutableSet.of((SkylarkProviderIdentifier.forLegacy(requiredAspect))));
+ }
+ final ImmutableList<ImmutableSet<SkylarkProviderIdentifier>> requiredProviders =
+ requiredProvidersBuilder.build();
+ return Aspect.forNative(
+ new NativeAspectClass() {
+ @Override
+ public String getName() {
+ return className;
+ }
+
+ @Override
+ public AspectDefinition getDefinition(AspectParameters aspectParameters) {
+ return AspectDefinition.builder(this)
+ .requireAspectsWithProviders(requiredProviders)
+ .advertiseProvider(ImmutableList.of(SkylarkProviderIdentifier.forLegacy(className)))
+ .build();
+ }
+ }
+ );
+ }
+
+
+}