Open source the aspects-related tests.

--
MOS_MIGRATED_REVID=91907246
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/AspectDefinitionTest.java b/src/test/java/com/google/devtools/build/lib/analysis/AspectDefinitionTest.java
new file mode 100644
index 0000000..b4b055a
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/analysis/AspectDefinitionTest.java
@@ -0,0 +1,79 @@
+// Copyright 2015 Google Inc. 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.devtools.build.lib.packages.Attribute.attr;
+import static org.junit.Assert.fail;
+
+import com.google.devtools.build.lib.packages.AspectDefinition;
+import com.google.devtools.build.lib.packages.Type;
+import com.google.devtools.build.lib.syntax.Label;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for aspect definitions.
+ */
+@RunWith(JUnit4.class)
+public class AspectDefinitionTest {
+  /**
+   * A dummy aspect factory. Is there to demonstrate how to define aspects and so that we can test
+   * {@code attributeAspect}.
+   */
+  public static final class TestAspectFactory implements ConfiguredAspectFactory {
+    private final AspectDefinition definition;
+
+    /**
+     * Normal aspects will have an argumentless constructor and their definition will be hard-wired
+     * as a static member. This one is different so that we can create the definition in a test
+     * method.
+     */
+    private TestAspectFactory(AspectDefinition definition) {
+      this.definition = definition;
+    }
+
+    @Override
+    public Aspect create(ConfiguredTarget base, RuleContext context) {
+      throw new IllegalStateException();
+    }
+
+    @Override
+    public AspectDefinition getDefinition() {
+      return definition;
+    }
+  }
+
+  @Test
+  public void testSimpleAspect() throws Exception {
+    new AspectDefinition.Builder("simple")
+        .add(attr("$runtime", Type.LABEL).value(Label.parseAbsoluteUnchecked("//run:time")))
+        .attributeAspect("deps", TestAspectFactory.class)
+        .build();
+  }
+
+  @Test
+  public void testAspectWithUserVisibleAttribute() throws Exception {
+    try {
+      new AspectDefinition.Builder("user_visible_attribute")
+          .add(attr("invalid", Type.LABEL).value(Label.parseAbsoluteUnchecked("//run:time")))
+          .attributeAspect("deps", TestAspectFactory.class)
+          .build();
+      fail(); // expected IllegalStateException
+    } catch (IllegalStateException e) {
+      // expected
+    }
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/AspectTest.java b/src/test/java/com/google/devtools/build/lib/analysis/AspectTest.java
new file mode 100644
index 0000000..c8fcf96
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/analysis/AspectTest.java
@@ -0,0 +1,127 @@
+// Copyright 2015 Google Inc. 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 com.google.devtools.build.lib.analysis.util.AnalysisTestCase;
+import com.google.devtools.build.lib.analysis.util.TestAspects;
+import com.google.devtools.build.lib.analysis.util.TestAspects.AspectRequiringRule;
+import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for aspect creation and merging with configured targets.
+ *
+ * <p>Uses the complete analysis machinery and depends on custom rules so that behaviors related to
+ * aspects can be tested even if they aren't used by regular rules.
+ */
+@RunWith(JUnit4.class)
+public class AspectTest extends AnalysisTestCase {
+  @Override
+  @Before
+  public void setUp() throws Exception {
+    super.setUp();
+  }
+
+  @Override
+  @After
+  public void tearDown() throws Exception {
+    super.tearDown();
+  }
+
+  @SafeVarargs
+  private final void setRules(RuleDefinition... rules) throws Exception {
+    ConfiguredRuleClassProvider.Builder builder =
+        new ConfiguredRuleClassProvider.Builder();
+    TestRuleClassProvider.addStandardRules(builder);
+    for (RuleDefinition rule : rules) {
+      builder.addRuleDefinition(rule);
+    }
+
+    useRuleClassProvider(builder.build());
+    update();
+  }
+
+  private void pkg(String name, String... contents) throws Exception {
+    scratchFile("" + name + "/BUILD", contents);
+  }
+
+  @Test
+  public void providersOfAspectAreMergedIntoDependency() throws Exception {
+    setRules(new TestAspects.BaseRule(), new AspectRequiringRule());
+    pkg("a",
+        "aspect(name='a', foo=[':b'])",
+        "aspect(name='b', foo=[])");
+
+    ConfiguredTarget a = getConfiguredTarget("//a:a");
+    assertThat(a.getProvider(TestAspects.RuleInfo.class).getData())
+        .containsExactly("aspect //a:b", "rule //a:a");
+  }
+
+  @Test
+  public void aspectIsNotCreatedIfAdvertisedProviderIsNotPresent() throws Exception {
+    setRules(new TestAspects.BaseRule(), new TestAspects.LiarRule(),
+        new TestAspects.AspectRequiringProviderRule());
+
+    pkg("a",
+        "aspect_requiring_provider(name='a', foo=[':b'])",
+        "liar(name='b', foo=[])");
+
+    ConfiguredTarget a = getConfiguredTarget("//a:a");
+    assertThat(a.getProvider(TestAspects.RuleInfo.class).getData())
+        .containsExactly("rule //a:a");
+  }
+
+  @Test
+  public void aspectCreatedIfAdvertisedProviderIsPresent() throws Exception {
+    setRules(new TestAspects.BaseRule(), new TestAspects.HonestRule(),
+        new TestAspects.AspectRequiringProviderRule());
+
+    pkg("a",
+        "aspect_requiring_provider(name='a', foo=[':b'])",
+        "honest(name='b', foo=[])");
+
+    ConfiguredTarget a = getConfiguredTarget("//a:a");
+    assertThat(a.getProvider(TestAspects.RuleInfo.class).getData())
+        .containsExactly("rule //a:a", "aspect //a:b");
+  }
+
+  @Test
+  public void aspectInError() throws Exception {
+    setRules(new TestAspects.BaseRule(), new TestAspects.ErrorAspectRule(),
+        new TestAspects.SimpleRule());
+
+    pkg("a",
+        "simple(name='a', foo=[':b'])",
+        "error_aspect(name='b', foo=[':c'])",
+        "simple(name='c')");
+
+    reporter.removeHandler(failFastHandler);
+    // getConfiguredTarget() uses a separate code path that does not hit
+    // SkyframeBuildView#configureTargets
+    try {
+      update("//a:a");
+      fail();
+    } catch (ViewCreationFailedException e) {
+      // expected
+    }
+    assertContainsEvent("Aspect error");
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/AspectValueTest.java b/src/test/java/com/google/devtools/build/lib/analysis/AspectValueTest.java
new file mode 100644
index 0000000..5365728
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/analysis/AspectValueTest.java
@@ -0,0 +1,69 @@
+// Copyright 2015 Google Inc. 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 com.google.common.testing.EqualsTester;
+import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
+import com.google.devtools.build.lib.analysis.util.AnalysisTestCase;
+import com.google.devtools.build.lib.analysis.util.TestAspects;
+import com.google.devtools.build.lib.skyframe.AspectValue;
+import com.google.devtools.build.lib.syntax.Label;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for {@link com.google.devtools.build.lib.skyframe.AspectValue}.
+ */
+@RunWith(JUnit4.class)
+public class AspectValueTest extends AnalysisTestCase {
+  @Override
+  @Before
+  public void setUp() throws Exception {
+    super.setUp();
+  }
+
+  @Override
+  @After
+  public void tearDown() throws Exception {
+    super.tearDown();
+  }
+
+  @Test
+  public void equality() throws Exception {
+    update();
+    BuildConfiguration c1 = getTargetConfiguration();
+    BuildConfiguration c2 = getHostConfiguration();
+    Label l1 = Label.parseAbsolute("//a:l1");
+    Label l1b = Label.parseAbsolute("//a:l1");
+    Label l2 = Label.parseAbsolute("//a:l2");
+    Class<? extends ConfiguredAspectFactory> a1 = TestAspects.AttributeAspect.class;
+    Class<? extends ConfiguredAspectFactory> a2 = TestAspects.ExtraAttributeAspect.class;
+
+    new EqualsTester()
+        .addEqualityGroup(AspectValue.key(l1, c1, a1), AspectValue.key(l1b, c1, a1))
+        .addEqualityGroup(AspectValue.key(l2, c1, a1))
+        .addEqualityGroup(AspectValue.key(l1, c2, a1))
+        .addEqualityGroup(AspectValue.key(l2, c2, a1))
+        .addEqualityGroup(AspectValue.key(l1, c1, a2))
+        .addEqualityGroup(AspectValue.key(l2, c1, a2))
+        .addEqualityGroup(AspectValue.key(l1, c2, a2))
+        .addEqualityGroup(AspectValue.key(l2, c2, a2))
+        .addEqualityGroup(l1)  // A random object
+        .testEquals();
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestCase.java b/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestCase.java
index 4ebd024..54f2593 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestCase.java
@@ -139,6 +139,7 @@
 
     mockToolsConfig = new MockToolsConfig(rootDirectory);
     mock.setupMockClient(mockToolsConfig);
+    mock.setupMockWorkspaceFiles(directories.getEmbeddedBinariesRoot());
     configurationFactory = mock.createConfigurationFactory();
 
     useRuleClassProvider(TestRuleClassProvider.getRuleClassProvider());
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/TestAspects.java b/src/test/java/com/google/devtools/build/lib/analysis/util/TestAspects.java
new file mode 100644
index 0000000..4d0fa69
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/TestAspects.java
@@ -0,0 +1,418 @@
+// Copyright 2015 Google Inc. 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.util;
+
+import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST;
+import static com.google.devtools.build.lib.packages.Attribute.attr;
+import static com.google.devtools.build.lib.packages.Type.BOOLEAN;
+import static com.google.devtools.build.lib.packages.Type.LABEL;
+import static com.google.devtools.build.lib.packages.Type.LABEL_LIST;
+import static com.google.devtools.build.lib.packages.Type.NODEP_LABEL_LIST;
+import static com.google.devtools.build.lib.packages.Type.STRING;
+import static com.google.devtools.build.lib.packages.Type.STRING_LIST;
+
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.Aspect;
+import com.google.devtools.build.lib.analysis.ConfiguredAspectFactory;
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
+import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
+import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.analysis.RuleDefinition;
+import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
+import com.google.devtools.build.lib.analysis.Runfiles;
+import com.google.devtools.build.lib.analysis.RunfilesProvider;
+import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
+import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
+import com.google.devtools.build.lib.collect.nestedset.Order;
+import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.packages.AspectDefinition;
+import com.google.devtools.build.lib.packages.RuleClass;
+import com.google.devtools.build.lib.packages.RuleClass.Builder;
+import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory;
+import com.google.devtools.build.lib.syntax.Label;
+import com.google.devtools.build.lib.util.FileTypeSet;
+
+/**
+ * Various rule and aspect classes that aid in testing the aspect machinery.
+ *
+ * <p>These are mostly used in {@link com.google.devtools.build.lib.analysis.DependencyResolverTest}
+ * and {@link com.google.devtools.build.lib.analysis.AspectTest}.
+ */
+public class TestAspects {
+  /**
+   * A transitive info provider for collecting aspects in the transitive closure. Created by
+   * aspects.
+   */
+  @Immutable
+  public static final class AspectInfo implements TransitiveInfoProvider {
+    private final NestedSet<String> data;
+
+    public AspectInfo(NestedSet<String> data) {
+      this.data = data;
+    }
+
+    public NestedSet<String> getData() {
+      return data;
+    }
+  }
+
+  /**
+   * A transitive info provider for collecting aspects in the transitive closure. Created by
+   * rules.
+   */
+  @Immutable
+  public static final class RuleInfo implements TransitiveInfoProvider {
+    private final NestedSet<String> data;
+
+    public RuleInfo(NestedSet<String> data) {
+      this.data = data;
+    }
+
+    public NestedSet<String> getData() {
+      return data;
+    }
+  }
+
+  /**
+   * A very simple provider used in tests that check whether the logic that attaches aspects
+   * depending on whether a configured target has a provider works or not.
+   */
+  @Immutable
+  public static final class RequiredProvider implements TransitiveInfoProvider {
+  }
+
+  private static NestedSet<String> collectAspectData(String me, RuleContext ruleContext) {
+    NestedSetBuilder<String> result = new NestedSetBuilder<>(Order.STABLE_ORDER);
+    result.add(me);
+    for (AspectInfo dep : ruleContext.getPrerequisites("foo", Mode.TARGET, AspectInfo.class)) {
+      result.addTransitive(dep.getData());
+    }
+
+    return result.build();
+  }
+
+  /**
+   * A simple rule configured target factory that is used in all the mock rules in this class.
+   */
+  public static class DummyRuleFactory implements RuleConfiguredTargetFactory {
+    @Override
+    public ConfiguredTarget create(RuleContext ruleContext) throws InterruptedException {
+
+      RuleConfiguredTargetBuilder builder = new RuleConfiguredTargetBuilder(ruleContext)
+          .addProvider(RuleInfo.class,
+              new RuleInfo(collectAspectData("rule " + ruleContext.getLabel(), ruleContext)))
+          .setFilesToBuild(NestedSetBuilder.<Artifact>create(Order.STABLE_ORDER))
+          .setRunfilesSupport(null, null)
+          .add(RunfilesProvider.class, RunfilesProvider.simple(Runfiles.EMPTY));
+
+      if (ruleContext.getRule().getRuleClassObject().getName().equals("honest")) {
+        builder.addProvider(RequiredProvider.class, new RequiredProvider());
+      }
+
+      return builder.build();
+    }
+  }
+
+  /**
+   * A base class for mock aspects to reduce boilerplate.
+   */
+  public abstract static class BaseAspect implements ConfiguredAspectFactory {
+    @Override
+    public Aspect create(ConfiguredTarget base, RuleContext ruleContext) {
+      return new Aspect.Builder()
+          .addProvider(AspectInfo.class,
+              new AspectInfo(collectAspectData("aspect " + ruleContext.getLabel(), ruleContext)))
+          .build();
+    }
+  }
+
+  private static final AspectDefinition SIMPLE_ASPECT =
+      new AspectDefinition.Builder("simple").build();
+
+  /**
+   * A very simple aspect.
+   */
+  public static class SimpleAspect extends BaseAspect {
+    @Override
+    public AspectDefinition getDefinition() {
+      return SIMPLE_ASPECT;
+    }
+  }
+
+  private static final AspectDefinition EXTRA_ATTRIBUTE_ASPECT =
+      new AspectDefinition.Builder("extra_attribute")
+          .add(attr("$dep", LABEL).value(Label.parseAbsoluteUnchecked("//extra:extra")))
+          .build();
+
+  private static final AspectDefinition EXTRA_ATTRIBUTE_ASPECT_REQUIRING_PROVIDER =
+      new AspectDefinition.Builder("extra_attribute_with_provider")
+          .add(attr("$dep", LABEL).value(Label.parseAbsoluteUnchecked("//extra:extra")))
+          .requireProvider(RequiredProvider.class)
+          .build();
+
+  /**
+   * An aspect that defines its own implicit attribute.
+   */
+  public static class ExtraAttributeAspect extends BaseAspect {
+    @Override
+    public AspectDefinition getDefinition() {
+      return EXTRA_ATTRIBUTE_ASPECT;
+    }
+  }
+
+  private static final AspectDefinition ATTRIBUTE_ASPECT = new AspectDefinition.Builder("attribute")
+      .attributeAspect("foo", AttributeAspect.class)
+      .build();
+
+  /**
+   * An aspect that requires aspects on the attributes of rules it attaches to.
+   */
+  public static class AttributeAspect extends BaseAspect {
+    @Override
+    public AspectDefinition getDefinition() {
+      return ATTRIBUTE_ASPECT;
+    }
+  }
+
+  /**
+   * An aspect that defines its own implicit attribute and requires provider.
+   */
+  public static class ExtraAttributeAspectRequiringProvider extends BaseAspect {
+    @Override
+    public AspectDefinition getDefinition() {
+      return EXTRA_ATTRIBUTE_ASPECT_REQUIRING_PROVIDER;
+    }
+  }
+
+  public static class AspectRequiringProvider extends BaseAspect {
+    @Override
+    public AspectDefinition getDefinition() {
+      return ASPECT_REQUIRING_PROVIDER;
+    }
+  }
+
+  private static final AspectDefinition ASPECT_REQUIRING_PROVIDER =
+      new AspectDefinition.Builder("requiring_provider")
+          .requireProvider(RequiredProvider.class)
+          .build();
+
+  /**
+   * An aspect that raises an error.
+   */
+  public static class ErrorAspect implements ConfiguredAspectFactory {
+    @Override
+    public Aspect create(ConfiguredTarget base, RuleContext ruleContext) {
+      ruleContext.ruleError("Aspect error");
+      return null;
+    }
+
+    @Override
+    public AspectDefinition getDefinition() {
+      return ERROR_ASPECT;
+    }
+  }
+
+  private static final AspectDefinition ERROR_ASPECT = new AspectDefinition.Builder("error")
+      .build();
+
+  /**
+   * A common base rule for mock rules in this class to reduce boilerplate.
+   *
+   * <p>It has a few common attributes because internal Blaze machinery assumes the presence of
+   * these.
+   */
+  public static class BaseRule implements RuleDefinition {
+    @Override
+    public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) {
+      return builder
+          .add(attr("testonly", BOOLEAN).nonconfigurable("test").value(false))
+          .add(attr("deprecation", STRING).nonconfigurable("test"))
+          .add(attr("tags", STRING_LIST))
+          .add(attr("visibility", NODEP_LABEL_LIST).orderIndependent().cfg(HOST)
+              .nonconfigurable("test"))
+          .add(attr(RuleClass.COMPATIBLE_ENVIRONMENT_ATTR, LABEL_LIST)
+              .allowedFileTypes(FileTypeSet.NO_FILE))
+          .add(attr(RuleClass.RESTRICTED_ENVIRONMENT_ATTR, LABEL_LIST)
+              .allowedFileTypes(FileTypeSet.NO_FILE))
+          .build();
+
+    }
+
+    @Override
+    public Metadata getMetadata() {
+      return RuleDefinition.Metadata.builder()
+          .name("base")
+          .factoryClass(DummyRuleFactory.class)
+          .build();
+    }
+  }
+
+  /**
+   * A rule that defines an aspect on one of its attributes.
+   */
+  public static class AspectRequiringRule implements RuleDefinition {
+    @Override
+    public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) {
+      return builder
+          .add(attr("foo", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE)
+              .aspect(SimpleAspect.class))
+          .build();
+
+    }
+
+    @Override
+    public Metadata getMetadata() {
+      return RuleDefinition.Metadata.builder()
+          .name("aspect")
+          .factoryClass(DummyRuleFactory.class)
+          .ancestors(BaseRule.class)
+          .build();
+    }
+  }
+
+  /**
+   * A rule that defines an {@link AspectRequiringProvider} on one of its attributes.
+   */
+  public static class AspectRequiringProviderRule implements RuleDefinition {
+    @Override
+    public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) {
+      return builder
+          .add(attr("foo", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE)
+              .aspect(AspectRequiringProvider.class))
+          .build();
+
+    }
+
+    @Override
+    public Metadata getMetadata() {
+      return RuleDefinition.Metadata.builder()
+          .name("aspect_requiring_provider")
+          .factoryClass(DummyRuleFactory.class)
+          .ancestors(BaseRule.class)
+          .build();
+    }
+  }
+
+  /**
+   * A rule that defines an {@link ExtraAttributeAspectRequiringProvider} on one of its attributes.
+   */
+  public static class ExtraAttributeAspectRequiringProviderRule implements RuleDefinition {
+    @Override
+    public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) {
+      return builder
+          .add(attr("foo", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE)
+              .aspect(ExtraAttributeAspectRequiringProvider.class))
+          .build();
+
+    }
+
+    @Override
+    public Metadata getMetadata() {
+      return RuleDefinition.Metadata.builder()
+          .name("extra_attribute_aspect_requiring_provider")
+          .factoryClass(DummyRuleFactory.class)
+          .ancestors(BaseRule.class)
+          .build();
+    }
+  }
+
+  /**
+   * A rule that defines an {@link AspectRequiringProvider} on one of its attributes.
+   */
+  public static class ErrorAspectRule implements RuleDefinition {
+    @Override
+    public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) {
+      return builder
+          .add(attr("foo", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE)
+              .aspect(ErrorAspect.class))
+          .build();
+    }
+
+    @Override
+    public Metadata getMetadata() {
+      return RuleDefinition.Metadata.builder()
+          .name("error_aspect")
+          .factoryClass(DummyRuleFactory.class)
+          .ancestors(BaseRule.class)
+          .build();
+    }
+  }
+
+  /**
+   * A simple rule that has an attribute.
+   */
+  public static class SimpleRule implements RuleDefinition {
+    @Override
+    public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) {
+      return builder
+          .add(attr("foo", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE))
+          .build();
+    }
+
+    @Override
+    public Metadata getMetadata() {
+      return RuleDefinition.Metadata.builder()
+          .name("simple")
+          .factoryClass(DummyRuleFactory.class)
+          .ancestors(BaseRule.class)
+          .build();
+    }
+  }
+
+  /**
+   * A rule that advertises a provider but doesn't implement it.
+   */
+  public static class LiarRule implements RuleDefinition {
+    @Override
+    public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) {
+      return builder
+          .add(attr("foo", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE))
+          .advertiseProvider(RequiredProvider.class)
+          .build();
+    }
+
+    @Override
+    public Metadata getMetadata() {
+      return RuleDefinition.Metadata.builder()
+          .name("liar")
+          .factoryClass(DummyRuleFactory.class)
+          .ancestors(BaseRule.class)
+          .build();
+    }
+  }
+
+  /**
+   * A rule that advertises a provider and implements it.
+   */
+  public static class HonestRule implements RuleDefinition {
+    @Override
+    public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) {
+      return builder
+          .add(attr("foo", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE))
+          .advertiseProvider(RequiredProvider.class)
+          .build();
+    }
+
+    @Override
+    public Metadata getMetadata() {
+      return RuleDefinition.Metadata.builder()
+          .name("honest")
+          .factoryClass(DummyRuleFactory.class)
+          .ancestors(BaseRule.class)
+          .build();
+    }
+  }
+}