Open source 'CircularDependencyTest'.

--
MOS_MIGRATED_REVID=108514346
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/CircularDependencyTest.java b/src/test/java/com/google/devtools/build/lib/analysis/CircularDependencyTest.java
new file mode 100644
index 0000000..18481c5
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/analysis/CircularDependencyTest.java
@@ -0,0 +1,175 @@
+// Copyright 2014 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 com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
+import com.google.devtools.build.lib.events.Event;
+import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.packages.NoSuchTargetException;
+import com.google.devtools.build.lib.packages.Package;
+
+/**
+ * Tests that check that dependency cycles are reported correctly.
+ */
+public class CircularDependencyTest extends BuildViewTestCase {
+  @Override
+  public void setUp() throws Exception {
+    super.setUp();
+  }
+
+  public void testOneRuleCycle() throws Exception {
+    checkError(
+        "cycle",
+        "foo.g",
+        //error message
+        selfEdgeMsg("//cycle:foo.g"),
+        // Rule
+        "genrule(name = 'foo.g',",
+        "        outs = ['Foo.java'],",
+        "        srcs = ['foo.g'],",
+        "        cmd = 'cat $(SRCS) > $<' )");
+  }
+
+  public void testDirectPackageGroupCycle() throws Exception {
+    checkError(
+        "cycle",
+        "melon",
+        selfEdgeMsg("//cycle:moebius"),
+        "package_group(name='moebius', packages=[], includes=['//cycle:moebius'])",
+        "sh_library(name='melon', visibility=[':moebius'])");
+  }
+
+  public void testThreeLongPackageGroupCycle() throws Exception {
+    String expectedEvent =
+        "cycle in dependency graph:\n"
+            + "    //cycle:superman\n"
+            + "  * //cycle:rock\n"
+            + "    //cycle:paper\n"
+            + "    //cycle:scissors";
+    checkError(
+        "cycle",
+        "superman",
+        expectedEvent,
+        "# dummy line",
+        "package_group(name='paper', includes=['//cycle:scissors'])",
+        "package_group(name='rock', includes=['//cycle:paper'])",
+        "package_group(name='scissors', includes=['//cycle:rock'])",
+        "sh_library(name='superman', visibility=[':rock'])");
+
+    Event foundEvent = null;
+    for (Event event : eventCollector) {
+      if (event.getMessage().contains(expectedEvent)) {
+        foundEvent = event;
+        break;
+      }
+    }
+
+    assertNotNull(foundEvent);
+    Location location = foundEvent.getLocation();
+    assertEquals(3, location.getStartLineAndColumn().getLine());
+    assertEquals("/workspace/cycle/BUILD", location.getPath().toString());
+  }
+
+  /**
+   * Test to detect implicit input/output file overlap in rules.
+   */
+  public void testOneRuleImplicitCycleJava() throws Exception {
+    Package pkg =
+        createScratchPackageForImplicitCycle(
+            "cycle", "java_library(name='jcyc',", "      srcs = ['libjcyc.jar', 'foo.java'])");
+    try {
+      pkg.getTarget("jcyc");
+      fail();
+    } catch (NoSuchTargetException e) {
+      /* ok */
+    }
+    assertTrue(pkg.containsErrors());
+    assertContainsEvent("rule 'jcyc' has file 'libjcyc.jar' as both an" + " input and an output");
+  }
+
+  /**
+   * Test not to detect implicit input/output file overlap in rules,
+   * when coming from a different package.
+   */
+  public void testInputOutputConflictDifferentPackage() throws Exception {
+    Package pkg =
+        createScratchPackageForImplicitCycle(
+            "googledata/xxx",
+            "genrule(name='geo',",
+            "    srcs = ['//googledata/geo:geo_info.txt'],",
+            "    outs = ['geoinfo.txt'],",
+            "    cmd = '$(SRCS) > $@')");
+    assertFalse(pkg.containsErrors());
+  }
+
+  public void testTwoRuleCycle() throws Exception {
+    scratchRule("b", "rule2", "cc_library(name='rule2',", "           deps=['//a:rule1'])");
+
+    checkError(
+        "a",
+        "rule1",
+        "in cc_library rule //a:rule1: cycle in dependency graph:\n"
+            + "  * //a:rule1\n"
+            + "    //b:rule2",
+        "cc_library(name='rule1',",
+        "           deps=['//b:rule2'])");
+  }
+
+  public void testTwoRuleCycle2() throws Exception {
+    reporter.removeHandler(failFastHandler); // expect errors
+    scratch.file(
+        "x/BUILD", "java_library(name='x', deps=['y'])", "java_library(name='y', deps=['x'])");
+    getConfiguredTarget("//x");
+    assertContainsEvent("in java_library rule //x:x: cycle in dependency graph");
+  }
+
+  public void testIndirectOneRuleCycle() throws Exception {
+    scratchRule(
+        "cycle",
+        "foo.h",
+        "genrule(name = 'foo.h',",
+        "      outs = ['bar.h'],",
+        "      srcs = ['foo.h'],",
+        "      cmd = 'cp $< $@')");
+    checkError(
+        "main",
+        "mygenrule",
+        //error message
+        selfEdgeMsg("//cycle:foo.h"),
+        // Rule
+        "genrule(name='mygenrule',",
+        "      outs = ['baz.h'],",
+        "      srcs = ['//cycle:foo.h'],",
+        "      cmd = 'cp $< $@')");
+  }
+
+  private String selfEdgeMsg(String label) {
+    return label + " [self-edge]";
+  }
+
+  // Regression test for: "IllegalStateException in
+  // AbstractConfiguredTarget.initialize()".
+  // Failure to mark all cycle-forming nodes when there are *two* cycles led to
+  // an attempt to initialise a node we'd already visited.
+  public void testTwoCycles() throws Exception {
+    reporter.removeHandler(failFastHandler); // expect errors
+    scratch.file(
+        "x/BUILD",
+        "genrule(name='b', srcs=['c'], tools=['c'], outs=['b.out'], cmd=':')",
+        "genrule(name='c', srcs=['b.out'], outs=[], cmd=':')");
+    getConfiguredTarget("//x:b"); // doesn't crash!
+    assertContainsEvent("in genrule rule //x:b: cycle in dependency graph");
+  }
+}