Open source the configuration tests.

- update the MOCK_CROSSTOOL to provide more stuff needed by tests
- add a THIS_IS_BAZEL constant to allow disabling individual test cases
- disable some tests in Bazel

The disabled tests are mainly due to differences in the test setup - making
the test setups more similar will largely fix that. I think we'll make some
changes to our internal setup, too, not just the external one. For example,
the use of 'k8' and 'piii' to refer to 'x86_64' and 'x86' seems archaic.

I decided to leave the dependency on the C++ and Java configurations rather
than rewriting the tests to use mock configurations. That would be nicer, but
also requires significantly more work.

--
MOS_MIGRATED_REVID=91399406
diff --git a/src/test/java/BUILD b/src/test/java/BUILD
index 8cb595f..79043b5 100644
--- a/src/test/java/BUILD
+++ b/src/test/java/BUILD
@@ -195,6 +195,7 @@
         "com/google/devtools/build/lib/exec/util/*.java",
         "com/google/devtools/build/lib/packages/util/*.java",
         "com/google/devtools/build/lib/skyframe/util/*.java",
+        "com/google/devtools/build/lib/analysis/BazelAnalysisMock.java",
     ]),
     data = [
         "//src/main/native:libunix.dylib",
@@ -223,7 +224,29 @@
     name = "analysis_actions_test",
     srcs = glob([
         "com/google/devtools/build/lib/analysis/actions/*.java",
-        "com/google/devtools/build/lib/analysis/BazelAnalysisMock.java",
+    ]),
+    args = ["com.google.devtools.build.lib.AllTests"],
+    deps = [
+        ":actions_testutil",
+        ":analysis_testutil",
+        ":foundations_testutil",
+        ":test_runner",
+        ":testutil",
+        "//src/main/java:bazel-core",
+        "//src/main/protobuf:proto_extra_actions_base",
+        "//third_party:guava",
+        "//third_party:guava-testlib",
+        "//third_party:jsr305",
+        "//third_party:junit4",
+        "//third_party:mockito",
+        "//third_party:truth",
+    ],
+)
+
+java_test(
+    name = "analysis_config_test",
+    srcs = glob([
+        "com/google/devtools/build/lib/analysis/config/*.java",
     ]),
     args = ["com.google.devtools.build.lib.AllTests"],
     deps = [
diff --git a/src/test/java/MOCK_CROSSTOOL b/src/test/java/MOCK_CROSSTOOL
index c57df53..29f1a28 100644
--- a/src/test/java/MOCK_CROSSTOOL
+++ b/src/test/java/MOCK_CROSSTOOL
@@ -1,13 +1,20 @@
-# This test resource file needs to cover all the things that we expect in the
-# tests. While it currently looks like a verbatim copy of tools/cpp/CROSSTOOL,
-# that's just because not all tests are there yet.
+# This test resource file is the default test setup - it largely matches what
+# we have in our repository cover; individual test cases can override this in
+# order to test more scenarios.
 
-major_version: "local"
-minor_version: ""
+major_version: "1"
+minor_version: "42"
 default_target_cpu: "k8"
+
+# TODO(bazel-team): We should change these identifiers to match what everyone
+# else is using, but that requires rewriting a lot of our tests.
 default_toolchain {
   cpu: "k8"
-  toolchain_identifier: "local_linux"
+  toolchain_identifier: "k8"
+}
+default_toolchain {
+  cpu: "piii"
+  toolchain_identifier: "piii"
 }
 default_toolchain {
   cpu: "darwin"
@@ -15,65 +22,100 @@
 }
 
 toolchain {
+  toolchain_identifier: "k8"
+
+  host_system_name: "local"
+  target_system_name: "local"
+  target_cpu: "k8"
+  target_libc: "local"
+  compiler: "compiler"
+
   abi_version: "local"
   abi_libc_version: "local"
-  builtin_sysroot: ""
-  compiler: "compiler"
-  host_system_name: "local"
-  needsPic: true
-  supports_gold_linker: false
-  supports_incremental_linker: false
-  supports_fission: false
-  supports_interface_shared_objects: false
-  supports_normalizing_ar: false
-  supports_start_end_lib: false
-  supports_thin_archives: false
-  target_libc: "local"
-  target_cpu: "local"
-  target_system_name: "local"
-  toolchain_identifier: "local_linux"
 
   tool_path { name: "ar" path: "/usr/bin/ar" }
   tool_path { name: "compat-ld" path: "/usr/bin/ld" }
   tool_path { name: "cpp" path: "/usr/bin/cpp" }
   tool_path { name: "dwp" path: "/usr/bin/dwp" }
   tool_path { name: "gcc" path: "/usr/bin/gcc" }
-  cxx_flag: "-std=c++0x"
-  linker_flag: "-lstdc++"
-  # TODO(bazel-team): In theory, the path here ought to exactly match the path
-  # used by gcc. That works because bazel currently doesn't track files at
-  # absolute locations and has no remote execution, yet. However, this will need
-  # to be fixed, maybe with auto-detection?
-  cxx_builtin_include_directory: "/usr/lib/gcc/"
-  cxx_builtin_include_directory: "/usr/local/include"
-  cxx_builtin_include_directory: "/usr/include"
   tool_path { name: "gcov" path: "/usr/bin/gcov" }
   tool_path { name: "ld" path: "/usr/bin/ld" }
   tool_path { name: "nm" path: "/usr/bin/nm" }
   tool_path { name: "objcopy" path: "/usr/bin/objcopy" }
-  objcopy_embed_flag: "-I"
-  objcopy_embed_flag: "binary"
   tool_path { name: "objdump" path: "/usr/bin/objdump" }
   tool_path { name: "strip" path: "/usr/bin/strip" }
+
+  builtin_sysroot: ""
+  cxx_flag: "-std=c++0x"
+  linker_flag: "-lstdc++"
+  cxx_builtin_include_directory: "/usr/lib/gcc/"
+  cxx_builtin_include_directory: "/usr/local/include"
+  cxx_builtin_include_directory: "/usr/include"
+  objcopy_embed_flag: "-I"
+  objcopy_embed_flag: "binary"
 }
 
 toolchain {
+  toolchain_identifier: "piii"
+
+  host_system_name: "local"
+  target_system_name: "local"
+  target_cpu: "piii"
+  target_libc: "local"
+  compiler: "compiler"
+
   abi_version: "local"
   abi_libc_version: "local"
+
+  tool_path { name: "ar" path: "/usr/bin/ar" }
+  tool_path { name: "compat-ld" path: "/usr/bin/ld" }
+  tool_path { name: "cpp" path: "/usr/bin/cpp" }
+  tool_path { name: "dwp" path: "/usr/bin/dwp" }
+  tool_path { name: "gcc" path: "/usr/bin/gcc" }
+  tool_path { name: "gcov" path: "/usr/bin/gcov" }
+  tool_path { name: "ld" path: "/usr/bin/ld" }
+  tool_path { name: "nm" path: "/usr/bin/nm" }
+  tool_path { name: "objcopy" path: "/usr/bin/objcopy" }
+  tool_path { name: "objdump" path: "/usr/bin/objdump" }
+  tool_path { name: "strip" path: "/usr/bin/strip" }
+
   builtin_sysroot: ""
-  compiler: "compiler"
-  host_system_name: "local"
-  needsPic: true
-  target_libc: "macosx"
-  target_cpu: "darwin"
-  target_system_name: "local"
+  cxx_flag: "-std=c++0x"
+  linker_flag: "-lstdc++"
+  cxx_builtin_include_directory: "/usr/lib/gcc/"
+  cxx_builtin_include_directory: "/usr/local/include"
+  cxx_builtin_include_directory: "/usr/include"
+  objcopy_embed_flag: "-I"
+  objcopy_embed_flag: "binary"
+}
+
+toolchain {
   toolchain_identifier: "local_darwin"
 
+  host_system_name: "local"
+  target_system_name: "local"
+  target_cpu: "darwin"
+  target_libc: "macosx"
+  compiler: "compiler"
+
+  abi_version: "local"
+  abi_libc_version: "local"
+
   tool_path { name: "ar" path: "/usr/bin/libtool" }
   tool_path { name: "compat-ld" path: "/usr/bin/ld" }
   tool_path { name: "cpp" path: "/usr/bin/cpp" }
   tool_path { name: "dwp" path: "/usr/bin/dwp" }
   tool_path { name: "gcc" path: "/usr/bin/gcc" }
+  tool_path { name: "gcov" path: "/usr/bin/gcov" }
+  tool_path { name: "ld" path: "/usr/bin/ld" }
+  tool_path { name: "nm" path: "/usr/bin/nm" }
+  tool_path { name: "objcopy" path: "/usr/bin/objcopy" }
+  tool_path { name: "objdump" path: "/usr/bin/objdump" }
+  tool_path { name: "strip" path: "/usr/bin/strip" }
+
+  needsPic: true
+
+  builtin_sysroot: ""
   cxx_flag: "-std=c++0x"
   ar_flag: "-static"
   ar_flag: "-s"
@@ -84,114 +126,102 @@
   cxx_builtin_include_directory: "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs"
   cxx_builtin_include_directory: "/opt/local/include"
   cxx_builtin_include_directory: "/Library/Developer/CommandLineTools"
-  tool_path { name: "gcov" path: "/usr/bin/gcov" }
-  tool_path { name: "ld" path: "/usr/bin/ld" }
-  tool_path { name: "nm" path: "/usr/bin/nm" }
-  tool_path { name: "objcopy" path: "/usr/bin/objcopy" }
   objcopy_embed_flag: "-I"
   objcopy_embed_flag: "binary"
-  tool_path { name: "objdump" path: "/usr/bin/objdump" }
-  tool_path { name: "strip" path: "/usr/bin/strip" }
 }
 
 toolchain {
+  toolchain_identifier: "local_windows_mingw"
+
+  host_system_name: "local"
+  target_system_name: "local"
+  target_cpu: "k8"
+  target_libc: "local"
+  compiler: "windows_mingw"
+
   abi_version: "local"
   abi_libc_version: "local"
-  builtin_sysroot: ""
-  compiler: "windows_mingw"
-  host_system_name: "local"
-  needsPic: false
-  target_libc: "local"
-  target_cpu: "k8"
-  target_system_name: "local"
-  toolchain_identifier: "local_windows_mingw"
 
   tool_path { name: "ar" path: "C:/mingw/bin/ar" }
   tool_path { name: "compat-ld" path: "C:/mingw/bin/ld" }
   tool_path { name: "cpp" path: "C:/mingw/bin/cpp" }
   tool_path { name: "dwp" path: "C:/mingw/bin/dwp" }
   tool_path { name: "gcc" path: "C:/mingw/bin/gcc" }
-  cxx_flag: "-std=c++0x"
-  # TODO(bazel-team): In theory, the path here ought to exactly match the path
-  # used by gcc. That works because bazel currently doesn't track files at
-  # absolute locations and has no remote execution, yet. However, this will need
-  # to be fixed, maybe with auto-detection?
-  cxx_builtin_include_directory: "C:/mingw/include"
-  cxx_builtin_include_directory: "C:/mingw/lib/gcc"
   tool_path { name: "gcov" path: "C:/mingw/bin/gcov" }
   tool_path { name: "ld" path: "C:/mingw/bin/ld" }
   tool_path { name: "nm" path: "C:/mingw/bin/nm" }
   tool_path { name: "objcopy" path: "C:/mingw/bin/objcopy" }
-  objcopy_embed_flag: "-I"
-  objcopy_embed_flag: "binary"
   tool_path { name: "objdump" path: "C:/mingw/bin/objdump" }
   tool_path { name: "strip" path: "C:/mingw/bin/strip" }
+
+  builtin_sysroot: ""
+  cxx_flag: "-std=c++0x"
+  cxx_builtin_include_directory: "C:/mingw/include"
+  cxx_builtin_include_directory: "C:/mingw/lib/gcc"
+  objcopy_embed_flag: "-I"
+  objcopy_embed_flag: "binary"
 }
 
 toolchain {
+  toolchain_identifier: "local_windows_msys64_mingw64"
+
+  host_system_name: "local"
+  target_system_name: "local"
+  target_cpu: "k8"
+  target_libc: "local"
+  compiler: "windows_msys64_mingw64"
+
   abi_version: "local"
   abi_libc_version: "local"
-  builtin_sysroot: ""
-  compiler: "windows_msys64_mingw64"
-  host_system_name: "local"
-  needsPic: false
-  target_libc: "local"
-  target_cpu: "k8"
-  target_system_name: "local"
-  toolchain_identifier: "local_windows_msys64_mingw64"
 
   tool_path { name: "ar" path: "C:/msys64/mingw64/bin/ar" }
   tool_path { name: "compat-ld" path: "C:/msys64/mingw64/bin/ld" }
   tool_path { name: "cpp" path: "C:/msys64/mingw64/bin/cpp" }
   tool_path { name: "dwp" path: "C:/msys64/mingw64/bin/dwp" }
   tool_path { name: "gcc" path: "C:/msys64/mingw64/bin/gcc" }
-  cxx_flag: "-std=c++0x"
-  # TODO(bazel-team): In theory, the path here ought to exactly match the path
-  # used by gcc. That works because bazel currently doesn't track files at
-  # absolute locations and has no remote execution, yet. However, this will need
-  # to be fixed, maybe with auto-detection?
-  cxx_builtin_include_directory: "C:/msys64/mingw64/x86_64-w64-mingw32/include"
   tool_path { name: "gcov" path: "C:/msys64/mingw64/bin/gcov" }
   tool_path { name: "ld" path: "C:/msys64/mingw64/bin/ld" }
   tool_path { name: "nm" path: "C:/msys64/mingw64/bin/nm" }
   tool_path { name: "objcopy" path: "C:/msys64/mingw64/bin/objcopy" }
-  objcopy_embed_flag: "-I"
-  objcopy_embed_flag: "binary"
   tool_path { name: "objdump" path: "C:/msys64/mingw64/bin/objdump" }
   tool_path { name: "strip" path: "C:/msys64/mingw64/bin/strip" }
+
+  builtin_sysroot: ""
+  cxx_flag: "-std=c++0x"
+  cxx_builtin_include_directory: "C:/msys64/mingw64/x86_64-w64-mingw32/include"
+  objcopy_embed_flag: "-I"
+  objcopy_embed_flag: "binary"
 }
 
 toolchain {
+  toolchain_identifier: "local_windows_clang"
+
+  host_system_name: "local"
+  target_system_name: "local"
+  target_cpu: "k8"
+  target_libc: "local"
+  compiler: "windows_clang"
+
   abi_version: "local"
   abi_libc_version: "local"
-  builtin_sysroot: ""
-  compiler: "windows_clang"
-  host_system_name: "local"
-  needsPic: false
-  target_libc: "local"
-  target_cpu: "k8"
-  target_system_name: "local"
-  toolchain_identifier: "local_windows_clang"
 
   tool_path { name: "ar" path: "C:/mingw/bin/ar" }
   tool_path { name: "compat-ld" path: "C:/Program Files (x86)/LLVM/bin/ld" }
   tool_path { name: "cpp" path: "C:/Program Files (x86)/LLVM/bin/cpp" }
   tool_path { name: "dwp" path: "C:/Program Files (x86)/LLVM/bin/dwp" }
   tool_path { name: "gcc" path: "C:/Program Files (x86)/LLVM/bin/clang" }
-  cxx_flag: "-std=c++0x"
-  # TODO(bazel-team): In theory, the path here ought to exactly match the path
-  # used by gcc. That works because bazel currently doesn't track files at
-  # absolute locations and has no remote execution, yet. However, this will need
-  # to be fixed, maybe with auto-detection?
-  cxx_builtin_include_directory: "/usr/lib/gcc/"
-  cxx_builtin_include_directory: "/usr/local/include"
-  cxx_builtin_include_directory: "/usr/include"
   tool_path { name: "gcov" path: "C:/Program Files (x86)/LLVM/bin/gcov" }
   tool_path { name: "ld" path: "C:/Program Files (x86)/LLVM/bin/ld" }
   tool_path { name: "nm" path: "C:/Program Files (x86)/LLVM/bin/nm" }
   tool_path { name: "objcopy" path: "C:/Program Files (x86)/LLVM/bin/objcopy" }
-  objcopy_embed_flag: "-I"
-  objcopy_embed_flag: "binary"
   tool_path { name: "objdump" path: "C:/Program Files (x86)/LLVM/bin/objdump" }
   tool_path { name: "strip" path: "C:/Program Files (x86)/LLVM/bin/strip" }
+
+  cxx_flag: "-std=c++0x"
+  cxx_builtin_include_directory: "/usr/lib/gcc/"
+  cxx_builtin_include_directory: "/usr/local/include"
+  cxx_builtin_include_directory: "/usr/include"
+  objcopy_embed_flag: "-I"
+  objcopy_embed_flag: "binary"
+  builtin_sysroot: ""
 }
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/BazelAnalysisMock.java b/src/test/java/com/google/devtools/build/lib/analysis/BazelAnalysisMock.java
index 6107b41..b428a2a 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/BazelAnalysisMock.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/BazelAnalysisMock.java
@@ -55,15 +55,22 @@
         "               'JavaBuilderCanary_deploy.jar', 'ijar'])");
     config.create("tools/cpp/BUILD",
         "filegroup(name = 'toolchain', srcs = [':cc-compiler-local', ':empty'])",
-        "cc_toolchain(name = 'cc-compiler-local', all_files = ':empty', compiler_files = ':empty',",
-        "cpu = 'local', dwp_files = ':empty', dynamic_runtime_libs = [':empty'], ",
-        "  linker_files = ':empty',",
-        "objcopy_files = ':empty', static_runtime_libs = [':empty'], strip_files = ':empty',)",
+        "cc_toolchain(name = 'cc-compiler-k8', all_files = ':empty', compiler_files = ':empty',",
+        "    cpu = 'local', dwp_files = ':empty', dynamic_runtime_libs = [':empty'], ",
+        "    linker_files = ':empty',",
+        "    objcopy_files = ':empty', static_runtime_libs = [':empty'], strip_files = ':empty',",
+        ")",
+        "cc_toolchain(name = 'cc-compiler-piii', all_files = ':empty', compiler_files = ':empty',",
+        "    cpu = 'local', dwp_files = ':empty', dynamic_runtime_libs = [':empty'], ",
+        "    linker_files = ':empty',",
+        "    objcopy_files = ':empty', static_runtime_libs = [':empty'], strip_files = ':empty',",
+        ")",
         "cc_toolchain(name = 'cc-compiler-darwin', all_files = ':empty', ",
-        "  compiler_files = ':empty',",
-        "cpu = 'local', dwp_files = ':empty', dynamic_runtime_libs = [':empty'], ",
-        "  linker_files = ':empty',",
-        "objcopy_files = ':empty', static_runtime_libs = [':empty'], strip_files = ':empty',)");
+        "    compiler_files = ':empty',",
+        "    cpu = 'local', dwp_files = ':empty', dynamic_runtime_libs = [':empty'], ",
+        "    linker_files = ':empty',",
+        "    objcopy_files = ':empty', static_runtime_libs = [':empty'], strip_files = ':empty',",
+        ")");
     config.create("tools/cpp/CROSSTOOL", readFromResources("MOCK_CROSSTOOL"));
   }
 
