blob: ec33040a834ae516e510c5cef3b9cc880d7e4285 [file] [log] [blame]
// 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 org.junit.Assert.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
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.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 com.google.devtools.build.lib.vfs.Root;
import java.util.Arrays;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests for {@link TargetPatternPreloader}. */
@RunWith(JUnit4.class)
public class TargetPatternEvaluatorTest extends AbstractTargetPatternEvaluatorTest {
private PathFragment fooOffset;
private Set<Label> rulesBeneathFoo;
private Set<Label> rulesInFoo;
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")
.getDefaultImplicitOutputsFunction() != ImplicitOutputsFunction.NONE;
scratch.file("BUILD",
"filegroup(name = 'fg', srcs = glob(['*.cc']))");
scratch.file("foo.cc");
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");
rulesInFoo = labels("//foo:foo1");
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(PathFragment.create(file)).build(),
Root.fromPath(rootDirectory));
}
private void invalidate(ModifiedFileSet modifiedFileSet) throws InterruptedException {
skyframeExecutor.invalidateFilesUnderPathForTesting(
reporter, modifiedFileSet, Root.fromPath(rootDirectory));
}
private void setDeletedPackages(Set<PackageIdentifier> deletedPackages) {
skyframeExecutor.setDeletedPackages(deletedPackages);
}
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);
return targetsToLabels(result.getTargets());
}
private Set<Label> parseListRelative(String... patterns)
throws TargetParsingException, InterruptedException {
return targetsToLabels(getFailFast(parseTargetPatternList(
fooOffset, parser, parsingListener, Arrays.asList(patterns), false)));
}
private static Set<Target> getFailFast(ResolvedTargets<Target> result) {
assertThat(result.hasError()).isFalse();
return result.getTargets();
}
private void expectError(
PathFragment offset, TargetPatternPreloader parser, String expectedError, String target)
throws InterruptedException {
TargetParsingException e =
assertThrows(
"target='" + target + "', expected error: " + expectedError,
TargetParsingException.class,
() ->
parseTargetPatternList(
offset, parser, parsingListener, ImmutableList.of(target), false));
assertThat(e).hasMessageThat().contains(expectedError);
}
private void expectError(String expectedError, String target) throws InterruptedException {
expectError(PathFragment.EMPTY_FRAGMENT, parser, expectedError, target);
}
private Label parseIndividualTarget(String targetLabel) throws Exception {
return Iterables.getOnlyElement(
getFailFast(
parseTargetPatternList(parser, parsingListener, ImmutableList.of(targetLabel), false)))
.getLabel();
}
@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 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
assertThat(parseIndividualTarget("sub/dir/dir").toString()).isEqualTo("//sub/dir/dir:dir");
// sub/dir is a package but not sub/dir/dir2
assertThat(parseIndividualTarget("sub/dir/dir2").toString()).isEqualTo("//sub/dir:dir2");
// sub is a package but not sub/dir2
assertThat(parseIndividualTarget("sub/dir2/dir2").toString()).isEqualTo("//sub:dir2/dir2");
}
/** 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.createInMainRepo("x/y")));
assertThat(parseList("x/..."))
.isEqualTo(Sets.newHashSet(Label.parseAbsolute("//x/z", ImmutableMap.of())));
}
@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.createInMainRepo("x/y")));
assertThat(
targetsToLabels(
getFailFast(
parseTargetPatternList(
PathFragment.create("x"),
parser,
parsingListener,
ImmutableList.of("..."),
false))))
.isEqualTo(Sets.newHashSet(Label.parseAbsolute("//x/z", ImmutableMap.of())));
}
@Test
public void testDeletedPackagesIncrementality() throws Exception {
scratch.file("x/y/BUILD", "cc_library(name='y')");
scratch.file("x/z/BUILD", "cc_library(name='z')");
assertThat(parseList("x/..."))
.containsExactly(
Label.parseAbsolute("//x/y", ImmutableMap.of()),
Label.parseAbsolute("//x/z", ImmutableMap.of()));
setDeletedPackages(Sets.newHashSet(PackageIdentifier.createInMainRepo("x/y")));
assertThat(parseList("x/...")).containsExactly(Label.parseAbsolute("//x/z", ImmutableMap.of()));
setDeletedPackages(ImmutableSet.<PackageIdentifier>of());
assertThat(parseList("x/..."))
.containsExactly(
Label.parseAbsolute("//x/y", ImmutableMap.of()),
Label.parseAbsolute("//x/z", ImmutableMap.of()));
}
@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_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);
}
/** 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");
}
@Test
public void testKeepGoingPartiallyBadPackage() throws Exception {
scratch.file(
"x/y/BUILD",
"filegroup(name = 'a')",
"x = 1 // 0", // dynamic error
"filegroup(name = 'b')");
reporter.removeHandler(failFastHandler);
Pair<Set<Label>, Boolean> result = parseListKeepGoing("//x/...");
assertContainsEvent("division by zero");
// Execution stops at the first error,
// Subsequent rule statements are not executed,
// But thanks to --keep_going, we learn about the ones before the error.
assertThat(result.first).containsExactly(Label.parseAbsolute("//x/y:a", ImmutableMap.of()));
assertThat(result.second).isFalse();
}
@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'");
reporter.addHandler(failFastHandler);
// Even though there was a loading error in the package, parsing the target pattern was
// successful.
assertThat(result.second).isFalse();
}
@Test
public void testKeepGoingBadFilenameTarget() throws Exception {
assertKeepGoing(rulesBeneathFoo,
"no such target '//:bad/filename/target'",
"bad/filename/target", "foo/...");
}
@Test
public void testMoreThanOneBadPatternFailFast() throws Exception {
TargetParsingException e =
assertThrows(
TargetParsingException.class,
() ->
parseTargetPatternList(
parser,
parsingListener,
ImmutableList.of("bad/filename/target", "other/bad/filename/target"),
/*keepGoing=*/ false));
assertThat(e).hasMessageThat().contains("no such target");
}
@Test
public void testMentioningBuildFile() throws Exception {
ResolvedTargets<Target> result = parseTargetPatternList(parser, parsingListener,
Arrays.asList("//foo/bar/BUILD"), false);
assertThat(result.hasError()).isFalse();
assertThat(result.getTargets()).hasSize(1);
Label label = Iterables.getOnlyElement(result.getTargets()).getLabel();
assertThat(label.getName()).isEqualTo("BUILD");
assertThat(label.getPackageName()).isEqualTo("foo/bar");
}
/**
* 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', cmd='')");
Pair<Set<Label>, Boolean> result = parseListKeepGoing("//loading:y");
assertThat(result.first).containsExactly(Label.parseAbsolute("//loading:y", ImmutableMap.of()));
assertContainsEvent("missing value for mandatory attribute");
assertThat(result.second).isFalse();
}
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);
}
@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(PathFragment.create("h/i/j/BUILD")).build();
invalidate(modifiedFileSet);
assertThat(parseList("//h/..."))
.containsExactly(
Label.parseAbsolute("//h/i/j:j", ImmutableMap.of()),
Label.parseAbsolute("//h", ImmutableMap.of()));
}
@Test
public void testAddedFilesAndDotDotDot() throws Exception {
invalidate(ModifiedFileSet.EVERYTHING_MODIFIED);
reporter.removeHandler(failFastHandler);
scratch.dir("h");
assertThrows(TargetParsingException.class, () -> parseList("//h/..."));
scratch.file("h/i/j/k/BUILD", "sh_library(name='l')");
ModifiedFileSet modifiedFileSet = ModifiedFileSet.builder()
.modify(PathFragment.create("h"))
.modify(PathFragment.create("h/i"))
.modify(PathFragment.create("h/i/j"))
.modify(PathFragment.create("h/i/j/k"))
.modify(PathFragment.create("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", ImmutableMap.of()));
}
@Test
public void testBrokenSymlinkRepaired() throws Exception {
reporter.removeHandler(failFastHandler);
Path tuv = scratch.dir("t/u/v");
tuv.getChild("BUILD").createSymbolicLink(PathFragment.create("../../BUILD"));
assertThrows(TargetParsingException.class, () -> parseList("//t/..."));
scratch.file("t/BUILD", "sh_library(name='t')");
ModifiedFileSet modifiedFileSet = ModifiedFileSet.builder()
.modify(PathFragment.create("t/BUILD"))
.build();
invalidate(modifiedFileSet);
reporter.addHandler(failFastHandler);
Set<Label> result = parseList("//t/...");
assertThat(result)
.containsExactly(
Label.parseAbsolute("//t:t", ImmutableMap.of()),
Label.parseAbsolute("//t/u/v:t", ImmutableMap.of()));
}
@Test
public void testInfiniteTreeFromSymlinks() throws Exception {
reporter.removeHandler(failFastHandler);
Path ab = scratch.dir("a/b");
ab.getChild("c").createSymbolicLink(PathFragment.create("../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", ImmutableMap.of()));
}
@Test
public void testSymlinkCycle() throws Exception {
reporter.removeHandler(failFastHandler);
Path ab = scratch.dir("a/b");
ab.getChild("c").createSymbolicLink(PathFragment.create("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", ImmutableMap.of()));
}
@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(PathFragment.create("../../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(PathFragment.create("../../from-c"));
ResolvedTargets<Target> result = parseTargetPatternList(parser, parsingListener,
ImmutableList.of("//a/..."), true);
assertThat(targetsToLabels(result.getTargets()))
.containsExactly(
Label.parseAbsolute("//a/c/symlink:from-c", ImmutableMap.of()),
Label.parseAbsolute("//a/b/not-a-symlink:not-a-symlink", ImmutableMap.of()));
}
@Test
public void testDoesNotRecurseIntoSymlinksToOutputBase() throws Exception {
Path outputBaseBuildFile = outputBase.getRelative("execroot/workspace/test/BUILD");
scratch.file(outputBaseBuildFile.getPathString(), "filegroup(name='c')");
PathFragment targetFragment = outputBase.asFragment().getRelative("execroot/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", ImmutableMap.of()));
assertThat(result).doesNotContain(Label.parseAbsolute("//d/c:c", ImmutableMap.of()));
}
@Test
public void testExternalPackage() throws Exception {
parseList("external:all");
}
@Test
public void testTopLevelPackage_Relative_BuildFile() throws Exception {
Set<Label> result = parseList("BUILD");
assertThat(result).containsExactly(Label.parseAbsolute("//:BUILD", ImmutableMap.of()));
}
@Test
public void testTopLevelPackage_Relative_DeclaredTarget() throws Exception {
Set<Label> result = parseList("fg");
assertThat(result).containsExactly(Label.parseAbsolute("//:fg", ImmutableMap.of()));
}
@Test
public void testTopLevelPackage_Relative_All() throws Exception {
expectError("no such target '//:all'", "all");
}
@Test
public void testTopLevelPackage_Relative_ColonAll() throws Exception {
Set<Label> result = parseList(":all");
assertThat(result).containsExactly(Label.parseAbsolute("//:fg", ImmutableMap.of()));
}
@Test
public void testTopLevelPackage_Relative_InputFile() throws Exception {
Set<Label> result = parseList("foo.cc");
assertThat(result).containsExactly(Label.parseAbsolute("//:foo.cc", ImmutableMap.of()));
}
@Test
public void testTopLevelPackage_Relative_InputFile_NoSuchInputFile() throws Exception {
expectError("no such target '//:nope.cc'", "nope.cc");
}
@Test
public void testTopLevelPackage_Absolute_BuildFile() throws Exception {
Set<Label> result = parseList("//:BUILD");
assertThat(result).containsExactly(Label.parseAbsolute("//:BUILD", ImmutableMap.of()));
}
@Test
public void testTopLevelPackage_Absolute_DeclaredTarget() throws Exception {
Set<Label> result = parseList("//:fg");
assertThat(result).containsExactly(Label.parseAbsolute("//:fg", ImmutableMap.of()));
}
@Test
public void testTopLevelPackage_Absolute_All() throws Exception {
Set<Label> result = parseList("//:all");
assertThat(result).containsExactly(Label.parseAbsolute("//:fg", ImmutableMap.of()));
}
@Test
public void testTopLevelPackage_Absolute_InputFile() throws Exception {
Set<Label> result = parseList("//:foo.cc");
assertThat(result).containsExactly(Label.parseAbsolute("//:foo.cc", ImmutableMap.of()));
}
@Test
public void testTopLevelPackage_Absolute_InputFile_NoSuchInputFile() throws Exception {
expectError("no such target '//:nope.cc'", "//:nope.cc");
}
}