Open source the TargetPatternEvaluator tests.

--
MOS_MIGRATED_REVID=109581214
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD
index 19dc976..bd2120d 100644
--- a/src/test/java/com/google/devtools/build/lib/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/BUILD
@@ -586,6 +586,28 @@
 )
 
 java_test(
+    name = "pkgcache_test",
+    srcs = glob(
+        [
+            "pkgcache/*.java",
+        ],
+    ),
+    args = ["com.google.devtools.build.lib.AllTests"],
+    deps = [
+        ":packages_testutil",
+        ":test_runner",
+        "//src/main/java/com/google/devtools/build/lib:bazel-rules",
+        "//src/main/java/com/google/devtools/build/lib:build-base",
+        "//src/main/java/com/google/devtools/build/lib:packages",
+        "//src/main/java/com/google/devtools/build/skyframe",
+        "//third_party:guava",
+        "//third_party:jsr305",
+        "//third_party:junit4",
+        "//third_party:truth",
+    ],
+)
+
+java_test(
     name = "ideinfo_test",
     srcs = glob([
         "ideinfo/*.java",
diff --git a/src/test/java/com/google/devtools/build/lib/pkgcache/AbstractTargetPatternEvaluatorTest.java b/src/test/java/com/google/devtools/build/lib/pkgcache/AbstractTargetPatternEvaluatorTest.java
new file mode 100644
index 0000000..766b85b
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/pkgcache/AbstractTargetPatternEvaluatorTest.java
@@ -0,0 +1,122 @@
+// Copyright 2015 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.pkgcache;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
+import com.google.devtools.build.lib.cmdline.ResolvedTargets;
+import com.google.devtools.build.lib.cmdline.TargetParsingException;
+import com.google.devtools.build.lib.events.DelegatingEventHandler;
+import com.google.devtools.build.lib.events.EventHandler;
+import com.google.devtools.build.lib.packages.ConstantRuleVisibility;
+import com.google.devtools.build.lib.packages.PackageFactory;
+import com.google.devtools.build.lib.packages.Preprocessor;
+import com.google.devtools.build.lib.packages.Target;
+import com.google.devtools.build.lib.packages.util.PackageLoadingTestCase;
+import com.google.devtools.build.lib.skyframe.SkyframeExecutor;
+import com.google.devtools.build.lib.util.Pair;
+
+import org.junit.Before;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * Abstract framework for target pattern evaluation tests. The {@link TargetPatternEvaluatorTest}
+ * contains much of the functionality that might be needed for future tests, and its methods should
+ * be extracted here if they are needed by other classes.
+ */
+public abstract class AbstractTargetPatternEvaluatorTest extends PackageLoadingTestCase {
+  protected SkyframeExecutor skyframeExecutor;
+  protected TargetPatternEvaluator parser;
+  protected RecordingParsingListener parsingListener;
+
+  protected static ResolvedTargets<Target> parseTargetPatternList(
+      TargetPatternEvaluator parser, EventHandler eventHandler,
+      List<String> targetPatterns, boolean keepGoing)
+          throws TargetParsingException, InterruptedException {
+    return parseTargetPatternList(
+        parser, eventHandler, targetPatterns, FilteringPolicies.NO_FILTER, keepGoing);
+  }
+
+  protected static ResolvedTargets<Target> parseTargetPatternList(
+      TargetPatternEvaluator parser, EventHandler eventHandler,
+      List<String> targetPatterns, FilteringPolicy policy,
+      boolean keepGoing) throws TargetParsingException, InterruptedException {
+    return parser.parseTargetPatternList(eventHandler, targetPatterns, policy, keepGoing);
+  }
+
+  /**
+   * Method converts collection of targets to the new, mutable,
+   * lexicographically-ordered set of corresponding labels.
+   */
+  protected static Set<Label> targetsToLabels(Iterable<Target> targets) {
+    Set<Label> labels = new TreeSet<>();
+    for (Target target : targets) {
+      labels.add(target.getLabel());
+    }
+    return labels;
+  }
+
+  @Before
+  public final void initializeParser() throws Exception {
+    skyframeExecutor =
+        super.createSkyframeExecutor(ImmutableList.<PackageFactory.EnvironmentExtension>of(),
+            Preprocessor.Factory.Supplier.NullSupplier.INSTANCE, ConstantRuleVisibility.PRIVATE,
+            ruleClassProvider.getDefaultsPackageContent());
+    parser = skyframeExecutor.getPackageManager().newTargetPatternEvaluator();
+    parsingListener = new RecordingParsingListener(reporter);
+  }
+
+  protected static Set<Label> labels(String... labelStrings) throws LabelSyntaxException {
+    Set<Label> labels = new HashSet<>();
+    for (String labelString : labelStrings) {
+      labels.add(Label.parseAbsolute(labelString));
+    }
+    return labels;
+  }
+
+  protected Pair<Set<Label>, Boolean> parseListKeepGoing(String... patterns)
+      throws TargetParsingException, InterruptedException {
+    ResolvedTargets<Target> result = parseTargetPatternList(parser, parsingListener,
+            Arrays.asList(patterns), true);
+    return Pair.of(targetsToLabels(result.getTargets()), result.hasError());
+  }
+
+  /** Event handler that records all parsing errors. */
+  protected static final class RecordingParsingListener extends DelegatingEventHandler
+      implements ParseFailureListener {
+    protected final List<Pair<String, String>> events = new ArrayList<>();
+
+    private RecordingParsingListener(EventHandler delegate) {
+      super(delegate);
+    }
+
+    @Override
+    public void parsingError(String targetPattern, String message) {
+      events.add(Pair.of(targetPattern, message));
+    }
+
+    protected void assertEmpty() {
+      assertThat(events).isEmpty();
+    }
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/pkgcache/TargetPatternEvaluatorIOTest.java b/src/test/java/com/google/devtools/build/lib/pkgcache/TargetPatternEvaluatorIOTest.java
new file mode 100644
index 0000000..3b63fbe
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/pkgcache/TargetPatternEvaluatorIOTest.java
@@ -0,0 +1,201 @@
+// Copyright 2015 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.pkgcache;
+
+import com.google.common.truth.Truth;
+import com.google.devtools.build.lib.util.BlazeClock;
+import com.google.devtools.build.lib.vfs.Dirent;
+import com.google.devtools.build.lib.vfs.FileStatus;
+import com.google.devtools.build.lib.vfs.FileSystem;
+import com.google.devtools.build.lib.vfs.FileSystemUtils;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryContentInfo;
+import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.annotation.Nullable;
+
+/** TargetPatternEvaluator tests that require a custom filesystem. */
+@RunWith(JUnit4.class)
+public class TargetPatternEvaluatorIOTest extends AbstractTargetPatternEvaluatorTest {
+  private static final String FS_ROOT = "/fsg";
+
+  private static class Transformer {
+    @SuppressWarnings("unused")
+    @Nullable
+    public FileStatus stat(FileStatus stat, Path path, boolean followSymlinks) throws IOException {
+      return stat;
+    }
+
+    @SuppressWarnings("unused")
+    @Nullable
+    public Collection<Dirent> readdir(Collection<Dirent> readdir, Path path, boolean followSymlinks)
+        throws IOException {
+      return readdir;
+    }
+  }
+
+  private Transformer transformer = new Transformer();
+
+  @Override
+  protected FileSystem createFileSystem() {
+    return new InMemoryFileSystem(BlazeClock.instance(), new PathFragment(FS_ROOT)) {
+      @Override
+      public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
+        FileStatus defaultResult = super.stat(path, followSymlinks);
+        return transformer.stat(defaultResult, path, followSymlinks);
+      }
+
+      @Override
+      protected Collection<Dirent> readdir(Path path, boolean followSymlinks) throws IOException {
+        Collection<Dirent> defaultResult = super.readdir(path, followSymlinks);
+        return transformer.readdir(defaultResult, path, followSymlinks);
+      }
+    };
+  }
+
+  /**
+   * Test that a child with an inconsistent stat (first a directory, then not) does not prevent
+   * evaluation of the remaining packages beneath a directory and the return of a partial result.
+   */
+  @Test
+  public void testBadStatKeepGoing() throws Exception {
+    reporter.removeHandler(failFastHandler);
+    // Given a package, "parent",
+    Path parent = scratch.file("parent/BUILD", "sh_library(name = 'parent')").getParentDirectory();
+    // And a child, "badstat",
+    FileSystemUtils.createDirectoryAndParents(parent.getRelative("badstat"));
+
+    // Such that badstat first reports that it is a directory, and then reports that it isn't,
+    this.transformer = createInconsistentFileStateTransformer("parent/badstat");
+
+    // When we find all the targets beneath parent in keep_going mode, we get the valid target
+    // parent:parent, even though processing badstat threw an InconsistentFilesystemException.
+    Truth.assertThat(parseListKeepGoing("//parent/...").getFirst())
+        .containsExactlyElementsIn(labels("//parent:parent"));
+  }
+
+  /**
+   * Test that a package subdirectory that throws an IOException when it is listed via readdir
+   * does not prevent evaluation of the remaining packages beneath a directory and the return of
+   * a partial result.
+   */
+  @Test
+  public void testBadReaddirKeepGoing() throws Exception {
+    reporter.removeHandler(failFastHandler);
+    // Given a package, "parent",
+    Path parent = scratch.file("parent/BUILD", "sh_library(name = 'parent')").getParentDirectory();
+    // And a child, "badstat",
+    FileSystemUtils.createDirectoryAndParents(parent.getRelative("badstat"));
+
+    // Such that badstat reports that it is a directory, but throws an error when its Dirents are
+    // collected,
+    this.transformer = createBadDirectoryListingTransformer("parent/badstat");
+
+    // When we find all the targets beneath parent in keep_going mode, we get the valid target
+    // parent:parent, even though processing badstat threw an InconsistentFilesystemException.
+    Truth.assertThat(parseListKeepGoing("//parent/...").getFirst())
+        .containsExactlyElementsIn(labels("//parent:parent"));
+  }
+
+  private Transformer createInconsistentFileStateTransformer(final String badPathSuffix) {
+    final AtomicBoolean isDirectory = new AtomicBoolean(true);
+    return new Transformer() {
+      @Nullable
+      @Override
+      public FileStatus stat(final FileStatus stat, Path path, boolean followSymlinks)
+          throws IOException {
+        if (path.getPathString().endsWith(badPathSuffix)) {
+          return new InMemoryContentInfo(BlazeClock.instance()) {
+            @Override
+            public boolean isDirectory() {
+              // Trigger inconsistent filesystem exception.
+              return isDirectory.getAndSet(false);
+            }
+
+            @Override
+            public boolean isFile() {
+              return stat.isFile();
+            }
+
+            @Override
+            public boolean isSpecialFile() {
+              return stat.isSpecialFile();
+            }
+
+            @Override
+            public boolean isSymbolicLink() {
+              return stat.isSymbolicLink();
+            }
+
+            @Override
+            public long getSize() throws IOException {
+              return stat.getSize();
+            }
+
+            @Override
+            public synchronized long getLastModifiedTime() {
+              try {
+                return stat.getLastModifiedTime();
+              } catch (IOException e) {
+                throw new IllegalStateException(e);
+              }
+            }
+
+            @Override
+            public synchronized long getLastChangeTime() {
+              try {
+                return stat.getLastChangeTime();
+              } catch (IOException e) {
+                throw new IllegalStateException(e);
+              }
+            }
+
+            @Override
+            public long getNodeId() {
+              try {
+                return stat.getNodeId();
+              } catch (IOException e) {
+                throw new IllegalStateException(e);
+              }
+            }
+          };
+        }
+        return stat;
+      }
+    };
+  }
+
+  private Transformer createBadDirectoryListingTransformer(final String badPathSuffix) {
+    return new Transformer() {
+      @Nullable
+      @Override
+      public Collection<Dirent> readdir(Collection<Dirent> readdir, Path path,
+          boolean followSymlinks) throws IOException {
+        if (path.getPathString().endsWith(badPathSuffix)) {
+          throw new IOException("Path ended in " + badPathSuffix + ", so readdir failed.");
+        }
+        return readdir;
+      }
+    };
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/pkgcache/TargetPatternEvaluatorTest.java b/src/test/java/com/google/devtools/build/lib/pkgcache/TargetPatternEvaluatorTest.java
new file mode 100644
index 0000000..7197650
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/pkgcache/TargetPatternEvaluatorTest.java
@@ -0,0 +1,1063 @@
+// Copyright 2015 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.pkgcache;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.devtools.build.lib.pkgcache.FilteringPolicies.FILTER_TESTS;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.cmdline.PackageIdentifier;
+import com.google.devtools.build.lib.cmdline.ResolvedTargets;
+import com.google.devtools.build.lib.cmdline.TargetParsingException;
+import com.google.devtools.build.lib.cmdline.TargetPattern;
+import com.google.devtools.build.lib.packages.ImplicitOutputsFunction;
+import com.google.devtools.build.lib.packages.Target;
+import com.google.devtools.build.lib.util.Pair;
+import com.google.devtools.build.lib.vfs.ModifiedFileSet;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Arrays;
+import java.util.Set;
+
+/** Tests for {@link TargetPatternEvaluator}. */
+@RunWith(JUnit4.class)
+public class TargetPatternEvaluatorTest extends AbstractTargetPatternEvaluatorTest {
+  private PathFragment fooOffset;
+
+  private Set<Label> rulesBeneathFoo;
+  private Set<Label> rulesBeneathFooBar;
+  private Set<Label> rulesBeneathOtherrules;
+  private Set<Label> rulesInFoo;
+  private Set<Label> rulesInFooBar;
+  private Set<Label> rulesInOtherrules;
+  private Set<Label> targetsInFoo;
+  private Set<Label> targetsInFooBar;
+  private Set<Label> targetsBeneathFoo;
+  private Set<Label> targetsInOtherrules;
+
+  @Before
+  public final void createFiles() throws Exception {
+    // TODO(ulfjack): Also disable the implicit C++ outputs in Google's internal version.
+    boolean hasImplicitCcOutputs = ruleClassProvider.getRuleClassMap().get("cc_library")
+        .getImplicitOutputsFunction() != ImplicitOutputsFunction.NONE;
+
+    scratch.file("foo/BUILD",
+        "cc_library(name = 'foo1', srcs = [ 'foo1.cc' ], hdrs = [ 'foo1.h' ])",
+        "exports_files(['baz/bang'])");
+    scratch.file("foo/bar/BUILD",
+        "cc_library(name = 'bar1', alwayslink = 1)",
+        "cc_library(name = 'bar2')",
+        "exports_files(['wiz/bang', 'wiz/all', 'baz', 'baz/bang', 'undeclared.h'])");
+
+    // 'filegroup' and 'test_suite' are rules, but 'exports_files' is not.
+    scratch.file("otherrules/BUILD",
+        "test_suite(name = 'suite1')",
+        "filegroup(name='group', srcs=['suite/somefile'])",
+        "exports_files(['suite/somefile'])",
+        "cc_library(name = 'wiz', linkstatic = 1)");
+    scratch.file("nosuchpkg/subdir/empty", "");
+
+    Path foo = scratch.dir("foo");
+    fooOffset = foo.relativeTo(rootDirectory);
+
+    rulesBeneathFoo = labels("//foo:foo1", "//foo/bar:bar1", "//foo/bar:bar2");
+    rulesBeneathFooBar = labels("//foo/bar:bar1", "//foo/bar:bar2");
+    rulesBeneathOtherrules = labels(
+        "//otherrules:suite1", "//otherrules:wiz", "//otherrules:group");
+    rulesInFoo = labels("//foo:foo1");
+    rulesInFooBar = labels("//foo/bar:bar1", "//foo/bar:bar2");
+    rulesInOtherrules = rulesBeneathOtherrules;
+
+    targetsInFoo = labels(
+        "//foo:foo1",
+        "//foo:foo1",
+        "//foo:foo1.cc",
+        "//foo:foo1.h",
+        "//foo:BUILD",
+        "//foo:baz/bang");
+    if (hasImplicitCcOutputs) {
+      targetsInFoo.addAll(labels("//foo:libfoo1.a", "//foo:libfoo1.so"));
+    }
+    targetsInFooBar = labels(
+        "//foo/bar:bar1",
+        "//foo/bar:bar2",
+        "//foo/bar:BUILD",
+        "//foo/bar:wiz/bang",
+        "//foo/bar:wiz/all",
+        "//foo/bar:baz",
+        "//foo/bar:baz/bang",
+        "//foo/bar:undeclared.h");
+    if (hasImplicitCcOutputs) {
+      targetsInFooBar.addAll(labels("//foo/bar:libbar1.lo", "//foo/bar:libbar2.a"));
+    }
+    targetsBeneathFoo = Sets.newHashSet();
+    targetsBeneathFoo.addAll(targetsInFoo);
+    targetsBeneathFoo.addAll(targetsInFooBar);
+
+    targetsInOtherrules = labels(
+        "//otherrules:group",
+        "//otherrules:wiz",
+        "//otherrules:suite1",
+        "//otherrules:BUILD",
+        "//otherrules:suite/somefile",
+        "//otherrules:wiz",
+        "//otherrules:suite1");
+    if (hasImplicitCcOutputs) {
+      targetsInOtherrules.addAll(labels("//otherrules:libwiz.a"));
+    }
+  }
+
+  private void invalidate(String file) throws InterruptedException {
+    skyframeExecutor.invalidateFilesUnderPathForTesting(reporter,
+        ModifiedFileSet.builder().modify(new PathFragment(file)).build(), rootDirectory);
+  }
+
+  private void invalidate(ModifiedFileSet modifiedFileSet) throws InterruptedException {
+    skyframeExecutor.invalidateFilesUnderPathForTesting(reporter, modifiedFileSet, rootDirectory);
+  }
+
+  private void setDeletedPackages(Set<PackageIdentifier> deletedPackages) {
+    skyframeExecutor.setDeletedPackages(deletedPackages);
+  }
+
+  private TargetPatternEvaluator shiftOffset() {
+    parser.updateOffset(fooOffset);
+    return parser;
+  }
+
+  private Set<Label> parseList(String... patterns)
+      throws TargetParsingException, InterruptedException {
+    return targetsToLabels(
+        getFailFast(parseTargetPatternList(parser, parsingListener, Arrays.asList(patterns),
+            false)));
+  }
+
+  private Set<Label> parseListKeepGoingExpectFailure(String... patterns)
+      throws TargetParsingException, InterruptedException {
+    ResolvedTargets<Target> result =
+        parseTargetPatternList(parser, parsingListener, Arrays.asList(patterns), true);
+    assertTrue(result.hasError());
+    return targetsToLabels(result.getTargets());
+  }
+
+  private Set<Label> parseList(
+      FilteringPolicy policy, String... patterns)
+      throws TargetParsingException, InterruptedException {
+    return targetsToLabels(getFailFast(
+        parseTargetPatternList(parser, parsingListener, Arrays.asList(patterns), policy, false)));
+  }
+
+  private Set<Label> parseListRelative(String... patterns)
+      throws TargetParsingException, InterruptedException {
+    return targetsToLabels(getFailFast(parseTargetPatternList(
+        shiftOffset(), parsingListener, Arrays.asList(patterns), false)));
+  }
+
+  private static Set<Target> getFailFast(ResolvedTargets<Target> result) {
+    assertFalse(result.hasError());
+    return result.getTargets();
+  }
+
+  private void expectError(TargetPatternEvaluator parser, String expectedError,
+      String target) throws InterruptedException {
+    try {
+      parser.parseTargetPattern(parsingListener, target, false);
+      fail("target='" + target + "', expected error: " + expectedError);
+    } catch (TargetParsingException e) {
+      assertThat(e.getMessage()).contains(expectedError);
+    }
+  }
+
+  private void expectError(String expectedError, String target) throws InterruptedException {
+    expectError(parser, expectedError, target);
+  }
+
+  private void expectErrorRelative(String expectedError, String target)
+      throws InterruptedException {
+    expectError(shiftOffset(), expectedError, target);
+  }
+
+  private Label parseIndividualTarget(String targetLabel) throws Exception {
+    return Iterables.getOnlyElement(
+        getFailFast(parser.parseTargetPattern(parsingListener, targetLabel, false))).getLabel();
+  }
+
+  private Label parseIndividualTargetRelative(String targetLabel) throws Exception {
+    return Iterables.getOnlyElement(
+        getFailFast(
+            shiftOffset().parseTargetPattern(parsingListener, targetLabel, false))).getLabel();
+  }
+
+  @Test
+  public void testParsingStandardLabel() throws Exception {
+    assertEquals("//foo:foo1",
+        parseIndividualTarget("//foo:foo1").toString());
+  }
+
+  @Test
+  public void testAbsolutePatternEndsWithSlashAll() throws Exception {
+    scratch.file("foo/all/BUILD", "cc_library(name = 'all')");
+    assertEquals("//foo/all:all", parseIndividualTarget("//foo/all").toString());
+    assertNoEvents();
+  }
+
+  @Test
+  public void testWildcardConflict() throws Exception {
+    scratch.file("foo/lib/BUILD",
+        "cc_library(name = 'lib1')",
+        "cc_library(name = 'lib2')",
+        "cc_library(name = 'all-targets')",
+        "cc_library(name = 'all')");
+
+    assertWildcardConflict("//foo/lib:all", ":all");
+    eventCollector.clear();
+    assertWildcardConflict("//foo/lib:all-targets", ":all-targets");
+  }
+
+  private void assertWildcardConflict(String label, String suffix) throws Exception {
+    assertEquals(label, parseIndividualTarget(label).toString());
+    assertSame(1, eventCollector.count());
+    assertContainsEvent(String.format("The target pattern '%s' is ambiguous: '%s' is both "
+        + "a wildcard, and the name of an existing cc_library rule; "
+        + "using the latter interpretation", label, suffix));
+  }
+
+  @Test
+  public void testMissingPackage() throws Exception {
+    try {
+      parseIndividualTarget("//missing:foo1");
+      fail("TargetParsingException expected");
+    } catch (TargetParsingException e) {
+      assertThat(e.getMessage()).startsWith("no such package");
+    }
+  }
+
+  @Test
+  public void testParsingStandardLabelWithRelativeParser() throws Exception {
+    assertEquals("//foo:foo1", parseIndividualTargetRelative("//foo:foo1").toString());
+  }
+
+  @Test
+  public void testMissingLabel() throws Exception {
+    try {
+      parseIndividualTarget("//foo:missing");
+      fail("TargetParsingException expected");
+    } catch (TargetParsingException e) {
+      assertThat(e.getMessage()).startsWith("no such target");
+    }
+  }
+
+  @Test
+  public void testParsingStandardLabelShorthand() throws Exception {
+    assertEquals("//foo:foo1",
+                 parseIndividualTarget("foo:foo1").toString());
+  }
+
+  @Test
+  public void testParsingStandardLabelShorthandRelative() throws Exception {
+    assertEquals("//foo:foo1", parseIndividualTargetRelative(":foo1").toString());
+  }
+
+
+  @Test
+  public void testAbsolutePathCantBeParsed() throws Exception {
+    expectError("not a relative path or label: '/absolute/path'",
+        "/absolute/path");
+  }
+
+  @Test
+  public void testAbsolutePathCantBeParsedWithRelativeParser() throws Exception {
+    expectErrorRelative("not a relative path or label: '/absolute/path'", "/absolute/path");
+  }
+
+  @Test
+  public void testUnsupportedTargets() throws Exception {
+    String expectedError = "no such target '//foo:foo': target 'foo' not declared in package 'foo'"
+        + " defined by /workspace/foo/BUILD";
+    expectError(expectedError, "foo");
+    expectError("The package part of 'foo/' should not end in a slash", "foo/");
+  }
+
+  @Test
+  public void testModifiedBuildFile() throws Exception {
+    assertThat(parseList("foo:all")).containsExactlyElementsIn(rulesInFoo);
+    assertNoEvents();
+
+    scratch.overwriteFile("foo/BUILD",
+        "cc_library(name = 'foo1', srcs = [ 'foo1.cc' ], hdrs = [ 'foo1.h' ])",
+        "cc_library(name = 'foo2', srcs = [ 'foo1.cc' ], hdrs = [ 'foo1.h' ])");
+    invalidate("foo/BUILD");
+    assertThat(parseList("foo:all")).containsExactlyElementsIn(labels("//foo:foo1", "//foo:foo2"));
+  }
+
+  @Test
+  public void testParserOffsetUpdated() throws Exception {
+    scratch.file("nest/BUILD",
+        "cc_library(name = 'nested1', srcs = [ ])");
+    scratch.file("nest/nest/BUILD",
+        "cc_library(name = 'nested2', srcs = [ ])");
+
+    updateOffset(new PathFragment("nest"));
+    assertThat(parseList(":all")).containsExactlyElementsIn(labels("//nest:nested1"));
+    updateOffset(new PathFragment("nest/nest"));
+    assertThat(parseList(":all")).containsExactlyElementsIn(labels("//nest/nest:nested2"));
+  }
+
+  protected void updateOffset(PathFragment rel) {
+    parser.updateOffset(rel);
+  }
+
+  private void runFindTargetsInPackage(String suffix) throws Exception {
+    // 'my_package:all'
+    assertThat(parseList("foo" + suffix)).containsExactlyElementsIn(rulesInFoo);
+    assertThat(parseList("foo/bar" + suffix)).containsExactlyElementsIn(rulesInFooBar);
+    assertThat(parseList("otherrules" + suffix)).containsExactlyElementsIn(rulesInOtherrules);
+    assertNoEvents();
+    String msg1 = "while parsing 'nosuchpkg" + suffix + "': no such package 'nosuchpkg': "
+        + "BUILD file not found on package path";
+    expectError(msg1, "nosuchpkg" + suffix);
+
+    String msg2 = "while parsing 'nosuchdirectory" + suffix
+        + "': no such package 'nosuchdirectory': "
+        + "BUILD file not found on package path";
+    expectError(msg2, "nosuchdirectory" + suffix);
+    assertThat(parsingListener.events).containsExactly(Pair.of("nosuchpkg" + suffix, msg1),
+        Pair.of("nosuchdirectory" + suffix, msg2));
+  }
+
+  private void runFindTargetsInPackageAbsolute(String suffix) throws Exception {
+    // '//my_package:all'
+    assertThat(parseList("//foo" + suffix)).containsExactlyElementsIn(rulesInFoo);
+    assertThat(parseList("//foo/bar" + suffix)).containsExactlyElementsIn(rulesInFooBar);
+    assertThat(parseList("//otherrules" + suffix)).containsExactlyElementsIn(rulesInOtherrules);
+    assertNoEvents();
+    expectError("while parsing 'nosuchpkg" + suffix + "': no such package 'nosuchpkg': "
+            + "BUILD file not found on package path",
+        "nosuchpkg" + suffix);
+    expectError("while parsing '//nosuchpkg" + suffix + "': no such package 'nosuchpkg': "
+            + "BUILD file not found on package path",
+        "//nosuchpkg" + suffix);
+  }
+
+  @Test
+  public void testFindRulesInPackage() throws Exception {
+    runFindTargetsInPackage(":all");
+    runFindTargetsInPackageAbsolute(":all");
+  }
+
+  private void runFindRulesRecursively(String suffix) throws Exception {
+    assertThat(parseList("foo" + suffix)).containsExactlyElementsIn(rulesBeneathFoo);
+    assertThat(parseList("//foo" + suffix)).containsExactlyElementsIn(rulesBeneathFoo);
+    assertThat(parseList("//foo/bar" + suffix)).containsExactlyElementsIn(rulesBeneathFooBar);
+    assertThat(parseList("//foo" + suffix)).containsExactlyElementsIn(rulesBeneathFoo);
+    assertThat(parseList("otherrules" + suffix)).containsExactlyElementsIn(rulesBeneathOtherrules);
+    assertThat(parseList("//foo" + suffix)).containsExactlyElementsIn(rulesBeneathFoo);
+    assertNoEvents();
+    eventCollector.clear();
+  }
+
+  @Test
+  public void testNoTargetsFoundRecursiveDirectory() throws Exception {
+    try {
+      parseList("nosuchpkg/...");
+      fail();
+    } catch (TargetParsingException e) {
+      assertThat(e).hasMessage("no targets found beneath 'nosuchpkg'");
+    }
+  }
+
+  @Test
+  public void testFindRulesRecursively() throws Exception {
+    runFindRulesRecursively("/...:all");
+    runFindRulesRecursively("/...");
+  }
+
+  private void runFindAllRules(String pattern) throws Exception {
+    assertThat(parseList(pattern))
+        .containsExactlyElementsIn(Sets.union(rulesBeneathFoo, rulesBeneathOtherrules));
+    assertNoEvents();
+    eventCollector.clear();
+  }
+
+  @Test
+  public void testFindAllRules() throws Exception {
+    runFindAllRules("//...:all");
+    runFindAllRules("//...");
+    runFindAllRules("...");
+  }
+
+  private void runFindAllTargets(String pattern) throws Exception {
+    assertThat(parseList(pattern))
+        .containsExactlyElementsIn(Sets.union(targetsBeneathFoo, targetsInOtherrules));
+    assertNoEvents();
+    eventCollector.clear();
+  }
+
+  @Test
+  public void testFindAllTargets() throws Exception {
+    runFindAllTargets("//...:all-targets");
+    runFindAllTargets("//...:*");
+    runFindAllTargets("...:*");
+  }
+
+  @Test
+  public void testFindAllRulesRecursivelyWithExperimental() throws Exception {
+    scratch.file("experimental/BUILD",
+        "cc_library(name = 'experimental', srcs = [ 'experimental.cc' ])");
+    assertThat(parseList("//..."))
+        .containsExactlyElementsIn(Sets.union(rulesBeneathFoo, rulesBeneathOtherrules));
+    assertNoEvents();
+  }
+
+  @Test
+  public void testFindAllRulesRecursivelyExperimental() throws Exception {
+    scratch.file("experimental/BUILD",
+        "cc_library(name = 'experimental', srcs = [ 'experimental.cc' ])");
+    assertThat(parseList("//experimental/..."))
+        .containsExactlyElementsIn(labels("//experimental:experimental"));
+    assertNoEvents();
+  }
+
+  @Test
+  public void testDefaultPackage() throws Exception {
+    scratch.file("experimental/BUILD",
+                "cc_library(name = 'experimental', srcs = [ 'experimental.cc' ])");
+    assertEquals("//experimental:experimental", parseIndividualTarget("//experimental").toString());
+    assertEquals("//experimental:experimental", parseIndividualTarget("experimental").toString());
+    assertNoEvents();
+  }
+
+  /**
+   * Test that the relative path label parsing behaves as stated in the target-syntax documentation.
+   */
+  @Test
+  public void testRelativePathLabel() throws Exception {
+    scratch.file("sub/BUILD", "exports_files(['dir2/dir2'])");
+    scratch.file("sub/dir/BUILD", "exports_files(['dir2'])");
+    scratch.file("sub/dir/dir/BUILD", "exports_files(['dir'])");
+    // sub/dir/dir is a package
+    assertEquals("//sub/dir/dir:dir", parseIndividualTarget("sub/dir/dir").toString());
+    // sub/dir is a package but not sub/dir/dir2
+    assertEquals("//sub/dir:dir2", parseIndividualTarget("sub/dir/dir2").toString());
+    // sub is a package but not sub/dir2
+    assertEquals("//sub:dir2/dir2", parseIndividualTarget("sub/dir2/dir2").toString());
+  }
+
+  @Test
+  public void testFindsLongestPlausiblePackageName() throws Exception {
+    assertEquals("//foo/bar:baz",
+                 parseIndividualTarget("foo/bar/baz").toString());
+    assertEquals("//foo/bar:baz/bang",
+                 parseIndividualTarget("foo/bar/baz/bang").toString());
+    assertEquals("//foo:baz/bang",
+        parseIndividualTarget("foo/baz/bang").toString());
+  }
+
+  @Test
+  public void testGivesUpIfPackageDoesNotExist() throws Exception {
+    expectError("couldn't determine target from filename 'does/not/exist'",
+        "does/not/exist");
+  }
+
+  @Test
+  public void testParsesIterableOfLabels() throws Exception {
+    Set<Label> labels = Sets.newHashSet(Label.parseAbsolute("//foo/bar:bar1"),
+        Label.parseAbsolute("//foo:foo1"));
+    assertEquals(labels, parseList("//foo/bar:bar1", "//foo:foo1"));
+    parsingListener.assertEmpty();
+  }
+
+  @Test
+  public void testParseAbsoluteWithRelativeParser() throws Exception {
+    Set<Label> labels = Sets.newHashSet(Label.parseAbsolute("//foo/bar:bar1"),
+        Label.parseAbsolute("//foo:foo1"));
+    assertEquals(labels, parseListRelative("//foo/bar:bar1", "//foo:foo1"));
+    parsingListener.assertEmpty();
+  }
+
+  @Test
+  public void testMultisegmentLabelsWithNoSlashSlash() throws Exception {
+    assertEquals("//foo/bar:wiz/bang",
+        parseIndividualTarget("foo/bar:wiz/bang").toString());
+    assertEquals("//foo/bar:wiz/all",
+        parseIndividualTarget("foo/bar:wiz/all").toString());
+  }
+
+  @Test
+  public void testMultisegmentLabelsWithNoSlashSlashRelative() throws Exception {
+    assertEquals("//foo/bar:wiz/bang",
+        parseIndividualTargetRelative("bar:wiz/bang").toString());
+    assertEquals("//foo/bar:wiz/all",
+        parseIndividualTargetRelative("bar:wiz/all").toString());
+  }
+
+  @Test
+  public void testAll() throws Exception {
+    expectError("couldn't determine target from filename 'all'", "all");
+  }
+
+  /** Regression test for a bug. */
+  @Test
+  public void testDotDotDotDoesntMatchDeletedPackages() throws Exception {
+    scratch.file("x/y/BUILD", "cc_library(name='y')");
+    scratch.file("x/z/BUILD", "cc_library(name='z')");
+    setDeletedPackages(Sets.newHashSet(PackageIdentifier.createInDefaultRepo("x/y")));
+    assertEquals(Sets.newHashSet(Label.parseAbsolute("//x/z")),
+        parseList("x/..."));
+  }
+
+  @Test
+  public void testDotDotDotDoesntMatchDeletedPackagesRelative() throws Exception {
+    scratch.file("x/y/BUILD", "cc_library(name='y')");
+    scratch.file("x/z/BUILD", "cc_library(name='z')");
+    setDeletedPackages(Sets.newHashSet(PackageIdentifier.createInDefaultRepo("x/y")));
+
+    parser.updateOffset(new PathFragment("x"));
+    assertEquals(Sets.newHashSet(Label.parseAbsolute("//x/z")),
+        targetsToLabels(getFailFast(parser.parseTargetPattern(parsingListener, "...", false))));
+  }
+
+  @Test
+  public void testDeletedPackagesIncrementality() throws Exception {
+    scratch.file("x/y/BUILD", "cc_library(name='y')");
+    scratch.file("x/z/BUILD", "cc_library(name='z')");
+
+    assertEquals(Sets.newHashSet(Label.parseAbsolute("//x/y"), Label.parseAbsolute("//x/z")),
+        parseList("x/..."));
+
+    setDeletedPackages(Sets.newHashSet(PackageIdentifier.createInDefaultRepo("x/y")));
+    assertEquals(Sets.newHashSet(Label.parseAbsolute("//x/z")), parseList("x/..."));
+
+    setDeletedPackages(ImmutableSet.<PackageIdentifier>of());
+    assertEquals(Sets.newHashSet(Label.parseAbsolute("//x/y"), Label.parseAbsolute("//x/z")),
+        parseList("x/..."));
+  }
+
+  @Test
+  public void testSequenceOfTargetPatterns_Union() throws Exception {
+    // No prefix negation operator => union.  Order is not significant.
+    assertThat(parseList("foo/...", "foo/bar/...")).containsExactlyElementsIn(rulesBeneathFoo);
+    assertThat(parseList("foo/bar/...", "foo/...")).containsExactlyElementsIn(rulesBeneathFoo);
+  }
+
+  @Test
+  public void testSequenceOfTargetPatterns_UnionRelative() throws Exception {
+    // No prefix negation operator => union.  Order is not significant.
+    assertThat(parseListRelative("...", "bar/...")).containsExactlyElementsIn(rulesBeneathFoo);
+    assertThat(parseListRelative("bar/...", "...")).containsExactlyElementsIn(rulesBeneathFoo);
+  }
+
+  @Test
+  public void testSequenceOfTargetPatterns_SetDifference() throws Exception {
+    // Prefix negation operator => set difference.  Order is significant.
+    assertThat(parseList("foo/...", "-foo/bar/...")).containsExactlyElementsIn(rulesInFoo);
+    assertThat(parseList("-foo/bar/...", "foo/...")).containsExactlyElementsIn(rulesBeneathFoo);
+  }
+
+  @Test
+  public void testSequenceOfTargetPatterns_SetDifferenceRelative() throws Exception {
+    // Prefix negation operator => set difference.  Order is significant.
+    assertThat(parseListRelative("...", "-bar/...")).containsExactlyElementsIn(rulesInFoo);
+    assertThat(parseListRelative("-bar/...", "...")).containsExactlyElementsIn(rulesBeneathFoo);
+  }
+
+  @Test
+  public void testAllTargetsWildcard() throws Exception {
+    assertThat(parseList("foo:all-targets")).containsExactlyElementsIn(targetsInFoo);
+    assertThat(parseList("foo/bar:all-targets")).containsExactlyElementsIn(targetsInFooBar);
+    assertThat(parseList("otherrules:all-targets")).containsExactlyElementsIn(targetsInOtherrules);
+    assertThat(parseList("foo/...:all-targets")).containsExactlyElementsIn(targetsBeneathFoo);
+
+    assertThat(parseList("foo:*")).containsExactlyElementsIn(targetsInFoo);
+    assertThat(parseList("foo/bar:*")).containsExactlyElementsIn(targetsInFooBar);
+    assertThat(parseList("otherrules:*")).containsExactlyElementsIn(targetsInOtherrules);
+    assertThat(parseList("foo/...:*")).containsExactlyElementsIn(targetsBeneathFoo);
+  }
+
+  @Test
+  public void testAllTargetsWildcardRelative() throws Exception {
+    assertThat(parseListRelative(":all-targets")).containsExactlyElementsIn(targetsInFoo);
+    assertThat(parseListRelative("//foo:all-targets")).containsExactlyElementsIn(targetsInFoo);
+    assertThat(parseListRelative("bar:all-targets")).containsExactlyElementsIn(targetsInFooBar);
+    assertThat(parseListRelative("//foo/bar:all-targets"))
+        .containsExactlyElementsIn(targetsInFooBar);
+    assertThat(parseListRelative("...:all-targets")).containsExactlyElementsIn(targetsBeneathFoo);
+    assertThat(parseListRelative("//foo/...:all-targets"))
+        .containsExactlyElementsIn(targetsBeneathFoo);
+
+    assertThat(parseListRelative(":*")).containsExactlyElementsIn(targetsInFoo);
+    assertThat(parseListRelative("//foo:*")).containsExactlyElementsIn(targetsInFoo);
+    assertThat(parseListRelative("bar:*")).containsExactlyElementsIn(targetsInFooBar);
+    assertThat(parseListRelative("//foo/bar:*")).containsExactlyElementsIn(targetsInFooBar);
+    assertThat(parseListRelative("...:*")).containsExactlyElementsIn(targetsBeneathFoo);
+    assertThat(parseListRelative("//foo/...:*")).containsExactlyElementsIn(targetsBeneathFoo);
+  }
+
+  @Test
+  public void testFactoryMethod() throws Exception {
+    Path workspace = scratch.dir("/client/workspace");
+    Path underWorkspace = scratch.dir("/client/workspace/foo");
+    Path notUnderWorkspace = scratch.dir("/client/otherclient");
+
+    updateOffset(workspace, underWorkspace);
+    updateOffset(workspace, workspace);
+
+    // The client must be equal to or underneath the workspace.
+    try {
+      updateOffset(workspace, notUnderWorkspace);
+      fail("Should have failed because client was not underneath the workspace");
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
+  private void updateOffset(Path workspace, Path workingDir) {
+    parser.updateOffset(workingDir.relativeTo(workspace));
+  }
+
+  private void setupSubDirectoryCircularSymlink() throws Exception {
+    Path parent = scratch.file("parent/BUILD", "sh_library(name = 'parent')").getParentDirectory();
+    Path child = parent.getRelative("child");
+    child.createDirectory();
+    Path badBuild = child.getRelative("BUILD");
+    badBuild.createSymbolicLink(badBuild);
+    reporter.removeHandler(failFastHandler);
+  }
+
+  @Test
+  public void testSubdirectoryCircularSymlinkKeepGoing() throws Exception {
+    setupSubDirectoryCircularSymlink();
+    assertThat(parseListKeepGoing("//parent/...").getFirst())
+        .containsExactlyElementsIn(labels("//parent:parent"));
+  }
+
+  @Test
+  public void testSubdirectoryCircularSymlinkNoKeepGoing() throws Exception {
+    setupSubDirectoryCircularSymlink();
+    try {
+      parseList("//parent/...");
+      fail();
+    } catch (TargetParsingException e) {
+      // Expected.
+    }
+  }
+
+  /** Regression test for bug: "Bogus 'helpful' error message" */
+  @Test
+  public void testHelpfulMessageForFileOutsideOfAnyPackage() throws Exception {
+    scratch.file("goo/wiz/file");
+    expectError("couldn't determine target from filename 'goo/wiz/file'",
+                "goo/wiz/file");
+    expectError("couldn't determine target from filename 'goo/wiz'",
+        "goo/wiz");
+  }
+
+  /** Regression test for bug: "Bogus 'helpful' error message" */
+  @Test
+  public void testHelpfulMessageForDirectoryWhichIsASubdirectoryOfAPackage() throws Exception {
+    scratch.file("bar/BUILD");
+    scratch.file("bar/quux/somefile");
+    expectError("no such target '//bar:quux': target 'quux' not declared in package 'bar'; "
+            + "however, a source directory of this name exists.  (Perhaps add "
+            + "'exports_files([\"quux\"])' to bar/BUILD, or define a filegroup?) defined by "
+            + "/workspace/bar/BUILD",
+        "bar/quux");
+  }
+
+  /** Regression test for bug: "Uplevel references in blaze target patterns cause crash" */
+  @Test
+  public void testNoCrashWhenUplevelReferencesUsed() throws Exception {
+    scratch.file("/other/workspace/project/BUILD");
+    expectError(
+        "Invalid package name '../other/workspace/project': ",
+        "../other/workspace/project/...:all");
+    expectError(
+        "Invalid package name '../other/workspace/project': ", "../other/workspace/project/...");
+    expectError(
+        "Invalid package name 'foo/../../other/workspace/project': ",
+        "foo/../../other/workspace/project/...");
+    expectError(
+        "Invalid package name '../other/workspace/project': ", "../other/workspace/project:all");
+  }
+
+  @Test
+  public void testPassingValidations() {
+    expectValidationPass("foo:bar");
+    expectValidationPass("foo:all");
+    expectValidationPass("foo/...:all");
+    expectValidationPass("foo:*");
+
+    expectValidationPass("//foo");
+    expectValidationPass("foo");
+    expectValidationPass("foo/bar");
+    expectValidationPass("//foo:bar");
+    expectValidationPass("//foo:all");
+
+    expectValidationPass("//foo/all");
+    expectValidationPass("java/com/google/foo/Bar.java");
+    expectValidationPass("//foo/...:all");
+  }
+
+  @Test
+  public void testFailingValidations() {
+    expectValidationFail("");
+    expectValidationFail("\\");
+    expectValidationFail("foo:**");
+    expectValidationFail("//foo/*");
+  }
+
+  private void expectValidationFail(String target) {
+    try {
+      TargetPattern.defaultParser().parse(target);
+      fail("TargetParsingException expected from parse(" + target + ")");
+    } catch (TargetParsingException expected) {
+      /* ignore */
+    }
+
+    // Ensure that validateTargetPattern's checking is strictly weaker than
+    // that of parseTargetPattern.
+    try {
+      parser.parseTargetPattern(parsingListener, target, false);
+      fail("parseTargetPattern(" + target + ") inconsistent with parseTargetPattern!");
+    } catch (TargetParsingException expected) {
+      /* ignore */
+    } catch (InterruptedException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  private void expectValidationPass(String target) {
+    try {
+      TargetPattern.defaultParser().parse(target);
+    } catch (TargetParsingException e) {
+      fail("Expected " + target + " to pass; got exception: " + e);
+    }
+  }
+
+  @Test
+  public void testSetOffset() throws Exception {
+    assertEquals("//foo:foo1", parseIndividualTarget("foo:foo1").toString());
+
+    parser.updateOffset(new PathFragment("foo"));
+    assertEquals("//foo:foo1", parseIndividualTarget(":foo1").toString());
+  }
+
+  @Test
+  public void testTestTargetParsing() throws Exception {
+    scratch.file("test/BUILD",
+        "cc_library(name = 'bar1', alwayslink = 1)",
+        "cc_library(name = 'bar2')",
+        "cc_test(name = 'test1', deps = ['bar1'], tags = ['local'])",
+        "cc_test(name = 'test2', deps = ['bar2'], tags = ['local'])",
+        "py_test(name = 'manual_test', tags = ['exclusive', 'manual'], srcs=['py_test.py'])",
+        "test_suite(name = 'suite1')");
+
+    Set<Label> testRules = labels("//test:test1", "//test:test2");
+    Set<Label> allTestRules =
+      labels("//test:test1", "//test:test2", "//test:manual_test");
+    assertThat(parseList(FILTER_TESTS, "test/...")).containsExactlyElementsIn(testRules);
+    assertThat(parseList(FILTER_TESTS, "test:all")).containsExactlyElementsIn(testRules);
+    assertThat(parseList(FILTER_TESTS, "test:*")).containsExactlyElementsIn(testRules);
+    assertThat(parseList(FILTER_TESTS, "test:test1", "test/test2", "//test:suite1"))
+        .containsExactlyElementsIn(testRules);
+    assertThat(parseList(FILTER_TESTS, "test:all", "//test:manual_test"))
+        .containsExactlyElementsIn(allTestRules);
+    assertThat(parseList(FILTER_TESTS, "test:all", "test/manual_test"))
+        .containsExactlyElementsIn(allTestRules);
+  }
+
+  /** Regression test for bug: "blaze test "no targets found" warning now fatal" */
+  @Test
+  public void testNoTestsInRecursivePattern() throws Exception {
+    assertThat(parseList(FILTER_TESTS, "foo/..."))
+        .containsExactlyElementsIn(labels()); // doesn't throw
+  }
+
+  @Test
+  public void testKeepGoingBadPackage() throws Exception {
+    assertKeepGoing(rulesBeneathFoo,
+        "Skipping '//missing_pkg': no such package 'missing_pkg': "
+            + "BUILD file not found on package path",
+        "//missing_pkg", "foo/...");
+  }
+
+  @Test
+  public void testKeepGoingPartiallyBadPackage() throws Exception {
+    scratch.file("x/y/BUILD",
+        "filegroup(name = 'a')",
+        "BROKEN",
+        "filegroup(name = 'b')");
+
+    reporter.removeHandler(failFastHandler);
+    Pair<Set<Label>, Boolean> result = parseListKeepGoing("//x/...");
+
+    assertContainsEvent("name 'BROKEN' is not defined");
+    assertThat(result.first)
+        .containsExactlyElementsIn(
+            Sets.newHashSet(Label.parseAbsolute("//x/y:a"), Label.parseAbsolute("//x/y:b")));
+    assertFalse(result.second);
+  }
+
+  @Test
+  public void testKeepGoingMissingRecursiveDirectory() throws Exception {
+    assertKeepGoing(rulesBeneathFoo,
+        "Skipping 'nosuchpkg/...': no targets found beneath 'nosuchpkg'",
+        "nosuchpkg/...", "foo/...");
+    eventCollector.clear();
+    assertKeepGoing(rulesBeneathFoo,
+        "Skipping 'nosuchdirectory/...': no targets found beneath 'nosuchdirectory'",
+        "nosuchdirectory/...", "foo/...");
+  }
+
+  @Test
+  public void testKeepGoingMissingTarget() throws Exception {
+    assertKeepGoing(rulesBeneathFoo,
+        "Skipping '//otherrules:missing_target': no such target "
+            + "'//otherrules:missing_target': target 'missing_target' not declared in "
+            + "package 'otherrules'",
+        "//otherrules:missing_target", "foo/...");
+  }
+
+  @Test
+  public void testKeepGoingOnAllRulesBeneath() throws Exception {
+    scratch.file("foo/bar/bad/BUILD", "invalid build file");
+
+    reporter.removeHandler(failFastHandler);
+    Pair<Set<Label>, Boolean> result = parseListKeepGoing("foo/...");
+    assertThat(result.first).containsExactlyElementsIn(rulesBeneathFoo);
+    assertContainsEvent("syntax error at 'build'");
+    assertContainsEvent("package contains errors");
+    reporter.addHandler(failFastHandler);
+
+    // Even though there was a loading error in the package, parsing the target pattern was
+    // successful.
+    assertFalse(result.second);
+  }
+
+  @Test
+  public void testKeepGoingBadFilenameTarget() throws Exception {
+    assertKeepGoing(rulesBeneathFoo,
+        "couldn't determine target from filename 'bad/filename/target'",
+        "bad/filename/target", "foo/...");
+  }
+
+  @Test
+  public void testMoreThanOneBadPatternFailFast() throws Exception {
+    try {
+      parseTargetPatternList(parser, parsingListener,
+          ImmutableList.of("bad/filename/target", "other/bad/filename/target"),
+          /*keepGoing=*/false);
+      fail();
+    } catch (TargetParsingException e) {
+      assertThat(e.getMessage()).contains("couldn't determine target from filename");
+    }
+  }
+
+  @Test
+  public void testMentioningBuildFile() throws Exception {
+    ResolvedTargets<Target> result = parseTargetPatternList(parser, parsingListener,
+        Arrays.asList("//foo/bar/BUILD"), false);
+
+    assertFalse(result.hasError());
+    assertThat(result.getTargets()).hasSize(1);
+
+    Label label = Iterables.getOnlyElement(result.getTargets()).getLabel();
+    assertEquals("BUILD", label.getName());
+    assertEquals("foo/bar", label.getPackageName());
+
+  }
+
+  /**
+   * Regression test for bug: '"Target pattern parsing failed. Continuing anyway" appears, even
+   * without --keep_going'
+   */
+  @Test
+  public void testLoadingErrorsAreNotParsingErrors() throws Exception {
+    reporter.removeHandler(failFastHandler);
+    scratch.file("loading/BUILD",
+        "cc_library(name='y', deps=['a'])",
+        "cc_library(name='a', deps=['b'])",
+        "cc_library(name='b', deps=['c'])",
+        "genrule(name='c', outs=['c.out'])");
+
+    Pair<Set<Label>, Boolean> result = parseListKeepGoing("//loading:y");
+    assertEquals(Label.parseAbsolute("//loading:y"), Iterables.getOnlyElement(result.first));
+    assertContainsEvent("missing value for mandatory attribute");
+    assertFalse(result.second);
+  }
+
+  private void assertKeepGoing(Set<Label> expectedLabels, String expectedEvent, String... toParse)
+      throws Exception {
+    reporter.removeHandler(failFastHandler);
+    assertThat(parseListKeepGoingExpectFailure(toParse)).containsExactlyElementsIn(expectedLabels);
+    assertContainsEvent(expectedEvent);
+    reporter.addHandler(failFastHandler);
+  }
+
+  /** Regression test for bug: "IllegalStateException in BuildTool.prepareToBuild()" */
+  @Test
+  public void testTestingIsSubset() throws Exception {
+    scratch.file("test/BUILD",
+        "cc_library(name = 'bar1')",
+        "cc_test(name = 'test', deps = [':bar1'], tags = ['manual'])");
+
+    assertThat(parseList(FILTER_TESTS, "//test:test", "-//test:all"))
+        .containsExactlyElementsIn(labels());
+  }
+
+  @Test
+  public void testAddedPkg() throws Exception {
+    invalidate(ModifiedFileSet.EVERYTHING_MODIFIED);
+    scratch.dir("h/i/j/k/BUILD");
+    scratch.file("h/BUILD", "sh_library(name='h')");
+    assertThat(parseList("//h/...")).containsExactlyElementsIn(labels("//h"));
+
+    scratch.file("h/i/j/BUILD", "sh_library(name='j')");
+
+    // Modifications not yet known.
+    assertThat(parseList("//h/...")).containsExactlyElementsIn(labels("//h"));
+
+    ModifiedFileSet modifiedFileSet = ModifiedFileSet.builder()
+        .modify(new PathFragment("h/i/j/BUILD")).build();
+    invalidate(modifiedFileSet);
+
+    assertThat(parseList("//h/...")).containsExactly(Label.parseAbsolute("//h/i/j:j"),
+        Label.parseAbsolute("//h"));
+  }
+
+  @Test
+  public void testAddedFilesAndDotDotDot() throws Exception {
+    invalidate(ModifiedFileSet.EVERYTHING_MODIFIED);
+    reporter.removeHandler(failFastHandler);
+    scratch.dir("h");
+    try {
+      parseList("//h/...");
+      fail("TargetParsingException expected");
+    } catch (TargetParsingException e) {
+      // expected
+    }
+
+    scratch.file("h/i/j/k/BUILD", "sh_library(name='l')");
+    ModifiedFileSet modifiedFileSet = ModifiedFileSet.builder()
+        .modify(new PathFragment("h"))
+        .modify(new PathFragment("h/i"))
+        .modify(new PathFragment("h/i/j"))
+        .modify(new PathFragment("h/i/j/k"))
+        .modify(new PathFragment("h/i/j/k/BUILD"))
+        .build();
+    invalidate(modifiedFileSet);
+    reporter.addHandler(failFastHandler);
+    Set<Label> nonEmptyResult = parseList("//h/...");
+    assertThat(nonEmptyResult).containsExactly(Label.parseAbsolute("//h/i/j/k:l"));
+  }
+
+  @Test
+  public void testBrokenSymlinkRepaired() throws Exception {
+    reporter.removeHandler(failFastHandler);
+    Path tuv = scratch.dir("t/u/v");
+    tuv.getChild("BUILD").createSymbolicLink(new PathFragment("../../BUILD"));
+
+    try {
+      parseList("//t/...");
+      fail("TargetParsingException expected");
+    } catch (TargetParsingException e) {
+      // expected
+    }
+
+    scratch.file("t/BUILD", "sh_library(name='t')");
+    ModifiedFileSet modifiedFileSet = ModifiedFileSet.builder()
+        .modify(new PathFragment("t/BUILD"))
+        .build();
+
+    invalidate(modifiedFileSet);
+    reporter.addHandler(failFastHandler);
+    Set<Label> result = parseList("//t/...");
+
+    assertThat(result).containsExactly(Label.parseAbsolute("//t:t"),
+        Label.parseAbsolute("//t/u/v:t"));
+  }
+
+  @Test
+  public void testInfiniteTreeFromSymlinks() throws Exception {
+    reporter.removeHandler(failFastHandler);
+    Path ab = scratch.dir("a/b");
+    ab.getChild("c").createSymbolicLink(new PathFragment("../b"));
+    scratch.file("a/b/BUILD", "filegroup(name='g')");
+    ResolvedTargets<Target> result = parseTargetPatternList(parser, parsingListener,
+        ImmutableList.of("//a/b/..."), true);
+    assertThat(targetsToLabels(result.getTargets())).containsExactly(
+        Label.parseAbsolute("//a/b:g"));
+  }
+
+  @Test
+  public void testSymlinkCycle() throws Exception {
+    reporter.removeHandler(failFastHandler);
+    Path ab = scratch.dir("a/b");
+    ab.getChild("c").createSymbolicLink(new PathFragment("c"));
+    scratch.file("a/b/BUILD", "filegroup(name='g')");
+    ResolvedTargets<Target> result = parseTargetPatternList(parser, parsingListener,
+        ImmutableList.of("//a/b/..."), true);
+    assertThat(targetsToLabels(result.getTargets())).contains(
+        Label.parseAbsolute("//a/b:g"));
+  }
+
+  @Test
+  public void testPerDirectorySymlinkTraversalOptOut() throws Exception {
+    scratch.dir("from-b");
+    scratch.file("from-b/BUILD", "filegroup(name = 'from-b')");
+    scratch.dir("from-c");
+    scratch.file("from-c/BUILD", "filegroup(name = 'from-c')");
+    Path ab = scratch.dir("a/b");
+    ab.getChild("symlink").createSymbolicLink(new PathFragment("../../from-b"));
+    scratch.dir("a/b/not-a-symlink");
+    scratch.file("a/b/not-a-symlink/BUILD", "filegroup(name = 'not-a-symlink')");
+    scratch.file(
+        "a/b/DONT_FOLLOW_SYMLINKS_WHEN_TRAVERSING_THIS_DIRECTORY_VIA_A_RECURSIVE_TARGET_PATTERN");
+    Path ac = scratch.dir("a/c");
+    ac.getChild("symlink").createSymbolicLink(new PathFragment("../../from-c"));
+    ResolvedTargets<Target> result = parseTargetPatternList(parser, parsingListener,
+        ImmutableList.of("//a/..."), true);
+    assertThat(targetsToLabels(result.getTargets())).containsExactly(
+        Label.parseAbsolute("//a/c/symlink:from-c"),
+        Label.parseAbsolute("//a/b/not-a-symlink:not-a-symlink"));
+  }
+
+  @Test
+  public void testDoesNotRecurseIntoSymlinksToOutputBase() throws Exception {
+    Path outputBaseBuildFile = outputBase.getRelative("workspace/test/BUILD");
+    scratch.file(outputBaseBuildFile.getPathString(), "filegroup(name='c')");
+    PathFragment targetFragment = outputBase.asFragment().getRelative("workspace/test");
+    Path d = scratch.dir("d");
+    d.getChild("c").createSymbolicLink(targetFragment);
+    rootDirectory.getChild("convenience").createSymbolicLink(targetFragment);
+    Set<Label> result = parseList("//...");
+    assertThat(result).doesNotContain(Label.parseAbsolute("//convenience:c"));
+    assertThat(result).doesNotContain(Label.parseAbsolute("//d/c:c"));
+  }
+}
+