@@ -105,6 +112,6 @@
 
   @Override
   public ImmutableList<Class<? extends FragmentOptions>> getBuildOptions() {
-    throw new UnsupportedOperationException();
+    return BazelRuleClassProvider.BUILD_OPTIONS;
   }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/config/BuildConfigurationTest.java b/src/test/java/com/google/devtools/build/lib/analysis/config/BuildConfigurationTest.java
new file mode 100644
index 0000000..8089f5d
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/analysis/config/BuildConfigurationTest.java
@@ -0,0 +1,304 @@
+// Copyright 2006-2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.analysis.config;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.devtools.build.lib.analysis.config.BuildConfiguration.Fragment;
+import com.google.devtools.build.lib.analysis.util.ConfigurationTestCase;
+import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition;
+import com.google.devtools.build.lib.rules.cpp.CppConfiguration;
+import com.google.devtools.build.lib.rules.cpp.CppOptions;
+import com.google.devtools.build.lib.rules.java.JavaConfiguration;
+import com.google.devtools.build.lib.syntax.Label;
+import com.google.devtools.build.lib.testutil.TestConstants;
+import com.google.devtools.build.lib.testutil.TestUtils;
+import com.google.devtools.build.lib.vfs.FileSystem;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.common.options.Options;
+
+import java.util.Map;
+
+/**
+ * Tests for {@link BuildConfiguration}.
+ */
+public class BuildConfigurationTest extends ConfigurationTestCase {
+
+  public void testBasics() throws Exception {
+    if (TestConstants.THIS_IS_BAZEL) {
+      return;
+    }
+
+    BuildConfiguration config = create("--cpu=piii");
+    String outputDirPrefix = outputBase
+        + "/workspace/blaze-out/gcc-4.4.0-glibc-2.3.6-grte-piii-fastbuild";
+
+    assertEquals(outputDirPrefix,
+                 config.getOutputDirectory().getPath().toString());
+    assertEquals(outputDirPrefix + "/bin",
+                 config.getBinDirectory().getPath().toString());
+    assertEquals(outputDirPrefix + "/include",
+                 config.getIncludeDirectory().getPath().toString());
+    assertEquals(outputDirPrefix + "/genfiles",
+                 config.getGenfilesDirectory().getPath().toString());
+    assertEquals(outputDirPrefix + "/testlogs",
+                 config.getTestLogsDirectory().getPath().toString());
+  }
+
+  public void testPlatformSuffix() throws Exception {
+    if (TestConstants.THIS_IS_BAZEL) {
+      return;
+    }
+
+    BuildConfiguration config = create("--platform_suffix=-test");
+    assertEquals("gcc-4.4.0-glibc-2.3.6-grte-k8-fastbuild-test",
+                 config.getShortName());
+    assertEquals(outputBase + "/workspace/blaze-out/gcc-4.4.0-glibc-2.3.6-grte-k8-fastbuild-test",
+        config.getOutputDirectory().getPath().toString());
+  }
+
+  public void testEnvironment() throws Exception {
+    if (TestConstants.THIS_IS_BAZEL) {
+      return;
+    }
+
+    Map<String, String> env = create().getDefaultShellEnvironment();
+    assertThat(env).containsEntry("LANG", "en_US");
+    assertThat(env).containsKey("PATH");
+    assertThat(env.get("PATH")).contains("/bin:/usr/bin");
+    try {
+      env.put("FOO", "bar");
+      fail("modifiable default environment");
+    } catch (UnsupportedOperationException ignored) {
+      //expected exception
+    }
+  }
+
+  public void testHostCpu() throws Exception {
+    for (String cpu : new String[] { "piii", "k8" }) {
+      BuildConfiguration hostConfig = createHost("--host_cpu=" + cpu);
+      assertEquals(cpu, hostConfig.getFragment(CppConfiguration.class).getTargetCpu());
+    }
+  }
+
+  public void testHostCrosstoolTop() throws Exception {
+    if (TestConstants.THIS_IS_BAZEL) {
+      return;
+    }
+
+    BuildConfiguration config = create("--cpu=piii");
+    assertEquals(Label.parseAbsoluteUnchecked("//third_party/crosstool/mock:cc-compiler-piii"),
+        config.getFragment(CppConfiguration.class).getCcToolchainRuleLabel());
+
+    BuildConfiguration hostConfig = config.getConfiguration(ConfigurationTransition.HOST);
+    assertEquals(Label.parseAbsoluteUnchecked("//third_party/crosstool/mock:cc-compiler-k8"),
+        hostConfig.getFragment(CppConfiguration.class).getCcToolchainRuleLabel());
+  }
+
+  public void testTestEnvironment() throws Exception {
+    clientEnv.put("MY_VAR1", "1");
+    clientEnv.put("MY_VAR2", "1");
+    BuildConfiguration config = create(
+        "--test_env=MY_VAR1", "--test_env=MY_VAR2=2",
+        "--test_env=MY_VAR3=3", "--test_env=NO_SUCH_VAR");
+    assertThat(config.getTestEnv()).containsEntry("MY_VAR1", "1");
+    assertThat(config.getTestEnv()).containsEntry("MY_VAR2", "2");
+    assertThat(config.getTestEnv()).containsEntry("MY_VAR3", "3");
+    assertThat(config.getTestEnv()).doesNotContainKey("NO_SUCH_VAR");
+  }
+
+  public void testMakeEnvFlags() throws Exception {
+    BuildConfiguration config = create();
+    assertThat(config.getMakeEnvironment().get("STRIP")).contains("strip");
+  }
+
+  public void testCaching() throws Exception {
+    BuildConfiguration.Options a = Options.getDefaults(BuildConfiguration.Options.class);
+    BuildConfiguration.Options b = Options.getDefaults(BuildConfiguration.Options.class);
+    // The String representations of the BuildConfiguration.Options must be equal even if these are
+    // different objects, if they were created with the same options (no options in this case).
+    assertEquals(a.toString(), b.toString());
+    assertEquals(a.cacheKey(), b.cacheKey());
+  }
+
+  private void checkInvalidCpuError(String cpuOption, String expectedMessage) throws Exception {
+    try {
+      create("--" + cpuOption + "=bogus");
+      fail();
+    } catch (InvalidConfigurationException e) {
+      assertThat(e).hasMessage(expectedMessage);
+    }
+  }
+
+  public void testInvalidCpu() throws Exception {
+    checkInvalidCpuError("cpu", "No toolchain found for cpu 'bogus'");
+  }
+
+  public void testConfigurationsHaveUniqueOutputDirectories() throws Exception {
+    assertConfigurationsHaveUniqueOutputDirectories(createCollection());
+    assertConfigurationsHaveUniqueOutputDirectories(createCollection("--compilation_mode=opt"));
+  }
+
+  public void testMultiCpu() throws Exception {
+    if (TestConstants.THIS_IS_BAZEL) {
+      return;
+    }
+
+    BuildConfigurationCollection master = createCollection(
+        "--multi_cpu=k8", "--multi_cpu=piii", "--ignore_java_and_python_cpu");
+    assertThat(master.getTargetConfigurations()).hasSize(2);
+    // Note: the cpus are sorted alphabetically.
+    assertEquals("k8", master.getTargetConfigurations().get(0).getCpu());
+    assertEquals("piii", master.getTargetConfigurations().get(1).getCpu());
+  }
+
+  /**
+   * Check that the cpus are sorted alphabetically regardless of the order in which they are
+   * specified.
+   */
+  public void testMultiCpuSorting() throws Exception {
+    if (TestConstants.THIS_IS_BAZEL) {
+      return;
+    }
+
+    for (int order = 0; order < 2; order++) {
+      BuildConfigurationCollection master;
+      if (order == 0) {
+        master = createCollection(
+            "--multi_cpu=k8", "--multi_cpu=piii", "--ignore_java_and_python_cpu");
+      } else {
+        master = createCollection(
+            "--multi_cpu=piii", "--multi_cpu=k8", "--ignore_java_and_python_cpu");
+      }
+      assertThat(master.getTargetConfigurations()).hasSize(2);
+      assertEquals("k8", master.getTargetConfigurations().get(0).getCpu());
+      assertEquals("piii", master.getTargetConfigurations().get(1).getCpu());
+    }
+  }
+
+  public void testTargetEnvironment() throws Exception {
+    BuildConfiguration oneEnvConfig = create("--target_environment=//foo");
+    assertThat(oneEnvConfig.getTargetEnvironments()).containsExactly(Label.parseAbsolute("//foo"));
+
+    BuildConfiguration twoEnvsConfig =
+        create("--target_environment=//foo", "--target_environment=//bar");
+    assertThat(twoEnvsConfig.getTargetEnvironments())
+        .containsExactly(Label.parseAbsolute("//foo"), Label.parseAbsolute("//bar"));
+
+    BuildConfiguration noEnvsConfig = create();
+    assertThat(noEnvsConfig.getTargetEnvironments()).isEmpty();
+  }
+
+  @SafeVarargs
+  @SuppressWarnings("unchecked")
+  private final ConfigurationFragmentFactory createMockFragment(
+      final Class<? extends Fragment> creates, final Class<? extends Fragment>... dependsOn) {
+    return new ConfigurationFragmentFactory() {
+
+      @Override
+      public Class<? extends Fragment> creates() {
+        return creates;
+      }
+
+      @Override
+      public Fragment create(ConfigurationEnvironment env, BuildOptions buildOptions)
+          throws InvalidConfigurationException {
+        for (Class<? extends Fragment> fragmentType : dependsOn) {
+          env.getFragment(buildOptions, fragmentType);
+        }
+        return new Fragment() {
+
+          @Override
+          public String getName() {
+            return creates.toString();
+          }
+
+          @Override
+          public String cacheKey() {
+            return creates.toString();
+          }
+        };
+      }
+    };
+  }
+
+  public void testCycleInFragments() throws Exception {
+    configurationFactory = new ConfigurationFactory(
+        getAnalysisMock().createConfigurationCollectionFactory(),
+        createMockFragment(CppConfiguration.class, JavaConfiguration.class),
+        createMockFragment(JavaConfiguration.class, CppConfiguration.class));
+    try {
+      createCollection();
+      fail();
+    } catch (IllegalStateException e) {
+      // expected
+    }
+  }
+
+  public void testMissingFragment() throws Exception {
+    configurationFactory = new ConfigurationFactory(
+        getAnalysisMock().createConfigurationCollectionFactory(),
+        createMockFragment(CppConfiguration.class, JavaConfiguration.class));
+    try {
+      createCollection();
+      fail();
+    } catch (RuntimeException e) {
+      // expected
+    }
+  }
+
+  public void testGlobalMakeVariableOverride() throws Exception {
+    assertThat(create().getMakeEnvironment()).containsEntry("COMPILATION_MODE", "fastbuild");
+    BuildConfiguration config = create("--define", "COMPILATION_MODE=fluttershy");
+    assertThat(config.getMakeEnvironment()).containsEntry("COMPILATION_MODE", "fluttershy");
+  }
+
+  public void testGetOptionClass() throws Exception {
+    BuildConfiguration config = create();
+    // Directly defined option:
+    assertEquals(BuildConfiguration.Options.class, config.getOptionClass("compilation_mode"));
+    // Option defined in a fragment:
+    assertEquals(CppOptions.class, config.getOptionClass("lipo"));
+    // Unrecognized option:
+    assertNull(config.getOptionClass("do_my_laundry"));
+  }
+
+  public void testGetOptionValue() throws Exception {
+    // Directly defined options:
+    assertEquals(CompilationMode.DBG, create("-c", "dbg").getOptionValue("compilation_mode"));
+    assertEquals(CompilationMode.OPT, create("-c", "opt").getOptionValue("compilation_mode"));
+
+    // Options defined in a fragment:
+    assertEquals(Boolean.TRUE, create("--cc_include_scanning")
+        .getOptionValue("cc_include_scanning"));
+    assertEquals(Boolean.FALSE, create("--nocc_include_scanning")
+        .getOptionValue("cc_include_scanning"));
+
+    // Unrecognized option:
+    assertNull(create().getOptionValue("do_my_dishes"));
+
+    // Legitimately null option:
+    assertNull(create().getOptionValue("test_filter"));
+  }
+
+  public void testSerialization() throws Exception {
+    FileSystem oldFileSystem = Path.getFileSystemForSerialization();
+    try {
+      Path.setFileSystemForSerialization(scratchFS());
+      TestUtils.serializeObject(createCollection());
+    } finally {
+      Path.setFileSystemForSerialization(oldFileSystem);
+    }
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/config/BuildOptionsTest.java b/src/test/java/com/google/devtools/build/lib/analysis/config/BuildOptionsTest.java
new file mode 100644
index 0000000..10b8f68
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/analysis/config/BuildOptionsTest.java
@@ -0,0 +1,50 @@
+// Copyright 2009-2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.analysis.config;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * A test for {@link BuildOptions}.
+ */
+@RunWith(JUnit4.class)
+public class BuildOptionsTest {
+  private static final ImmutableList<Class<? extends FragmentOptions>> TEST_OPTIONS =
+      ImmutableList.<Class<? extends FragmentOptions>>of(BuildConfiguration.Options.class);
+
+  @Test
+  public void testOptionSetCaching() throws Exception {
+    BuildOptions a = BuildOptions.createDefaults(TEST_OPTIONS);
+    BuildOptions b = BuildOptions.createDefaults(TEST_OPTIONS);
+    // The cache keys of the OptionSets must be equal even if these are
+    // different objects, if they were created with the same options (no options in this case).
+    assertEquals(a.toString(), b.toString());
+    assertEquals(a.computeCacheKey(), b.computeCacheKey());
+  }
+
+  @Test
+  public void testCachingSpecialCases() throws Exception {
+    // You can add options here to test that their string representations are good.
+    String[] options = new String[] { "--run_under=//run_under" };
+    BuildOptions a = BuildOptions.of(TEST_OPTIONS, options);
+    BuildOptions b = BuildOptions.of(TEST_OPTIONS, options);
+    assertEquals(a.toString(), b.toString());
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/config/ConfigSettingTest.java b/src/test/java/com/google/devtools/build/lib/analysis/config/ConfigSettingTest.java
new file mode 100644
index 0000000..e9e89f2
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/analysis/config/ConfigSettingTest.java
@@ -0,0 +1,183 @@
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.analysis.config;
+
+import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
+import com.google.devtools.build.lib.syntax.Label;
+
+/**
+ * Tests for {@link ConfigSetting}.
+ */
+public class ConfigSettingTest extends BuildViewTestCase {
+
+  private void writeSimpleExample() throws Exception {
+    scratchFile("pkg/BUILD",
+        "config_setting(",
+        "    name = 'foo',",
+        "    values = {",
+        "        'compilation_mode': 'dbg',",
+        "        'stamp': '1',",
+        "    })");
+  }
+
+  private ConfigMatchingProvider getConfigMatchingProvider(String label) throws Exception {
+    return getConfiguredTarget(label).getProvider(ConfigMatchingProvider.class);
+  }
+
+  /**
+   * Tests that a config_setting only matches build configurations where *all* of
+   * its flag specifications match.
+   */
+  public void testMatchingCriteria() throws Exception {
+    writeSimpleExample();
+
+    // First flag mismatches:
+    useConfiguration("-c", "opt", "--stamp");
+    assertFalse(getConfigMatchingProvider("//pkg:foo").matches());
+
+    // Second flag mismatches:
+    useConfiguration("-c", "dbg", "--nostamp");
+    assertFalse(getConfigMatchingProvider("//pkg:foo").matches());
+
+    // Both flags mismatch:
+    useConfiguration("-c", "opt", "--nostamp");
+    assertFalse(getConfigMatchingProvider("//pkg:foo").matches());
+
+    // Both flags match:
+    useConfiguration("-c", "dbg", "--stamp");
+    assertTrue(getConfigMatchingProvider("//pkg:foo").matches());
+  }
+
+  /**
+   * Tests that {@link ConfigMatchingProvider#label} is correct.
+   */
+  public void testLabel() throws Exception {
+    writeSimpleExample();
+    assertEquals(
+        Label.parseAbsolute("//pkg:foo"),
+        getConfigMatchingProvider("//pkg:foo").label());
+  }
+
+  /**
+   * Tests that rule analysis fails on unknown options.
+   */
+  public void testUnknownOption() throws Exception {
+    checkError("foo", "badoption",
+        "unknown option: 'not_an_option'",
+        "config_setting(",
+        "    name = 'badoption',",
+        "    values = {'not_an_option': 'bar'})");
+  }
+
+  /**
+   * Tests that rule analysis fails on invalid option values.
+   */
+  public void testInvalidOptionValue() throws Exception {
+    checkError("foo", "badvalue",
+        "Not a valid compilation mode: 'baz'",
+        "config_setting(",
+        "    name = 'badvalue',",
+        "    values = {'compilation_mode': 'baz'})");
+  }
+
+  /**
+   * Tests that when the first option is valid but the config_setting doesn't match,
+   * remaining options are still validity-checked.
+   */
+  public void testInvalidOptionFartherDown() throws Exception {
+    checkError("foo", "badoption",
+        "unknown option: 'not_an_option'",
+        "config_setting(",
+        "    name = 'badoption',",
+        "    values = {",
+        "        'compilation_mode': 'opt',",
+        "        'not_an_option': 'bar',",
+        "    })");
+  }
+
+  /**
+   * Tests that *some* settings must be specified.
+   */
+  public void testEmptySettings() throws Exception {
+    checkError("foo", "empty",
+        "//foo:empty: no settings specified",
+        "config_setting(",
+        "    name = 'empty',",
+        "    values = {})");
+  }
+
+  /**
+   * Tests {@link BuildConfiguration.Fragment#lateBoundOptionDefaults} options (options
+   * that take alternative defaults from what's specified in {@link
+   * com.google.devtools.common.options.Option#defaultValue}).
+   */
+  public void testLateBoundOptionDefaults() throws Exception {
+    scratchFile("test/BUILD",
+        "config_setting(",
+        "    name = 'match',",
+        "    values = {",
+        "        'cpu': 'k8',",
+        "    })");
+    useConfiguration("--cpu=k8");
+    assertTrue(getConfigMatchingProvider("//test:match").matches());
+  }
+
+  /**
+   * Tests matching on multi-value attributes with key=value entries (e.g. --define).
+   */
+  public void testMultiValueDict() throws Exception {
+    scratchFile("test/BUILD",
+        "config_setting(",
+        "    name = 'match',",
+        "    values = {",
+        "        'define': 'foo=bar',",
+        "    })");
+
+    useConfiguration("");
+    assertFalse(getConfigMatchingProvider("//test:match").matches());
+    useConfiguration("--define", "foo=bar");
+    assertTrue(getConfigMatchingProvider("//test:match").matches());
+    useConfiguration("--define", "foo=baz");
+    assertFalse(getConfigMatchingProvider("//test:match").matches());
+    useConfiguration("--define", "foo=bar", "--define", "bar=baz");
+    assertTrue(getConfigMatchingProvider("//test:match").matches());
+    useConfiguration("--define", "foo=bar", "--define", "bar=baz", "--define", "foo=nope");
+    assertFalse(getConfigMatchingProvider("//test:match").matches());
+    useConfiguration("--define", "foo=nope", "--define", "bar=baz", "--define", "foo=bar");
+    assertTrue(getConfigMatchingProvider("//test:match").matches());
+  }
+
+  /**
+   * Tests matching on multi-value attributes with primitive values.
+   */
+  public void testMultiValueList() throws Exception {
+    scratchFile("test/BUILD",
+        "config_setting(",
+        "    name = 'match',",
+        "    values = {",
+        "        'copt': '-Dfoo',",
+        "    })");
+
+    useConfiguration("");
+    assertFalse(getConfigMatchingProvider("//test:match").matches());
+    useConfiguration("--copt", "-Dfoo");
+    assertTrue(getConfigMatchingProvider("//test:match").matches());
+    useConfiguration("--copt", "-Dbar");
+    assertFalse(getConfigMatchingProvider("//test:match").matches());
+    useConfiguration("--copt", "-Dfoo", "--copt", "-Dbar");
+    assertTrue(getConfigMatchingProvider("//test:match").matches());
+    useConfiguration("--copt", "-Dbar", "--copt", "-Dfoo");
+    assertTrue(getConfigMatchingProvider("//test:match").matches());
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/config/PerLabelOptionsTest.java b/src/test/java/com/google/devtools/build/lib/analysis/config/PerLabelOptionsTest.java
new file mode 100644
index 0000000..aa46f34
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/analysis/config/PerLabelOptionsTest.java
@@ -0,0 +1,84 @@
+// Copyright 2009-2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.analysis.config;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import com.google.common.testing.EqualsTester;
+import com.google.devtools.build.lib.util.RegexFilter;
+import com.google.devtools.common.options.OptionsParsingException;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A test for {@link PerLabelOptions}.
+ */
+@RunWith(JUnit4.class)
+public class PerLabelOptionsTest {
+  private PerLabelOptions options = null;
+
+  private PerLabelOptions createOptions(String string) throws OptionsParsingException {
+    options = new PerLabelOptions.PerLabelOptionsConverter().convert(string);
+    return options;
+  }
+
+  private void assertRegexParsing(String filter) throws OptionsParsingException {
+    RegexFilter regexFilter = new RegexFilter.RegexFilterConverter().convert(filter);
+    assertEquals(regexFilter.toString(), options.getRegexFilter().toString());
+  }
+
+  private void assertOptions(String pattern, String opts, List<String> expectedOptions)
+      throws OptionsParsingException {
+    createOptions(pattern + "@" + opts);
+    assertRegexParsing(pattern);
+    assertNotNull(options.getOptions());
+    assertEquals(expectedOptions, options.getOptions());
+  }
+
+  @Test
+  public void testEmpty() throws Exception {
+    createOptions("");
+    assertRegexParsing("");
+    assertThat(options.getOptions()).isEmpty();
+  }
+
+  @Test
+  public void testParsing() throws Exception {
+    assertOptions("", "", Collections.<String> emptyList());
+    assertOptions("", ", ,\t,", Collections.<String> emptyList());
+    assertOptions("a/b,+^c,_test$", ", ,\t,", Collections.<String> emptyList());
+    assertOptions("a/b,+^c,_test$", "", Collections.<String> emptyList());
+    assertOptions("a/b,+^c,_test$", "-g,-O0", Arrays.asList("-g", "-O0"));
+    assertOptions("a/b,+^c,_test$", "-g@,-O0", Arrays.asList("-g@", "-O0"));
+    assertOptions("a/b,+^c,_test$", "-g\\,,-O0", Arrays.asList("-g,", "-O0"));
+    assertOptions("a/b,+^c,_test$", "-g\\,,,,,-O0,,,@,", Arrays.asList("-g,", "-O0", "@"));
+  }
+
+  @Test
+  public void testEquals() throws Exception {
+    new EqualsTester()
+        .addEqualityGroup(createOptions("a/b,+^c,_test$@-g,-O0"),
+            createOptions("a/b,+^c,_test$@-g,-O0"))
+        .addEqualityGroup(createOptions("a/b,+^c,_test$@-O0"))
+        .testEquals();
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/config/RunUnderConverterTest.java b/src/test/java/com/google/devtools/build/lib/analysis/config/RunUnderConverterTest.java
new file mode 100644
index 0000000..3ba6e57
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/analysis/config/RunUnderConverterTest.java
@@ -0,0 +1,65 @@
+// Copyright 2007-2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.analysis.config;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.common.options.OptionsParsingException;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.List;
+
+/**
+ * Tests {@link RunUnderConverter}.
+ */
+@RunWith(JUnit4.class)
+public class RunUnderConverterTest {
+
+  @Test
+  public void testConverter() throws Exception {
+    assertEqualsRunUnder("command", null, "command", ImmutableList.<String>of());
+    assertEqualsRunUnder("command -c", null, "command", ImmutableList.of("-c"));
+    assertEqualsRunUnder("command -c --out=all", null, "command",
+        ImmutableList.of("-c", "--out=all"));
+    assertEqualsRunUnder("//run:under", "//run:under", null, ImmutableList.<String>of());
+    assertEqualsRunUnder("//run:under -c", "//run:under", null, ImmutableList.of("-c"));
+    assertEqualsRunUnder("//run:under -c --out=all", "//run:under", null,
+        ImmutableList.of("-c", "--out=all"));
+
+    assertRunUnderFails("", "Empty command");
+  }
+
+  private void assertEqualsRunUnder(String input, String label, String command,
+      List<String> options) throws Exception {
+    RunUnder runUnder = new RunUnderConverter().convert(input);
+    assertEquals(label, runUnder.getLabel() == null ? null : runUnder.getLabel().toString());
+    assertEquals(command, runUnder.getCommand());
+    assertEquals(options, runUnder.getOptions());
+  }
+
+  private void assertRunUnderFails(String input, String expectedError) {
+    try {
+      new RunUnderConverter().convert(input);
+      fail();
+    } catch (OptionsParsingException e) {
+      assertThat(e).hasMessage(expectedError);
+    }
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/config/StampTest.java b/src/test/java/com/google/devtools/build/lib/analysis/config/StampTest.java
new file mode 100644
index 0000000..8407ec9
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/analysis/config/StampTest.java
@@ -0,0 +1,40 @@
+// Copyright 2010-2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.analysis.config;
+
+import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
+import com.google.devtools.build.lib.packages.RuleClass;
+import com.google.devtools.build.lib.packages.RuleFactory;
+import com.google.devtools.build.lib.packages.TargetUtils;
+import com.google.devtools.build.lib.packages.TriState;
+import com.google.devtools.build.lib.packages.Type;
+import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
+
+/**
+ * Tests for link stamping.
+ */
+public class StampTest extends BuildViewTestCase {
+  /**
+   * Tests that link stamping is disabled for all tests that support it.
+   */
+  public void testNoStampingForTests() throws Exception {
+    RuleFactory ruleFactory = new RuleFactory(TestRuleClassProvider.getRuleClassProvider());
+    for (String name : ruleFactory.getRuleClassNames()) {
+      RuleClass ruleClass = ruleFactory.getRuleClass(name);
+      if (TargetUtils.isTestRuleName(name) && ruleClass.hasAttr("stamp", Type.TRISTATE)) {
+        assertEquals(TriState.NO, ruleClass.getAttributeByName("stamp").getDefaultValue(null));
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/ConfigurationTestCase.java b/src/test/java/com/google/devtools/build/lib/analysis/util/ConfigurationTestCase.java
index c9aec0f..a761b08 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/util/ConfigurationTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/ConfigurationTestCase.java
@@ -113,6 +113,7 @@
 
     AnalysisMock analysisMock = getAnalysisMock();
     analysisMock.setupMockClient(new MockToolsConfig(rootDirectory));
+    analysisMock.setupMockWorkspaceFiles(directories.getEmbeddedBinariesRoot());
     configurationFactory = analysisMock.createConfigurationFactory();
     buildOptionClasses = analysisMock.getBuildOptions();
   }
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 66c8a1c..2bae9b9 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
@@ -56,4 +56,6 @@
   public static final String TEST_RULE_CLASS_PROVIDER =
       "com.google.devtools.build.lib.bazel.rules.BazelRuleClassProvider";
   public static final ImmutableList<String> IGNORED_MESSAGE_PREFIXES = ImmutableList.<String>of();
+
+  public static final boolean THIS_IS_BAZEL = true;
 }