(Re)-Add tests for GenRule.
Reverts commit 4bf8cc30a572018ac27101396d18686b75ad1ab5.
With fix for https://github.com/bazelbuild/bazel/issues/2408.

--
PiperOrigin-RevId: 145544771
MOS_MIGRATED_REVID=145544771
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD
index faf0a0f..1bcbec9 100644
--- a/src/test/java/com/google/devtools/build/lib/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/BUILD
@@ -1024,8 +1024,10 @@
         "//src/main/java/com/google/devtools/build/lib:vfs",
         "//src/main/java/com/google/devtools/build/lib/actions",
         "//src/main/java/com/google/devtools/build/lib/cmdline",
+        "//src/main/java/com/google/devtools/build/lib/rules/cpp",
         "//src/main/protobuf:crosstool_config_java_proto",
         "//src/test/java/com/google/devtools/build/lib:actions_testutil",
+        "//src/test/java/com/google/devtools/build/lib:packages_testutil",
         "//third_party:guava",
         "//third_party:junit4",
         "//third_party:truth",
diff --git a/src/test/java/com/google/devtools/build/lib/bazel/rules/genrule/GenRuleCommandSubstitutionTest.java b/src/test/java/com/google/devtools/build/lib/bazel/rules/genrule/GenRuleCommandSubstitutionTest.java
new file mode 100644
index 0000000..a17c2eb
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/bazel/rules/genrule/GenRuleCommandSubstitutionTest.java
@@ -0,0 +1,478 @@
+// Copyright 2017 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.bazel.rules.genrule;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.base.Joiner;
+import com.google.devtools.build.lib.analysis.actions.SpawnAction;
+import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * A unit test of the various kinds of label and "Make"-variable substitutions that are applied to
+ * the genrule "cmd" attribute.
+ *
+ * <p>Some of these tests are similar to tests in LabelExpanderTest and MakeVariableExpanderTest,
+ * but this test case exercises the composition of these various transformations.
+ */
+@RunWith(JUnit4.class)
+public class GenRuleCommandSubstitutionTest extends BuildViewTestCase {
+
+  private static final Pattern SETUP_COMMAND_PATTERN =
+      Pattern.compile(".*/genrule-setup.sh;\\s+(?<command>.*)");
+
+  private String getGenruleCommand(String genrule) throws Exception {
+    return ((SpawnAction)
+            getGeneratingAction(getFilesToBuild(getConfiguredTarget(genrule)).iterator().next()))
+        .getArguments()
+        .get(2);
+  }
+
+  private void assertExpansionEquals(String expected, String genrule) throws Exception {
+    String command = getGenruleCommand(genrule);
+    assertCommandEquals(expected, command);
+  }
+
+  private void assertCommandEquals(String expected, String command) {
+    // Ensure the command after the genrule setup is correct.
+    Matcher m = SETUP_COMMAND_PATTERN.matcher(command);
+    if (m.matches()) {
+      command = m.group("command");
+    }
+
+    assertEquals(
+        "Expected command to be \"" + expected + "\", but found \"" + command + "\"",
+        expected,
+        command);
+  }
+
+  private void assertExpansionFails(String expectedErrorSuffix, String genrule) throws Exception {
+    reporter.removeHandler(failFastHandler); // we expect errors
+    eventCollector.clear();
+    getConfiguredTarget(genrule);
+    assertContainsEvent(expectedErrorSuffix);
+  }
+
+  // Creates a BUILD file defining a genrule called "//test" with no srcs or
+  // deps, one output and the specified command.
+  private void genrule(String command) throws Exception {
+    scratch.overwriteFile(
+        "test/BUILD",
+        "genrule(name = 'test',",
+        "        outs = ['out'],",
+        "        cmd = '" + command + "')");
+
+    // Since we're probably re-defining "//test":
+    invalidatePackages();
+  }
+
+  @Test
+  public void testLocationSyntaxErrors() throws Exception {
+    genrule("$(location )");
+    assertExpansionFails(
+        "invalid label in $(location) expression: empty package-relative label", "//test");
+
+    eventCollector.clear();
+
+    genrule("$(location foo bar");
+    assertExpansionFails("unterminated $(location) expression", "//test");
+
+    genrule("$(location");
+    assertExpansionFails("unterminated variable reference", "//test");
+
+    genrule("$(locationz");
+    assertExpansionFails("unterminated variable reference", "//test");
+
+    genrule("$(locationz)");
+    assertExpansionFails("$(locationz) not defined", "//test");
+
+    genrule("$(locationz )");
+    assertExpansionFails("$(locationz ) not defined", "//test");
+
+    genrule("$(locationz foo )");
+    assertExpansionFails("$(locationz foo ) not defined", "//test");
+  }
+
+  @Test
+  public void testLocationOfLabelThatIsNotAPrerequsite() throws Exception {
+    scratch.file(
+        "test/BUILD",
+        "exports_files(['exists'])",
+        "genrule(name = 'test1',",
+        "        outs = ['test1.out'],",
+        "        cmd = '$(location :exists)')",
+        "genrule(name = 'test2',",
+        "        outs = ['test2.out'],",
+        "        cmd = '$(location :doesnt_exist)')");
+
+    // $(location) of a non-prerequisite fails, even if the target exists:
+
+    assertExpansionFails(
+        "label '//test:exists' in $(location) expression is "
+            + "not a declared prerequisite of this rule",
+        "//test:test1");
+
+    assertExpansionFails(
+        "label '//test:doesnt_exist' in $(location) expression is "
+            + "not a declared prerequisite of this rule",
+        "//test:test2");
+  }
+
+  @Test
+  public void testLocationOfMultiFileLabel() throws Exception {
+    scratch.file(
+        "deuce/BUILD",
+        "genrule(name = 'deuce',",
+        "        outs = ['out.1', 'out.2'],",
+        "        cmd = ':')");
+    checkError(
+        "test",
+        "test1",
+        "label '//deuce:deuce' in $(location) expression expands to more than one "
+            + "file, please use $(locations //deuce:deuce) instead",
+        "genrule(name = 'test1',",
+        "        tools = ['//deuce'],",
+        "        outs = ['test1.out'],",
+        "        cmd = '$(location //deuce)')");
+  }
+
+  @Test
+  public void testUnknownVariable() throws Exception {
+    genrule("$(UNKNOWN)");
+    assertExpansionFails("$(UNKNOWN) not defined", "//test");
+  }
+
+  @Test
+  public void testLocationOfSourceLabel() throws Exception {
+    scratch.file(
+        "test1/BUILD",
+        "genrule(name = 'test1',",
+        "        srcs = ['src'],",
+        "        outs = ['out'],",
+        "        cmd = '$(location //test1:src)')");
+    assertExpansionEquals("test1/src", "//test1");
+
+    scratch.file(
+        "test2/BUILD",
+        "genrule(name = 'test2',",
+        "        srcs = ['src'],",
+        "        outs = ['out'],",
+        "        cmd = '$(location src)')");
+    assertExpansionEquals("test2/src", "//test2");
+
+    scratch.file(
+        "test3/BUILD",
+        "genrule(name = 'test3',",
+        "        srcs = ['src'],",
+        "        outs = ['out'],",
+        "        cmd = '$(location :src)')");
+    assertExpansionEquals("test3/src", "//test3");
+  }
+
+  @Test
+  public void testLocationOfOutputLabel() throws Exception {
+    String gendir = targetConfig.getMakeVariableDefault("GENDIR");
+    scratch.file(
+        "test1/BUILD",
+        "genrule(name = 'test1',",
+        "        outs = ['out'],",
+        "        cmd = '$(location //test1:out)')");
+    assertExpansionEquals(gendir + "/test1/out", "//test1");
+
+    scratch.file(
+        "test2/BUILD",
+        "genrule(name = 'test2',",
+        "        outs = ['out'],",
+        "        cmd = '$(location out)')");
+    assertExpansionEquals(gendir + "/test2/out", "//test2");
+
+    scratch.file(
+        "test3/BUILD",
+        "genrule(name = 'test3',",
+        "        outs = ['out'],",
+        "        cmd = '$(location out)')");
+    assertExpansionEquals(gendir + "/test3/out", "//test3");
+  }
+
+  @Test
+  public void testLocationsSyntaxErrors() throws Exception {
+    genrule("$(locations )");
+    assertExpansionFails(
+        "invalid label in $(locations) expression: empty package-relative label", "//test");
+
+    eventCollector.clear();
+
+    genrule("$(locations foo bar");
+    assertExpansionFails("unterminated $(locations) expression", "//test");
+
+    genrule("$(locations");
+    assertExpansionFails("unterminated variable reference", "//test");
+
+    genrule("$(locationsz");
+    assertExpansionFails("unterminated variable reference", "//test");
+
+    genrule("$(locationsz)");
+    assertExpansionFails("$(locationsz) not defined", "//test");
+
+    genrule("$(locationsz )");
+    assertExpansionFails("$(locationsz ) not defined", "//test");
+
+    genrule("$(locationsz foo )");
+    assertExpansionFails("$(locationsz foo ) not defined", "//test");
+  }
+
+  @Test
+  public void testLocationsOfLabelThatIsNotAPrerequsite() throws Exception {
+    scratch.file(
+        "test/BUILD",
+        "exports_files(['exists'])",
+        "genrule(name = 'test1',",
+        "        outs = ['test1.out'],",
+        "        cmd = '$(locations :exists)')",
+        "genrule(name = 'test2',",
+        "        outs = ['test2.out'],",
+        "        cmd = '$(locations :doesnt_exist)')");
+
+    // $(locations) of a non-prerequisite fails, even if the target exists:
+
+    assertExpansionFails(
+        "label '//test:exists' in $(locations) expression is "
+            + "not a declared prerequisite of this rule",
+        "//test:test1");
+
+    assertExpansionFails(
+        "label '//test:doesnt_exist' in $(locations) expression is "
+            + "not a declared prerequisite of this rule",
+        "//test:test2");
+  }
+
+  @Test
+  public void testLocationsOfMultiFileLabel() throws Exception {
+    String gendir = targetConfig.getMakeVariableDefault("GENDIR");
+    scratch.file(
+        "test/BUILD",
+        "genrule(name = 'x',",
+        "        srcs = ['src'],",
+        "        outs = ['out1', 'out2'],",
+        "        cmd = ':')",
+        "genrule(name = 'y',",
+        "        srcs = ['x'],",
+        "        outs = ['out'],",
+        "        cmd = '$(locations x)')");
+
+    assertExpansionEquals(gendir + "/test/out1 " + gendir + "/test/out2", "//test:y");
+  }
+
+  @Test
+  public void testLocationLocationsAndLabel() throws Exception {
+    String gendir = targetConfig.getMakeVariableDefault("GENDIR");
+    scratch.file(
+        "test/BUILD",
+        "genrule(name = 'x',",
+        "        srcs = ['src'],",
+        "        outs = ['out'],",
+        "        cmd = ':')",
+        "genrule(name = 'y',",
+        "        srcs = ['src'],",
+        "        outs = ['out1', 'out2'],",
+        "        cmd = ':')",
+        "genrule(name = 'r',",
+        "        srcs = ['x', 'y', 'z'],",
+        "        outs = ['res'],",
+        "        cmd = ' _ $(location x) _ $(locations y) _ ')");
+
+    String expected =
+        "_ " + gendir + "/test/out _ " + gendir + "/test/out1 " + gendir + "/test/out2 _ ";
+    assertExpansionEquals(expected, "//test:r");
+  }
+
+  @Test
+  public void testLocationsOfSourceLabel() throws Exception {
+    scratch.file(
+        "test1/BUILD",
+        "genrule(name = 'test1',",
+        "        srcs = ['src'],",
+        "        outs = ['out'],",
+        "        cmd = '$(locations //test1:src)')");
+    assertExpansionEquals("test1/src", "//test1");
+
+    scratch.file(
+        "test2/BUILD",
+        "genrule(name = 'test2',",
+        "        srcs = ['src'],",
+        "        outs = ['out'],",
+        "        cmd = '$(locations src)')");
+    assertExpansionEquals("test2/src", "//test2");
+
+    scratch.file(
+        "test3/BUILD",
+        "genrule(name = 'test3',",
+        "        srcs = ['src'],",
+        "        outs = ['out'],",
+        "        cmd = '$(location :src)')");
+    assertExpansionEquals("test3/src", "//test3");
+  }
+
+  @Test
+  public void testLocationsOfOutputLabel() throws Exception {
+    String gendir = targetConfig.getMakeVariableDefault("GENDIR");
+    scratch.file(
+        "test1/BUILD",
+        "genrule(name = 'test1',",
+        "        outs = ['out'],",
+        "        cmd = '$(locations //test1:out)')");
+    assertExpansionEquals(gendir + "/test1/out", "//test1");
+
+    scratch.file(
+        "test2/BUILD",
+        "genrule(name = 'test2',",
+        "        outs = ['out'],",
+        "        cmd = '$(locations out)')");
+    assertExpansionEquals(gendir + "/test2/out", "//test2");
+
+    scratch.file(
+        "test3/BUILD",
+        "genrule(name = 'test3',",
+        "        outs = ['out'],",
+        "        cmd = '$(locations out)')");
+    assertExpansionEquals(gendir + "/test3/out", "//test3");
+  }
+
+  @Test
+  public void testOuts() throws Exception {
+    String expected = targetConfig.getMakeVariableDefault("GENDIR") + "/test/out";
+    scratch.file(
+        "test/BUILD",
+        "genrule(name = 'test',",
+        "        outs = ['out'],",
+        "        cmd = '$(OUTS) # $@')");
+    assertExpansionEquals(expected + " # " + expected, "//test");
+  }
+
+  @Test
+  public void testSrcs() throws Exception {
+    String expected = "test/src";
+
+    scratch.file(
+        "test/BUILD",
+        "genrule(name = 'test',",
+        "        srcs = ['src'],",
+        "        outs = ['out'],",
+        "        cmd = '$(SRCS) # $<')");
+    assertExpansionEquals(expected + " # " + expected, "//test");
+  }
+
+  @Test
+  public void testDollarDollar() throws Exception {
+    scratch.file(
+        "test/BUILD",
+        "genrule(name = 'test',",
+        "        outs = ['out'],",
+        "        cmd = '$$DOLLAR')");
+    assertExpansionEquals("$DOLLAR", "//test");
+  }
+
+  @Test
+  public void testDollarLessThanWithZeroInputs() throws Exception {
+    scratch.file(
+        "test/BUILD",
+        "genrule(name = 'test',",
+        "        outs = ['out'],",
+        "        cmd  = '$<')");
+    assertExpansionFails("variable '$<' : no input file", "//test");
+  }
+
+  @Test
+  public void testDollarLessThanWithMultipleInputs() throws Exception {
+    scratch.file(
+        "test/BUILD",
+        "genrule(name = 'test',",
+        "        srcs = ['src1', 'src2'],",
+        "        outs = ['out'],",
+        "        cmd  = '$<')");
+    assertExpansionFails("variable '$<' : more than one input file", "//test");
+  }
+
+  @Test
+  public void testDollarAtWithMultipleOutputs() throws Exception {
+    scratch.file(
+        "test/BUILD",
+        "genrule(name = 'test',",
+        "        outs = ['out.1', 'out.2'],",
+        "        cmd  = '$@')");
+    assertExpansionFails("variable '$@' : more than one output file", "//test");
+  }
+
+  @Test
+  public void testDollarAtWithZeroOutputs() throws Exception {
+    scratch.file(
+        "test/BUILD",
+        "genrule(name = 'test',",
+        "        srcs = ['src1', 'src2'],",
+        "        outs = [],",
+        "        cmd  = '$@')");
+    assertExpansionFails("Genrules without outputs don't make sense", "//test");
+  }
+
+  @Test
+  public void testShellVariables() throws Exception {
+    genrule("for file in a b c;do echo $$file;done");
+    assertExpansionEquals("for file in a b c;do echo $file;done", "//test");
+    assertNoEvents();
+
+    genrule("$${file%:.*8}");
+    assertExpansionEquals("${file%:.*8}", "//test");
+    assertNoEvents();
+
+    genrule("$$(basename file)");
+    assertExpansionEquals("$(basename file)", "//test");
+    assertNoEvents();
+
+    genrule("$(basename file)");
+    assertExpansionFails("$(basename file) not defined", "//test");
+    assertContainsEvent("$(basename file) not defined");
+  }
+
+  @Test
+  public void testDollarFileFails() throws Exception {
+    checkError(
+        "test",
+        "test",
+        "'$file' syntax is not supported; use '$(file)' ",
+        getBuildFileWithCommand("for file in a b c;do echo $file;done"));
+  }
+
+  @Test
+  public void testDollarFile2Fails() throws Exception {
+    checkError(
+        "test",
+        "test",
+        "'${file%:.*8}' syntax is not supported; use '$(file%:.*8)' ",
+        getBuildFileWithCommand("${file%:.*8}"));
+  }
+
+  private String getBuildFileWithCommand(String command) {
+    return Joiner.on("\n")
+        .join(
+            "genrule(name = 'test',",
+            "        outs = ['out'],",
+            "        cmd = '" + command + "')");
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/bazel/rules/genrule/GenRuleConfiguredTargetTest.java b/src/test/java/com/google/devtools/build/lib/bazel/rules/genrule/GenRuleConfiguredTargetTest.java
new file mode 100644
index 0000000..b83428a
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/bazel/rules/genrule/GenRuleConfiguredTargetTest.java
@@ -0,0 +1,669 @@
+// Copyright 2017 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.bazel.rules.genrule;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.devtools.build.lib.testutil.TestConstants.GENRULE_SETUP;
+import static com.google.devtools.build.lib.testutil.TestConstants.GENRULE_SETUP_PATH;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import com.google.devtools.build.lib.actions.Action;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.FileConfiguredTarget;
+import com.google.devtools.build.lib.analysis.actions.SpawnAction;
+import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
+import com.google.devtools.build.lib.analysis.util.AnalysisMock;
+import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.rules.cpp.CppConfiguration;
+import com.google.devtools.build.lib.rules.java.Jvm;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests of {@link BazelGenRule}. */
+@RunWith(JUnit4.class)
+public class GenRuleConfiguredTargetTest extends BuildViewTestCase {
+
+  /** Filter to remove implicit dependencies of C/C++ rules. */
+  private static final Predicate<ConfiguredTarget> CC_CONFIGURED_TARGET_FILTER =
+      new Predicate<ConfiguredTarget>() {
+        @Override
+        public boolean apply(ConfiguredTarget target) {
+          return AnalysisMock.get().ccSupport().labelFilter().apply(target.getLabel());
+        }
+      };
+
+  /** Filter to remove implicit dependencies of Java rules. */
+  private static final Predicate<ConfiguredTarget> JAVA_CONFIGURED_TARGET_FILTER =
+      new Predicate<ConfiguredTarget>() {
+        @Override
+        public boolean apply(ConfiguredTarget target) {
+          Label label = target.getLabel();
+          String labelName = "//" + label.getPackageName();
+          return !labelName.startsWith("//third_party/java/jdk");
+        }
+      };
+
+  private static final Pattern SETUP_COMMAND_PATTERN =
+      Pattern.compile(".*/genrule-setup.sh;\\s+(?<command>.*)");
+
+  private void assertCommandEquals(String expected, String command) {
+    // Ensure the command after the genrule setup is correct.
+    Matcher m = SETUP_COMMAND_PATTERN.matcher(command);
+    if (m.matches()) {
+      command = m.group("command");
+    }
+
+    assertThat(command).isEqualTo(expected);
+  }
+
+  public void createFiles() throws Exception {
+    scratch.file(
+        "hello/BUILD",
+        "genrule(",
+        "    name = 'z',",
+        "    outs = ['x/y'],",
+        "    cmd = 'echo hi > $(@D)/y',",
+        ")",
+        "genrule(",
+        "    name = 'w',",
+        "    outs = ['a/b', 'c/d'],",
+        "    cmd = 'echo hi | tee $(@D)/a/b $(@D)/c/d',",
+        ")");
+  }
+
+  @Test
+  public void testD() throws Exception {
+    createFiles();
+    ConfiguredTarget z = getConfiguredTarget("//hello:z");
+    Artifact y = getOnlyElement(getFilesToBuild(z));
+    assertEquals(new PathFragment("hello/x/y"), y.getRootRelativePath());
+  }
+
+  @Test
+  public void testDMultiOutput() throws Exception {
+    createFiles();
+    ConfiguredTarget z = getConfiguredTarget("//hello:w");
+    List<Artifact> files = getFilesToBuild(z).toList();
+    assertThat(files).hasSize(2);
+    assertEquals(new PathFragment("hello/a/b"), files.get(0).getRootRelativePath());
+    assertEquals(new PathFragment("hello/c/d"), files.get(1).getRootRelativePath());
+  }
+
+  @Test
+  public void testOutsWithSameNameAsRule() throws Exception {
+    // The error was demoted to a warning.
+    // Re-enable after June 1 2008 when we make it an error again.
+    checkWarning(
+        "genrule2",
+        "hello_world",
+        "target 'hello_world' is both a rule and a file;",
+        "genrule(name = 'hello_world',",
+        "srcs = ['ignore_me.txt'],",
+        "outs = ['message.txt', 'hello_world'],",
+        "cmd  = 'echo \"Hello, world.\" >$(location message.txt)')");
+  }
+
+  @Test
+  public void testFilesToBuildIsOuts() throws Exception {
+    scratch.file(
+        "genrule1/BUILD",
+        "genrule(name = 'hello_world',",
+        "srcs = ['ignore_me.txt'],",
+        "outs = ['message.txt'],",
+        "cmd  = 'echo \"Hello, world.\" >$(location message.txt)')");
+    Artifact messageArtifact = getFileConfiguredTarget("//genrule1:message.txt").getArtifact();
+    assertThat(getFilesToBuild(getConfiguredTarget("//genrule1:hello_world")))
+        .containsExactly(messageArtifact);
+  }
+
+  @Test
+  public void testActionIsShellCommand() throws Exception {
+    scratch.file(
+        "genrule1/BUILD",
+        "genrule(name = 'hello_world',",
+        "srcs = ['ignore_me.txt'],",
+        "outs = ['message.txt'],",
+        "cmd  = 'echo \"Hello, world.\" >$(location message.txt)')");
+
+    Artifact messageArtifact = getFileConfiguredTarget("//genrule1:message.txt").getArtifact();
+    SpawnAction shellAction = (SpawnAction) getGeneratingAction(messageArtifact);
+
+    Artifact ignoreMeArtifact = getFileConfiguredTarget("//genrule1:ignore_me.txt").getArtifact();
+    Artifact genruleSetupArtifact = getFileConfiguredTarget(GENRULE_SETUP).getArtifact();
+
+    assertNotNull(shellAction);
+    assertEquals(
+        Sets.newHashSet(ignoreMeArtifact, genruleSetupArtifact),
+        Sets.newHashSet(shellAction.getInputs()));
+    assertEquals(Sets.newHashSet(messageArtifact), Sets.newHashSet(shellAction.getOutputs()));
+
+    String expected = "echo \"Hello, world.\" >" + messageArtifact.getExecPathString();
+    assertEquals(
+        targetConfig.getShellExecutable().getPathString(), shellAction.getArguments().get(0));
+    assertEquals("-c", shellAction.getArguments().get(1));
+    assertCommandEquals(expected, shellAction.getArguments().get(2));
+  }
+
+  @Test
+  public void testDependentGenrule() throws Exception {
+    scratch.file(
+        "genrule1/BUILD",
+        "genrule(name = 'hello_world',",
+        "srcs = ['ignore_me.txt'],",
+        "outs = ['message.txt'],",
+        "cmd  = 'echo \"Hello, world.\" >$(location message.txt)')");
+    scratch.file(
+        "genrule2/BUILD",
+        "genrule(name = 'goodbye_world',",
+        "srcs = ['goodbye.txt', '//genrule1:hello_world'],",
+        "outs = ['farewell.txt'],",
+        "cmd  = 'echo $(SRCS) >$(location farewell.txt)')");
+
+    getConfiguredTarget("//genrule2:goodbye_world");
+
+    Artifact farewellArtifact = getFileConfiguredTarget("//genrule2:farewell.txt").getArtifact();
+    Artifact goodbyeArtifact = getFileConfiguredTarget("//genrule2:goodbye.txt").getArtifact();
+    Artifact messageArtifact = getFileConfiguredTarget("//genrule1:message.txt").getArtifact();
+    Artifact genruleSetupArtifact = getFileConfiguredTarget(GENRULE_SETUP).getArtifact();
+
+    SpawnAction shellAction = (SpawnAction) getGeneratingAction(farewellArtifact);
+
+    // inputs = { "goodbye.txt", "//genrule1:message.txt" }
+    assertEquals(
+        Sets.newHashSet(goodbyeArtifact, messageArtifact, genruleSetupArtifact),
+        Sets.newHashSet(shellAction.getInputs()));
+
+    // outputs = { "farewell.txt" }
+    assertEquals(Sets.newHashSet(farewellArtifact), Sets.newHashSet(shellAction.getOutputs()));
+
+    String expected =
+        "echo "
+            + goodbyeArtifact.getExecPathString()
+            + " "
+            + messageArtifact.getExecPathString()
+            + " >"
+            + farewellArtifact.getExecPathString();
+    assertCommandEquals(expected, shellAction.getArguments().get(2));
+  }
+
+  /**
+   * Ensure that the actions / artifacts created by genrule dependencies allow us to follow the
+   * chain of generated files backward.
+   */
+  @Test
+  public void testDependenciesViaFiles() throws Exception {
+    scratch.file(
+        "foo/BUILD",
+        "genrule(name = 'bar',",
+        "        srcs = ['bar_in.txt'],",
+        "        cmd = 'touch $(OUTS)',",
+        "        outs = ['bar_out.txt'])",
+        "genrule(name = 'baz',",
+        "        srcs = ['bar_out.txt'],",
+        "        cmd = 'touch $(OUTS)',",
+        "        outs = ['baz_out.txt'])");
+
+    FileConfiguredTarget bazOutTarget = getFileConfiguredTarget("//foo:baz_out.txt");
+    Action bazAction = getGeneratingAction(bazOutTarget.getArtifact());
+    Artifact barOut = bazAction.getInputs().iterator().next();
+    assertTrue(barOut.getExecPath().endsWith(new PathFragment("foo/bar_out.txt")));
+    Action barAction = getGeneratingAction(barOut);
+    Artifact barIn = barAction.getInputs().iterator().next();
+    assertTrue(barIn.getExecPath().endsWith(new PathFragment("foo/bar_in.txt")));
+  }
+
+  /** Ensure that variable $(@D) gets expanded correctly in the genrule cmd. */
+  @Test
+  public void testOutputDirExpansion() throws Exception {
+    scratch.file(
+        "foo/BUILD",
+        "genrule(name = 'bar',",
+        "        srcs = ['bar_in.txt'],",
+        "        cmd = 'touch $(@D)',",
+        "        outs = ['bar/bar_out.txt'])",
+        "genrule(name = 'baz',",
+        "        srcs = ['bar/bar_out.txt'],",
+        "        cmd = 'touch $(@D)',",
+        "        outs = ['logs/baz_out.txt', 'logs/baz.log'])");
+
+    getConfiguredTarget("//foo:bar");
+
+    FileConfiguredTarget bazOutTarget = getFileConfiguredTarget("//foo:logs/baz_out.txt");
+
+    SpawnAction bazAction = (SpawnAction) getGeneratingAction(bazOutTarget.getArtifact());
+
+    // Make sure the expansion for $(@D) results in the
+    // directory of the BUILD file ("foo"), not the common parent
+    // directory of the output files ("logs")
+    String bazExpected =
+        "touch "
+            + bazOutTarget
+                .getArtifact()
+                .getExecPath()
+                .getParentDirectory()
+                .getParentDirectory()
+                .getPathString();
+    assertCommandEquals(bazExpected, bazAction.getArguments().get(2));
+    assertThat(bazAction.getArguments().get(2)).endsWith("/foo");
+
+    getConfiguredTarget("//foo:bar");
+
+    Artifact barOut = bazAction.getInputs().iterator().next();
+    assertTrue(barOut.getExecPath().endsWith(new PathFragment("foo/bar/bar_out.txt")));
+    SpawnAction barAction = (SpawnAction) getGeneratingAction(barOut);
+    String barExpected = "touch " + barOut.getExecPath().getParentDirectory().getPathString();
+    assertCommandEquals(barExpected, barAction.getArguments().get(2));
+    assertFalse(bazExpected.equals(barExpected));
+  }
+
+  /** Ensure that variable $(CC) gets expanded correctly in the genrule cmd. */
+  @Test
+  public void testMakeVarExpansion() throws Exception {
+    scratch.file(
+        "foo/BUILD",
+        "genrule(name = 'bar',",
+        "        srcs = ['bar.cc'],",
+        "        cmd = '$(CC) -o $(OUTS) $(SRCS) $$shellvar',",
+        "        outs = ['bar.o'])");
+    FileConfiguredTarget barOutTarget = getFileConfiguredTarget("//foo:bar.o");
+    FileConfiguredTarget barInTarget = getFileConfiguredTarget("//foo:bar.cc");
+
+    SpawnAction barAction = (SpawnAction) getGeneratingAction(barOutTarget.getArtifact());
+
+    String cc = "" + targetConfig.getFragment(CppConfiguration.class).getCppExecutable();
+    String expected =
+        cc
+            + " -o "
+            + barOutTarget.getArtifact().getExecPathString()
+            + " "
+            + barInTarget.getArtifact().getRootRelativePath().getPathString()
+            + " $shellvar";
+    assertCommandEquals(expected, barAction.getArguments().get(2));
+  }
+
+  /** Ensure that Java make variables get expanded under the *host* configuration. */
+  @Test
+  public void testJavaMakeVarExpansion() throws Exception {
+    String ruleTemplate =
+        "genrule(name = '%s',"
+            + "  srcs = [],"
+            + "  cmd = 'echo $(%s) > $@',"
+            + "  outs = ['%s'])";
+
+    scratch.file(
+        "foo/BUILD",
+        String.format(ruleTemplate, "java_rule", "JAVA", "java.txt"),
+        String.format(ruleTemplate, "javabase_rule", "JAVABASE", "javabase.txt"));
+
+    Artifact javaOutput = getFileConfiguredTarget("//foo:java.txt").getArtifact();
+    Artifact javabaseOutput = getFileConfiguredTarget("//foo:javabase.txt").getArtifact();
+
+    String expectedPattern = "echo %s > %s";
+
+    BuildConfiguration hostConfig = getHostConfiguration();
+    String expectedJava = hostConfig.getFragment(Jvm.class).getJavaExecutable().getPathString();
+    String expectedJavabase = hostConfig.getFragment(Jvm.class).getJavaHome().getPathString();
+
+    assertCommandEquals(
+        String.format(expectedPattern, expectedJava, javaOutput.getExecPathString()),
+        ((SpawnAction) getGeneratingAction(javaOutput)).getArguments().get(2));
+    assertCommandEquals(
+        String.format(expectedPattern, expectedJavabase, javabaseOutput.getExecPathString()),
+        ((SpawnAction) getGeneratingAction(javabaseOutput)).getArguments().get(2));
+  }
+
+  // Returns the expansion of 'cmd' for the specified genrule.
+  private String getCommand(String label) throws Exception {
+    return getSpawnAction(label).getArguments().get(2);
+  }
+
+  // Returns the SpawnAction for the specified genrule.
+  private SpawnAction getSpawnAction(String label) throws Exception {
+    return (SpawnAction)
+        getGeneratingAction(getFilesToBuild(getConfiguredTarget(label)).iterator().next());
+  }
+
+  @Test
+  public void testMessage() throws Exception {
+    scratch.file(
+        "genrule3/BUILD",
+        "genrule(name = 'hello_world',",
+        "    srcs = ['ignore_me.txt'],",
+        "    outs = ['hello.txt'],",
+        "    cmd  = 'echo \"Hello, world.\" >hello.txt')",
+        "genrule(name = 'goodbye_world',",
+        "    srcs = ['ignore_me.txt'],",
+        "    outs = ['goodbye.txt'],",
+        "    message = 'Generating message',",
+        "    cmd  = 'echo \"Goodbye, world.\" >goodbye.txt')");
+    assertEquals(
+        "Executing genrule //genrule3:hello_world",
+        getSpawnAction("//genrule3:hello_world").getProgressMessage());
+    assertEquals(
+        "Generating message //genrule3:goodbye_world",
+        getSpawnAction("//genrule3:goodbye_world").getProgressMessage());
+  }
+
+  /** Ensure that labels from binary targets expand to the executable */
+  @Test
+  public void testBinaryTargetsExpandToExecutable() throws Exception {
+    scratch.file(
+        "genrule3/BUILD",
+        "genrule(name = 'hello_world',",
+        "    srcs = ['ignore_me.txt'],",
+        "    tools = ['echo'],",
+        "    outs = ['message.txt'],",
+        "    cmd  = '$(location :echo) \"Hello, world.\" >message.txt')",
+        "cc_binary(name = 'echo',",
+        "    srcs = ['echo.cc'])");
+    String regex = "b.{4}-out/.*/bin/genrule3/echo(\\.exe)? \"Hello, world.\" >message.txt";
+    assertThat(getCommand("//genrule3:hello_world")).containsMatch(regex);
+  }
+
+  @Test
+  public void testOutputToBindir() throws Exception {
+    scratch.file(
+        "x/BUILD",
+        "genrule(name='bin', ",
+        "        outs=['bin.out'],",
+        "        cmd=':',",
+        "        output_to_bindir=1)",
+        "genrule(name='genfiles', ",
+        "        outs=['genfiles.out'],",
+        "        cmd=':',",
+        "        output_to_bindir=0)");
+
+    assertEquals(
+        getBinArtifact("bin.out", "//x:bin"), getFileConfiguredTarget("//x:bin.out").getArtifact());
+    assertEquals(
+        getGenfilesArtifact("genfiles.out", "//x:genfiles"),
+        getFileConfiguredTarget("//x:genfiles.out").getArtifact());
+  }
+
+  @Test
+  public void testMultipleOutputsToBindir() throws Exception {
+    scratch.file(
+        "x/BUILD",
+        "genrule(name='bin', ",
+        "        outs=['bin_a.out', 'bin_b.out'],",
+        "        cmd=':',",
+        "        output_to_bindir=1)",
+        "genrule(name='genfiles', ",
+        "        outs=['genfiles_a.out', 'genfiles_b.out'],",
+        "        cmd=':',",
+        "        output_to_bindir=0)");
+
+    assertEquals(
+        getBinArtifact("bin_a.out", "//x:bin"),
+        getFileConfiguredTarget("//x:bin_a.out").getArtifact());
+    assertEquals(
+        getBinArtifact("bin_b.out", "//x:bin"),
+        getFileConfiguredTarget("//x:bin_b.out").getArtifact());
+    assertEquals(
+        getGenfilesArtifact("genfiles_a.out", "//x:genfiles"),
+        getFileConfiguredTarget("//x:genfiles_a.out").getArtifact());
+    assertEquals(
+        getGenfilesArtifact("genfiles_b.out", "//x:genfiles"),
+        getFileConfiguredTarget("//x:genfiles_b.out").getArtifact());
+  }
+
+  @Test
+  public void testMultipleOutsPreservesOrdering() throws Exception {
+    scratch.file(
+        "multiple/outs/BUILD",
+        "genrule(name='test', ",
+        "        outs=['file1.out', 'file2.out'],",
+        "        cmd='touch $(OUTS)')");
+    String regex =
+        "touch b.{4}-out/.*/genfiles/multiple/outs/file1.out "
+            + "b.{4}-out/.*/genfiles/multiple/outs/file2.out";
+    assertThat(getCommand("//multiple/outs:test")).containsMatch(regex);
+  }
+
+  @Test
+  public void testToolsAreHostConfiguration() throws Exception {
+    scratch.file(
+        "config/BUILD",
+        "genrule(name='src', outs=['src.out'], cmd=':')",
+        "genrule(name='tool', outs=['tool.out'], cmd=':')",
+        "genrule(name='config', ",
+        "        srcs=[':src'], tools=[':tool'], outs=['out'],",
+        "        cmd='$(location :tool)')");
+
+    Iterable<ConfiguredTarget> prereqs =
+        Iterables.filter(
+            Iterables.filter(
+                getDirectPrerequisites(getConfiguredTarget("//config")),
+                CC_CONFIGURED_TARGET_FILTER),
+            JAVA_CONFIGURED_TARGET_FILTER);
+
+    for (ConfiguredTarget prereq : prereqs) {
+      String name = prereq.getLabel().getName();
+      switch (name) {
+        case "src":
+          assertConfigurationsEqual(getTargetConfiguration(), prereq.getConfiguration());
+          break;
+        case "tool":
+          assertTrue(getHostConfiguration().equalsOrIsSupersetOf(prereq.getConfiguration()));
+          break;
+        case GENRULE_SETUP_PATH:
+          assertNull(prereq.getConfiguration());
+          break;
+        default:
+          fail("unexpected prerequisite " + prereq);
+      }
+    }
+    if (Iterables.size(prereqs) != 3) {
+      fail("Expected 3 prerequisites, got: " + prereqs);
+    }
+  }
+
+  @Test
+  public void testLabelsContainingAtDAreExpanded() throws Exception {
+    scratch.file(
+        "p/BUILD",
+        "genrule(name='gen', ",
+        "        tools=['p'],",
+        "        outs=['out'],",
+        "        cmd='echo $(@D)')");
+    String regex = "echo b.{4}-out/.*/genfiles/p";
+    assertThat(getCommand("//p:gen")).containsMatch(regex);
+  }
+
+  @Test
+  public void testGetExecutable() throws Exception {
+    ConfiguredTarget turtle =
+        scratchConfiguredTarget(
+            "java/com/google/turtle",
+            "turtle_bootstrap",
+            "genrule(name = 'turtle_bootstrap',",
+            "    srcs = ['Turtle.java'],",
+            "    outs = ['turtle'],",
+            "    executable = 1,",
+            "    cmd = 'touch $(OUTS)')");
+    assertEquals("turtle", getExecutable(turtle).getExecPath().getBaseName());
+  }
+
+  @Test
+  public void testGetExecutableForNonExecutableOut() throws Exception {
+    ConfiguredTarget turtle =
+        scratchConfiguredTarget(
+            "java/com/google/turtle",
+            "turtle_bootstrap",
+            "genrule(name = 'turtle_bootstrap',",
+            "    srcs = ['Turtle.java'],",
+            "    outs = ['debugdata.txt'],",
+            "    cmd = 'touch $(OUTS)')");
+    assertNull(getExecutable(turtle));
+  }
+
+  @Test
+  public void testGetExecutableForMultipleOuts() throws Exception {
+    ConfiguredTarget turtle =
+        scratchConfiguredTarget(
+            "java/com/google/turtle",
+            "turtle_bootstrap",
+            "genrule(name = 'turtle_bootstrap',",
+            "    srcs = ['Turtle.java'],",
+            "    outs = ['turtle', 'debugdata.txt'],",
+            "    cmd = 'touch $(OUTS)')");
+    assertNull(getExecutable(turtle));
+  }
+
+  @Test
+  public void testGetExecutableFailsForMultipleOutputs() throws Exception {
+    // Multiple output files are invalid when executable=1.
+    checkError(
+        "bad",
+        "bad",
+        "in executable attribute of genrule rule //bad:bad: "
+            + "if genrules produce executables, they are allowed only one output. "
+            + "If you need the executable=1 argument, then you should split this genrule into "
+            + "genrules producing single outputs",
+        "genrule(name = 'bad',",
+        "        outs = [ 'bad_out1', 'bad_out2' ],",
+        "        executable = 1,",
+        "        cmd = 'touch $(OUTS)')");
+  }
+
+  @Test
+  public void testEmptyOutsError() throws Exception {
+    checkError(
+        "x",
+        "x",
+        "Genrules without outputs don't make sense",
+        "genrule(name = 'x', outs = [], cmd='echo')");
+  }
+
+  @Test
+  public void testGenruleSetup() throws Exception {
+    scratch.file(
+        "foo/BUILD",
+        "genrule(name = 'foo_sh',",
+        "        outs = [ 'foo.sh' ],", // Shell script files are known to be executable.
+        "        cmd = 'touch $@')");
+
+    assertThat(getCommand("//foo:foo_sh")).contains(GENRULE_SETUP_PATH);
+  }
+
+  private void createStampingTargets() throws Exception {
+    scratch.file(
+        "u/BUILD",
+        "genrule(name='foo_stamp', srcs=[], outs=['uu'], stamp=1, cmd='')",
+        "genrule(name='foo_nostamp', srcs=[], outs=['vv'], stamp=0, cmd='')",
+        "genrule(name='foo_default', srcs=[], outs=['xx'], cmd='')");
+  }
+
+  private void assertStamped(String target) throws Exception {
+    assertStamped(getConfiguredTarget(target));
+  }
+
+  private void assertNotStamped(String target) throws Exception {
+    assertNotStamped(getConfiguredTarget(target));
+  }
+
+  private void assertStamped(ConfiguredTarget target) throws Exception {
+    Artifact out = Iterables.getFirst(getFilesToBuild(target), null);
+    List<String> inputs = ActionsTestUtil.baseArtifactNames(getGeneratingAction(out).getInputs());
+    assertThat(inputs).containsAllIn(ImmutableList.of("build-info.txt", "build-changelist.txt"));
+  }
+
+  private void assertNotStamped(ConfiguredTarget target) throws Exception {
+    Artifact out = Iterables.getFirst(getFilesToBuild(target), null);
+    List<String> inputs = ActionsTestUtil.baseArtifactNames(getGeneratingAction(out).getInputs());
+    assertThat(inputs).doesNotContain("build-info.txt");
+    assertThat(inputs).doesNotContain("build-changelist.txt");
+  }
+
+  @Test
+  public void testStampingWithNoStamp() throws Exception {
+    useConfiguration("--nostamp");
+    createStampingTargets();
+    assertStamped("//u:foo_stamp");
+    assertStamped(getHostConfiguredTarget("//u:foo_stamp"));
+    assertNotStamped("//u:foo_nostamp");
+    assertNotStamped(getHostConfiguredTarget("//u:foo_nostamp"));
+    assertNotStamped("//u:foo_default");
+  }
+
+  @Test
+  public void testStampingWithStamp() throws Exception {
+    useConfiguration("--stamp");
+    createStampingTargets();
+    assertStamped("//u:foo_stamp");
+    assertStamped(getHostConfiguredTarget("//u:foo_stamp"));
+    //assertStamped("//u:foo_nostamp");
+    assertNotStamped(getHostConfiguredTarget("//u:foo_nostamp"));
+    assertNotStamped("//u:foo_default");
+  }
+
+  @Test
+  public void testRequiresDarwin() throws Exception {
+    scratch.file(
+        "foo/BUILD",
+        "genrule(name='darwin', srcs=[], outs=['macout'], cmd='', tags=['requires-darwin'])");
+
+    SpawnAction action = getSpawnAction("//foo:darwin");
+    assertThat(action.getExecutionInfo().keySet()).contains("requires-darwin");
+    // requires-darwin causes /bin/bash to be hard-coded, see CommandHelper.shellPath().
+    assertThat(action.getCommandFilename())
+        .isEqualTo("/bin/bash");
+  }
+
+  @Test
+  public void testJarError() throws Exception {
+    checkError(
+        "foo",
+        "grj",
+        "in cmd attribute of genrule rule //foo:grj: $(JAR) not defined",
+        "genrule(name='grj',"
+            + "      srcs = [],"
+            + "      outs=['grj'],"
+            + "      cmd='$(JAR) foo bar')");
+  }
+
+  /** Regression test for b/15589451. */
+  @Test
+  public void testDuplicateLocalFlags() throws Exception {
+    scratch.file(
+        "foo/BUILD",
+        "genrule(name='g',"
+            + "      srcs = [],"
+            + "      outs = ['grj'],"
+            + "      cmd ='echo g',"
+            + "      local = 1,"
+            + "      tags = ['local'])");
+    getConfiguredTarget("//foo:g");
+    assertNoEvents();
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/bazel/rules/genrule/GenruleConfiguredTargetTest.java b/src/test/java/com/google/devtools/build/lib/bazel/rules/genrule/GenruleConfiguredTargetTest.java
deleted file mode 100644
index 00c8115..0000000
--- a/src/test/java/com/google/devtools/build/lib/bazel/rules/genrule/GenruleConfiguredTargetTest.java
+++ /dev/null
@@ -1,68 +0,0 @@
-// 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.bazel.rules.genrule;
-
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.assertEquals;
-
-import com.google.devtools.build.lib.actions.Artifact;
-import com.google.devtools.build.lib.analysis.ConfiguredTarget;
-import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
-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.List;
-
-/**
- * Unit tests for {@link GenRule}.
- */
-@RunWith(JUnit4.class)
-public class GenruleConfiguredTargetTest extends BuildViewTestCase {
-  @Before
-  public void createFiles() throws Exception {
-    scratch.file("hello/BUILD",
-        "genrule(",
-        "    name = 'z',",
-        "    outs = ['x/y'],",
-        "    cmd = 'echo hi > $(@D)/y',",
-        ")",
-        "genrule(",
-        "    name = 'w',",
-        "    outs = ['a/b', 'c/d'],",
-        "    cmd = 'echo hi | tee $(@D)/a/b $(@D)/c/d',",
-        ")");
-  }
-
-  @Test
-  public void testD() throws Exception {
-    ConfiguredTarget z = getConfiguredTarget("//hello:z");
-    Artifact y = getOnlyElement(getFilesToBuild(z));
-    assertEquals(new PathFragment("hello/x/y"), y.getRootRelativePath());
-  }
-
-  @Test
-  public void testDMultiOutput() throws Exception {
-    ConfiguredTarget z = getConfiguredTarget("//hello:w");
-    List<Artifact> files = getFilesToBuild(z).toList();
-    assertThat(files).hasSize(2);
-    assertEquals(new PathFragment("hello/a/b"), files.get(0).getRootRelativePath());
-    assertEquals(new PathFragment("hello/c/d"), files.get(1).getRootRelativePath());
-  }
-}
diff --git a/src/test/java/com/google/devtools/build/lib/testutil/TestConstants.java b/src/test/java/com/google/devtools/build/lib/testutil/TestConstants.java
index 8db1105..d994756 100644
--- a/src/test/java/com/google/devtools/build/lib/testutil/TestConstants.java
+++ b/src/test/java/com/google/devtools/build/lib/testutil/TestConstants.java
@@ -74,6 +74,10 @@
   public static final ImmutableList<String> DOCS_RULES_PATHS = ImmutableList.of(
       "src/main/java/com/google/devtools/build/lib/rules");
 
+  // Constants used to determine how genrule pulls in the setup script.
+  public static final String GENRULE_SETUP = "@bazel_tools//tools/genrule:genrule-setup.sh";
+  public static final String GENRULE_SETUP_PATH = "genrule-setup.sh";
+
   public static final InvocationPolicy TEST_INVOCATION_POLICY =
       InvocationPolicy.getDefaultInstance();