Add unit tests for AndroidStudioInfoAspect and implement dependency collection.

--
MOS_MIGRATED_REVID=102976551
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/Aspect.java b/src/main/java/com/google/devtools/build/lib/analysis/Aspect.java
index 939e3ea..091fa4a 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/Aspect.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/Aspect.java
@@ -14,6 +14,7 @@
 
 package com.google.devtools.build.lib.analysis;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.UnmodifiableIterator;
@@ -68,7 +69,8 @@
 
 
   @Nullable
-  <P extends TransitiveInfoProvider> P getProvider(Class<P> providerClass) {
+  @VisibleForTesting
+  public <P extends TransitiveInfoProvider> P getProvider(Class<P> providerClass) {
     AnalysisUtils.checkProvider(providerClass);
 
     return providerClass.cast(providers.get(providerClass));
diff --git a/src/main/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoAspect.java b/src/main/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoAspect.java
index 202d1da..fa26e5c 100644
--- a/src/main/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoAspect.java
+++ b/src/main/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoAspect.java
@@ -14,6 +14,9 @@
 
 package com.google.devtools.build.lib.ideinfo;
 
+import static com.google.common.collect.Iterables.transform;
+
+import com.google.common.base.Function;
 import com.google.common.collect.ImmutableList;
 import com.google.common.io.ByteSource;
 import com.google.devtools.build.lib.actions.ActionOwner;
@@ -25,6 +28,7 @@
 import com.google.devtools.build.lib.analysis.ConfiguredTarget;
 import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
 import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
 import com.google.devtools.build.lib.analysis.actions.BinaryFileWriteAction;
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
@@ -33,13 +37,16 @@
 import com.google.devtools.build.lib.ideinfo.androidstudio.AndroidStudioIdeInfo.JavaRuleIdeInfo;
 import com.google.devtools.build.lib.ideinfo.androidstudio.AndroidStudioIdeInfo.LibraryArtifact;
 import com.google.devtools.build.lib.ideinfo.androidstudio.AndroidStudioIdeInfo.RuleIdeInfo;
+import com.google.devtools.build.lib.ideinfo.androidstudio.AndroidStudioIdeInfo.RuleIdeInfo.Kind;
 import com.google.devtools.build.lib.packages.AspectDefinition;
 import com.google.devtools.build.lib.packages.AspectParameters;
 import com.google.devtools.build.lib.packages.Rule;
 import com.google.devtools.build.lib.packages.Type;
 import com.google.devtools.build.lib.rules.android.AndroidSdkProvider;
+import com.google.devtools.build.lib.rules.java.JavaExportsProvider;
 import com.google.devtools.build.lib.rules.java.JavaRuleOutputJarsProvider;
 import com.google.devtools.build.lib.rules.java.JavaSourceInfoProvider;
+import com.google.devtools.build.lib.syntax.Label;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.protobuf.MessageLite;
@@ -47,6 +54,9 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Collection;
+import java.util.List;
+
+import javax.annotation.Nullable;
 
 /**
  * Generates ide-build information for Android Studio.
@@ -60,6 +70,13 @@
 
   // File suffixes.
   public static final String ASWB_BUILD_SUFFIX = ".aswb-build";
+  public static final Function<Label, String> LABEL_TO_STRING = new Function<Label, String>() {
+    @Nullable
+    @Override
+    public String apply(Label label) {
+      return label.toString();
+    }
+  };
 
   @Override
   public AspectDefinition getDefinition() {
@@ -74,19 +91,46 @@
       AspectParameters parameters) {
     Aspect.Builder builder = new Builder(NAME);
 
-    // Collect ide build files.
+    // Collect ide build files and calculate dependencies.
+    NestedSetBuilder<Label> transitiveDependenciesBuilder = NestedSetBuilder.stableOrder();
+    NestedSetBuilder<Label> dependenciesBuilder = NestedSetBuilder.stableOrder();
+
     NestedSetBuilder<Artifact> ideBuildFilesBuilder = NestedSetBuilder.stableOrder();
+
+    // todo(dslomov,tomlu): following current build info logic, this code enumerates dependencies
+    // directly by iterating over deps attribute. The more robust way to do this might be
+    // to iterate classpath as provided to build action.
     if (ruleContext.attributes().has("deps", Type.LABEL_LIST)) {
-      Iterable<AndroidStudioInfoFilesProvider> deps =
+      Iterable<AndroidStudioInfoFilesProvider> androidStudioInfoFilesProviders =
           ruleContext.getPrerequisites("deps", Mode.TARGET, AndroidStudioInfoFilesProvider.class);
-      for (AndroidStudioInfoFilesProvider dep : deps) {
-        ideBuildFilesBuilder.addTransitive(dep.getIdeBuildFiles());
+      for (AndroidStudioInfoFilesProvider depProvider : androidStudioInfoFilesProviders) {
+        ideBuildFilesBuilder.addTransitive(depProvider.getIdeBuildFiles());
+        transitiveDependenciesBuilder.addTransitive(depProvider.getTransitiveDependencies());
+      }
+      List<? extends TransitiveInfoCollection> deps =
+          ruleContext.getPrerequisites("deps", Mode.TARGET);
+      for (TransitiveInfoCollection dep : deps) {
+        dependenciesBuilder.add(dep.getLabel());
+      }
+
+      Iterable<JavaExportsProvider> javaExportsProviders = ruleContext
+          .getPrerequisites("deps", Mode.TARGET, JavaExportsProvider.class);
+      for (JavaExportsProvider javaExportsProvider : javaExportsProviders) {
+        dependenciesBuilder.addTransitive(javaExportsProvider.getTransitiveExports());
       }
     }
 
+    NestedSet<Label> directDependencies = dependenciesBuilder.build();
+    transitiveDependenciesBuilder.addTransitive(directDependencies);
+    NestedSet<Label> transitiveDependencies = transitiveDependenciesBuilder.build();
+
     RuleIdeInfo.Kind ruleKind = getRuleKind(ruleContext.getRule(), base);
+
     if (ruleKind != RuleIdeInfo.Kind.UNRECOGNIZED) {
-      Artifact ideBuildFile = createIdeBuildArtifact(base, ruleContext, ruleKind);
+      Artifact ideBuildFile =
+          createIdeBuildArtifact(base, ruleContext, ruleKind,
+              directDependencies,
+              transitiveDependencies);
       ideBuildFilesBuilder.add(ideBuildFile);
     }
 
@@ -95,7 +139,7 @@
         .addOutputGroup(IDE_BUILD, ideBuildFiles)
         .addProvider(
             AndroidStudioInfoFilesProvider.class,
-            new AndroidStudioInfoFilesProvider(ideBuildFiles));
+            new AndroidStudioInfoFilesProvider(ideBuildFiles, transitiveDependencies));
 
     return builder.build();
   }
@@ -117,7 +161,10 @@
   }
 
   private Artifact createIdeBuildArtifact(
-      ConfiguredTarget base, RuleContext ruleContext, RuleIdeInfo.Kind ruleKind) {
+      ConfiguredTarget base,
+      RuleContext ruleContext,
+      Kind ruleKind,
+      NestedSet<Label> directDependencies, NestedSet<Label> transitiveDependencies) {
     PathFragment ideBuildFilePath = getOutputFilePath(base, ruleContext);
     Root genfilesDirectory = ruleContext.getConfiguration().getGenfilesDirectory();
     Artifact ideBuildFile =
@@ -139,16 +186,21 @@
 
     outputBuilder.setKind(ruleKind);
 
-    if (ruleKind == RuleIdeInfo.Kind.JAVA_LIBRARY) {
+    outputBuilder.addAllDependencies(transform(directDependencies, LABEL_TO_STRING));
+    outputBuilder.addAllTransitiveDependencies(transform(transitiveDependencies, LABEL_TO_STRING));
+
+    if (ruleKind == Kind.JAVA_LIBRARY) {
       outputBuilder.setJavaRuleIdeInfo(makeJavaRuleIdeInfo(base));
-    } else if (ruleKind == RuleIdeInfo.Kind.ANDROID_SDK) {
+    } else if (ruleKind == Kind.ANDROID_SDK) {
       outputBuilder.setAndroidSdkRuleInfo(
           makeAndroidSdkRuleInfo(ruleContext, base.getProvider(AndroidSdkProvider.class)));
     }
 
+
     final RuleIdeInfo ruleIdeInfo = outputBuilder.build();
     ruleContext.registerAction(
         makeProtoWriteAction(ruleContext.getActionOwner(), ruleIdeInfo, ideBuildFile));
+
     return ideBuildFile;
   }
 
@@ -209,13 +261,8 @@
         }
       }
     }
+    Collection<Artifact> sourceFiles = getSources(base);
 
-    // Calculate source files.
-    JavaSourceInfoProvider sourceInfoProvider = base.getProvider(JavaSourceInfoProvider.class);
-    Collection<Artifact> sourceFiles =
-        sourceInfoProvider != null
-            ? sourceInfoProvider.getSourceFiles()
-            : ImmutableList.<Artifact>of();
     for (Artifact sourceFile : sourceFiles) {
       builder.addSources(makeArtifactLocation(sourceFile));
     }
@@ -223,6 +270,14 @@
     return builder.build();
   }
 
+  private static Collection<Artifact> getSources(ConfiguredTarget base) {
+    // Calculate source files.
+    JavaSourceInfoProvider sourceInfoProvider = base.getProvider(JavaSourceInfoProvider.class);
+    return sourceInfoProvider != null
+        ? sourceInfoProvider.getSourceFiles()
+        : ImmutableList.<Artifact>of();
+  }
+
   private PathFragment getOutputFilePath(ConfiguredTarget base, RuleContext ruleContext) {
     PathFragment packagePathFragment =
         ruleContext.getLabel().getPackageIdentifier().getPathFragment();
diff --git a/src/main/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoFilesProvider.java b/src/main/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoFilesProvider.java
index 5795ae5..95faf9e 100644
--- a/src/main/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoFilesProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoFilesProvider.java
@@ -18,6 +18,7 @@
 import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.syntax.Label;
 
 /**
  * File provider for Android Studio ide build files.
@@ -25,12 +26,19 @@
 @Immutable
 public final class AndroidStudioInfoFilesProvider implements TransitiveInfoProvider {
   private final NestedSet<Artifact> ideBuildFiles;
+  private final NestedSet<Label> transitiveDependencies;
 
-  public AndroidStudioInfoFilesProvider(NestedSet<Artifact> ideBuildFiles) {
+  public AndroidStudioInfoFilesProvider(
+      NestedSet<Artifact> ideBuildFiles, NestedSet<Label> transitiveDependencies) {
     this.ideBuildFiles = ideBuildFiles;
+    this.transitiveDependencies = transitiveDependencies;
   }
 
   public NestedSet<Artifact> getIdeBuildFiles() {
     return ideBuildFiles;
   }
+
+  public NestedSet<Label> getTransitiveDependencies() {
+    return transitiveDependencies;
+  }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
index 02e1f8b..754500c 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
@@ -739,8 +739,8 @@
     eventCollector.clear();
     ConfiguredTarget target = scratchConfiguredTarget(packageName, ruleName,
                                                      lines);
-    assertFalse("Rule '" + "//" + packageName + ":" + ruleName
-        + "' did contain an error",
+    assertFalse(
+        "Rule '" + "//" + packageName + ":" + ruleName + "' did contain an error",
         view.hasErrors(target));
     return assertContainsEvent(expectedWarningMessage);
   }
@@ -1273,6 +1273,18 @@
       int loadingPhaseThreads,
       boolean doAnalysis,
       EventBus eventBus) throws Exception {
+    return update(
+        targets, ImmutableList.<String>of(), keepGoing, loadingPhaseThreads, doAnalysis, eventBus);
+  }
+
+  protected AnalysisResult update(
+      List<String> targets,
+      List<String> aspects,
+      boolean keepGoing,
+      int loadingPhaseThreads,
+      boolean doAnalysis,
+      EventBus eventBus)
+      throws Exception {
 
     LoadingPhaseRunner.Options loadingOptions =
         Options.getDefaults(LoadingPhaseRunner.Options.class);
@@ -1293,7 +1305,7 @@
     return view.update(
         loadingResult,
         masterConfig,
-        ImmutableList.<String>of(),
+        aspects,
         viewOptions,
         AnalysisTestUtil.TOP_LEVEL_ARTIFACT_CONTEXT,
         reporter,
diff --git a/src/test/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoAspectTest.java b/src/test/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoAspectTest.java
new file mode 100644
index 0000000..3268bf7
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoAspectTest.java
@@ -0,0 +1,303 @@
+// Copyright 2014 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.ideinfo;
+
+import static com.google.common.collect.Iterables.transform;
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.eventbus.EventBus;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.BuildView.AnalysisResult;
+import com.google.devtools.build.lib.analysis.actions.BinaryFileWriteAction;
+import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
+import com.google.devtools.build.lib.ideinfo.androidstudio.AndroidStudioIdeInfo.ArtifactLocation;
+import com.google.devtools.build.lib.ideinfo.androidstudio.AndroidStudioIdeInfo.RuleIdeInfo;
+import com.google.devtools.build.lib.skyframe.AspectValue;
+
+import java.util.Collection;
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+/**
+ * Tests for {@link AndroidStudioInfoAspect} validating proto's contents.
+ */
+public class AndroidStudioInfoAspectTest extends BuildViewTestCase {
+
+  public static final Function<ArtifactLocation, String> ARTIFACT_TO_RELATIVE_PATH =
+      new Function<ArtifactLocation, String>() {
+        @Nullable
+        @Override
+        public String apply(ArtifactLocation artifactLocation) {
+          return artifactLocation.getRelativePath();
+        }
+      };
+
+  public void testSimpleJavaLibrary() throws Exception {
+    scratch.file(
+        "com/google/example/BUILD",
+        "java_library(",
+        "    name = \"simple\",",
+        "    srcs = [\"simple/Simple.java\"]",
+        ")",
+        "java_library(",
+        "    name = \"complex\",",
+        "    srcs = [\"complex/Complex.java\"],",
+        "    deps = [\":simple\"]",
+        ")");
+    String target = "//com/google/example:simple";
+    Map<String, RuleIdeInfo> ruleIdeInfos = buildRuleIdeInfo(target);
+    assertThat(ruleIdeInfos.size()).isEqualTo(1);
+    RuleIdeInfo ruleIdeInfo = getRuleInfoAndVerifyLabel(target, ruleIdeInfos);
+    assertThat(ruleIdeInfo.getDependenciesCount()).isEqualTo(0);
+    assertThat(relativePathsForSourcesOf(ruleIdeInfo))
+        .containsExactly("com/google/example/simple/Simple.java");
+  }
+
+  private static Iterable<String> relativePathsForSourcesOf(RuleIdeInfo ruleIdeInfo) {
+    return transform(ruleIdeInfo.getJavaRuleIdeInfo().getSourcesList(), ARTIFACT_TO_RELATIVE_PATH);
+  }
+
+  private RuleIdeInfo getRuleInfoAndVerifyLabel(
+      String target, Map<String, RuleIdeInfo> ruleIdeInfos) {
+    RuleIdeInfo ruleIdeInfo = ruleIdeInfos.get(target);
+    assertThat(ruleIdeInfo.getLabel()).isEqualTo(target);
+    return ruleIdeInfo;
+  }
+
+  public void testJavaLibraryProtoWithDependencies() throws Exception {
+    scratch.file(
+        "com/google/example/BUILD",
+        "java_library(",
+        "    name = \"simple\",",
+        "    srcs = [\"simple/Simple.java\"]",
+        ")",
+        "java_library(",
+        "    name = \"complex\",",
+        "    srcs = [\"complex/Complex.java\"],",
+        "    deps = [\":simple\"]",
+        ")");
+    String target = "//com/google/example:complex";
+    Map<String, RuleIdeInfo> ruleIdeInfos = buildRuleIdeInfo(target);
+    assertThat(ruleIdeInfos.size()).isEqualTo(2);
+
+    getRuleInfoAndVerifyLabel("//com/google/example:simple", ruleIdeInfos);
+
+    RuleIdeInfo complexRuleIdeInfo = getRuleInfoAndVerifyLabel(target, ruleIdeInfos);
+
+    assertThat(relativePathsForSourcesOf(complexRuleIdeInfo))
+        .containsExactly("com/google/example/complex/Complex.java");
+    assertThat(complexRuleIdeInfo.getDependenciesList())
+        .containsExactly("//com/google/example:simple");
+
+    assertThat(complexRuleIdeInfo.getTransitiveDependenciesList())
+        .containsExactly("//com/google/example:simple");
+  }
+
+  public void testJavaLibraryWithTransitiveDependencies() throws Exception {
+    scratch.file(
+        "com/google/example/BUILD",
+        "java_library(",
+        "    name = \"simple\",",
+        "    srcs = [\"simple/Simple.java\"]",
+        ")",
+        "java_library(",
+        "    name = \"complex\",",
+        "    srcs = [\"complex/Complex.java\"],",
+        "    deps = [\":simple\"]",
+        ")",
+        "java_library(",
+        "    name = \"extracomplex\",",
+        "    srcs = [\"extracomplex/ExtraComplex.java\"],",
+        "    deps = [\":complex\"]",
+        ")");
+    String target = "//com/google/example:extracomplex";
+    Map<String, RuleIdeInfo> ruleIdeInfos = buildRuleIdeInfo(target);
+    assertThat(ruleIdeInfos.size()).isEqualTo(3);
+
+    getRuleInfoAndVerifyLabel("//com/google/example:simple", ruleIdeInfos);
+    getRuleInfoAndVerifyLabel("//com/google/example:complex", ruleIdeInfos);
+
+    RuleIdeInfo extraComplexRuleIdeInfo = getRuleInfoAndVerifyLabel(target, ruleIdeInfos);
+
+    assertThat(relativePathsForSourcesOf(extraComplexRuleIdeInfo))
+        .containsExactly("com/google/example/extracomplex/ExtraComplex.java");
+    assertThat(extraComplexRuleIdeInfo.getDependenciesList())
+        .containsExactly("//com/google/example:complex");
+
+    assertThat(extraComplexRuleIdeInfo.getTransitiveDependenciesList())
+        .containsExactly("//com/google/example:complex", "//com/google/example:simple");
+  }
+
+  public void testJavaLibraryWithDiamondDependencies() throws Exception {
+    scratch.file(
+        "com/google/example/BUILD",
+        "java_library(",
+        "    name = \"simple\",",
+        "    srcs = [\"simple/Simple.java\"]",
+        ")",
+        "java_library(",
+        "    name = \"complex\",",
+        "    srcs = [\"complex/Complex.java\"],",
+        "    deps = [\":simple\"]",
+        ")",
+        "java_library(",
+        "    name = \"complex1\",",
+        "    srcs = [\"complex1/Complex.java\"],",
+        "    deps = [\":simple\"]",
+        ")",
+        "java_library(",
+        "    name = \"extracomplex\",",
+        "    srcs = [\"extracomplex/ExtraComplex.java\"],",
+        "    deps = [\":complex\", \":complex1\"]",
+        ")");
+    String target = "//com/google/example:extracomplex";
+    Map<String, RuleIdeInfo> ruleIdeInfos = buildRuleIdeInfo(target);
+    assertThat(ruleIdeInfos.size()).isEqualTo(4);
+
+    getRuleInfoAndVerifyLabel("//com/google/example:simple", ruleIdeInfos);
+    getRuleInfoAndVerifyLabel("//com/google/example:complex", ruleIdeInfos);
+    getRuleInfoAndVerifyLabel("//com/google/example:complex1", ruleIdeInfos);
+
+    RuleIdeInfo extraComplexRuleIdeInfo = getRuleInfoAndVerifyLabel(target, ruleIdeInfos);
+
+    assertThat(relativePathsForSourcesOf(extraComplexRuleIdeInfo))
+        .containsExactly("com/google/example/extracomplex/ExtraComplex.java");
+    assertThat(extraComplexRuleIdeInfo.getDependenciesList())
+        .containsExactly("//com/google/example:complex", "//com/google/example:complex1");
+
+    assertThat(extraComplexRuleIdeInfo.getTransitiveDependenciesList())
+        .containsExactly(
+            "//com/google/example:complex",
+            "//com/google/example:complex1",
+            "//com/google/example:simple");
+  }
+
+  public void testJavaLibraryWithExports() throws Exception {
+    scratch.file(
+        "com/google/example/BUILD",
+        "java_library(",
+        "    name = \"simple\",",
+        "    srcs = [\"simple/Simple.java\"]",
+        ")",
+        "java_library(",
+        "    name = \"complex\",",
+        "    srcs = [\"complex/Complex.java\"],",
+        "    deps = [\":simple\"],",
+        "    exports = [\":simple\"],",
+        ")",
+        "java_library(",
+        "    name = \"extracomplex\",",
+        "    srcs = [\"extracomplex/ExtraComplex.java\"],",
+        "    deps = [\":complex\"]",
+        ")");
+    String target = "//com/google/example:extracomplex";
+    Map<String, RuleIdeInfo> ruleIdeInfos = buildRuleIdeInfo(target);
+    assertThat(ruleIdeInfos.size()).isEqualTo(3);
+
+    getRuleInfoAndVerifyLabel("//com/google/example:simple", ruleIdeInfos);
+    getRuleInfoAndVerifyLabel("//com/google/example:complex", ruleIdeInfos);
+
+    RuleIdeInfo extraComplexRuleIdeInfo = getRuleInfoAndVerifyLabel(target, ruleIdeInfos);
+
+    assertThat(relativePathsForSourcesOf(extraComplexRuleIdeInfo))
+        .containsExactly("com/google/example/extracomplex/ExtraComplex.java");
+    assertThat(extraComplexRuleIdeInfo.getDependenciesList())
+        .containsExactly("//com/google/example:complex", "//com/google/example:simple");
+
+    assertThat(extraComplexRuleIdeInfo.getTransitiveDependenciesList())
+        .containsExactly(
+            "//com/google/example:complex",
+            "//com/google/example:simple");
+  }
+
+  public void testJavaLibraryWithTransitiveExports() throws Exception {
+    scratch.file(
+        "com/google/example/BUILD",
+        "java_library(",
+        "    name = \"simple\",",
+        "    srcs = [\"simple/Simple.java\"]",
+        ")",
+        "java_library(",
+        "    name = \"complex\",",
+        "    srcs = [\"complex/Complex.java\"],",
+        "    deps = [\":simple\"],",
+        "    exports = [\":simple\"],",
+        ")",
+        "java_library(",
+        "    name = \"extracomplex\",",
+        "    srcs = [\"extracomplex/ExtraComplex.java\"],",
+        "    deps = [\":complex\"],",
+        "    exports = [\":complex\"],",
+        ")",
+        "java_library(",
+        "    name = \"megacomplex\",",
+        "    srcs = [\"megacomplex/MegaComplex.java\"],",
+        "    deps = [\":extracomplex\"],",
+        ")"
+    );
+    String target = "//com/google/example:megacomplex";
+    Map<String, RuleIdeInfo> ruleIdeInfos = buildRuleIdeInfo(target);
+    assertThat(ruleIdeInfos.size()).isEqualTo(4);
+
+    getRuleInfoAndVerifyLabel("//com/google/example:simple", ruleIdeInfos);
+    getRuleInfoAndVerifyLabel("//com/google/example:complex", ruleIdeInfos);
+    getRuleInfoAndVerifyLabel("//com/google/example:extracomplex", ruleIdeInfos);
+
+    RuleIdeInfo megaComplexRuleIdeInfo = getRuleInfoAndVerifyLabel(target, ruleIdeInfos);
+
+    assertThat(relativePathsForSourcesOf(megaComplexRuleIdeInfo))
+        .containsExactly("com/google/example/megacomplex/MegaComplex.java");
+    assertThat(megaComplexRuleIdeInfo.getDependenciesList())
+        .containsExactly(
+            "//com/google/example:extracomplex",
+            "//com/google/example:complex",
+            "//com/google/example:simple");
+
+    assertThat(megaComplexRuleIdeInfo.getTransitiveDependenciesList())
+        .containsExactly(
+            "//com/google/example:extracomplex",
+            "//com/google/example:complex",
+            "//com/google/example:simple");
+  }
+
+  private Map<String, RuleIdeInfo> buildRuleIdeInfo(String target) throws Exception {
+    AnalysisResult analysisResult =
+        update(
+            ImmutableList.of(target),
+            ImmutableList.of(AndroidStudioInfoAspect.NAME),
+            false,
+            LOADING_PHASE_THREADS,
+            true,
+            new EventBus());
+    Collection<AspectValue> aspects = analysisResult.getAspects();
+    assertThat(aspects.size()).isEqualTo(1);
+    AspectValue value = aspects.iterator().next();
+    assertThat(value.getAspect().getName()).isEqualTo(AndroidStudioInfoAspect.NAME);
+    AndroidStudioInfoFilesProvider provider =
+        value.getAspect().getProvider(AndroidStudioInfoFilesProvider.class);
+    Iterable<Artifact> artifacts = provider.getIdeBuildFiles();
+    ImmutableMap.Builder<String, RuleIdeInfo> builder = ImmutableMap.builder();
+    for (Artifact artifact : artifacts) {
+      BinaryFileWriteAction generatingAction =
+          (BinaryFileWriteAction) getGeneratingAction(artifact);
+      RuleIdeInfo ruleIdeInfo = RuleIdeInfo.parseFrom(generatingAction.getSource().openStream());
+      builder.put(ruleIdeInfo.getLabel(), ruleIdeInfo);
+    }
+    return builder.build();
+  }
+}