Add skyfunction to return all registered toolchain labels.

Part of #2219.

Change-Id: I7293fd13bd8e0931f92afd051e18a9e7ce63762d
PiperOrigin-RevId: 161721445
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
index 81db738..9b46c81 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
@@ -773,6 +773,24 @@
   }
 
   /**
+   * Rewrites the WORKSPACE to have the required boilerplate and the given lines of content.
+   *
+   * <p>Triggers Skyframe to reinitialize everything.
+   */
+  public void rewriteWorkspace(String... lines) throws Exception {
+    scratch.overwriteFile(
+        "WORKSPACE",
+        new ImmutableList.Builder<String>()
+            .addAll(analysisMock.getWorkspaceContents(mockToolsConfig))
+            .addAll(ImmutableList.copyOf(lines))
+            .build());
+
+    invalidatePackages();
+    // Need to re-initialize the workspace status.
+    getSkyframeExecutor().injectWorkspaceStatusData("test");
+  }
+
+  /**
    * Create and return a configured scratch rule.
    *
    * @param packageName the package name of the rule.
diff --git a/src/test/java/com/google/devtools/build/lib/rules/platform/BUILD b/src/test/java/com/google/devtools/build/lib/rules/platform/BUILD
index 781420d..9c34048 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/platform/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/rules/platform/BUILD
@@ -4,9 +4,14 @@
     visibility = ["//src/test/java/com/google/devtools/build/lib:__pkg__"],
 )
 
+TESTUTIL_SRCS = ["ToolchainTestCase.java"]
+
 java_test(
     name = "PlatformRulesTests",
-    srcs = glob(["*.java"]),
+    srcs = glob(
+        ["*.java"],
+        exclude = TESTUTIL_SRCS,
+    ),
     test_class = "com.google.devtools.build.lib.AllTests",
     deps = [
         "//src/main/java/com/google/devtools/build/lib:build-base",
@@ -26,3 +31,16 @@
         "//third_party:truth",
     ],
 )
+
+java_library(
+    name = "testutil",
+    srcs = TESTUTIL_SRCS,
+    visibility = ["//src/test/java/com/google/devtools/build/lib:__subpackages__"],
+    deps = [
+        "//src/main/java/com/google/devtools/build/lib/analysis/platform",
+        "//src/main/java/com/google/devtools/build/lib/cmdline",
+        "//src/test/java/com/google/devtools/build/lib/skylark:testutil",
+        "//third_party:junit4",
+        "//third_party:truth",
+    ],
+)
diff --git a/src/test/java/com/google/devtools/build/lib/rules/platform/ToolchainTestCase.java b/src/test/java/com/google/devtools/build/lib/rules/platform/ToolchainTestCase.java
new file mode 100644
index 0000000..87bce40
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/rules/platform/ToolchainTestCase.java
@@ -0,0 +1,87 @@
+// 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.rules.platform;
+
+import com.google.devtools.build.lib.analysis.platform.ConstraintSettingInfo;
+import com.google.devtools.build.lib.analysis.platform.ConstraintValueInfo;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.skylark.util.SkylarkTestCase;
+import org.junit.Before;
+
+/** Utility methods for setting up platform and toolchain related tests. */
+public abstract class ToolchainTestCase extends SkylarkTestCase {
+
+  public Label testToolchainType;
+  public ConstraintSettingInfo setting;
+  public ConstraintValueInfo linuxConstraint;
+  public ConstraintValueInfo macConstraint;
+
+  @Before
+  public void createConstraints() throws Exception {
+    scratch.file(
+        "constraint/BUILD",
+        "constraint_setting(name = 'os')",
+        "constraint_value(name = 'linux',",
+        "    constraint_setting = ':os')",
+        "constraint_value(name = 'mac',",
+        "    constraint_setting = ':os')");
+
+    setting = ConstraintSettingInfo.create(makeLabel("//constraint:os"));
+    linuxConstraint = ConstraintValueInfo.create(setting, makeLabel("//constraint:linux"));
+    macConstraint = ConstraintValueInfo.create(setting, makeLabel("//constraint:mac"));
+  }
+
+  @Before
+  public void createToolchains() throws Exception {
+    rewriteWorkspace(
+        "register_toolchains(", "    '//toolchain:toolchain_1',", "    '//toolchain:toolchain_2')");
+
+    scratch.file(
+        "toolchain/BUILD",
+        "load(':toolchain_def.bzl', 'test_toolchain')",
+        "toolchain_type(name = 'test_toolchain')",
+        "toolchain(",
+        "    name = 'toolchain_1',",
+        "    toolchain_type = ':test_toolchain',",
+        "    exec_compatible_with = ['//constraint:linux'],",
+        "    target_compatible_with = ['//constraint:mac'],",
+        "    toolchain = ':test_toolchain_1')",
+        "toolchain(",
+        "    name = 'toolchain_2',",
+        "    toolchain_type = ':test_toolchain',",
+        "    exec_compatible_with = ['//constraint:mac'],",
+        "    target_compatible_with = ['//constraint:linux'],",
+        "    toolchain = ':test_toolchain_2')",
+        "test_toolchain(",
+        "  name='test_toolchain_1',",
+        "  data = 'foo')",
+        "test_toolchain(",
+        "  name='test_toolchain_2',",
+        "  data = 'bar')");
+    scratch.file(
+        "toolchain/toolchain_def.bzl",
+        "def _impl(ctx):",
+        "  toolchain = platform_common.ToolchainInfo(",
+        "      type = Label('//toolchain:test_toolchain'),",
+        "      data = ctx.attr.data)",
+        "  return [toolchain]",
+        "test_toolchain = rule(",
+        "    implementation = _impl,",
+        "    attrs = {",
+        "       'data': attr.string()})");
+
+    testToolchainType = makeLabel("//toolchain:test_toolchain");
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/BUILD b/src/test/java/com/google/devtools/build/lib/skyframe/BUILD
index 9af05dd..d68e4e8 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/BUILD
@@ -25,6 +25,7 @@
         "//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/platform",
         "//src/main/java/com/google/devtools/build/skyframe",
         "//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
         "//src/test/java/com/google/devtools/build/lib:actions_testutil",
@@ -69,8 +70,10 @@
         "//src/main/java/com/google/devtools/build/lib:util",
         "//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/analysis/platform",
         "//src/main/java/com/google/devtools/build/lib/cmdline",
         "//src/main/java/com/google/devtools/build/lib/rules/cpp",
+        "//src/main/java/com/google/devtools/build/lib/rules/platform",
         "//src/main/java/com/google/devtools/build/skyframe",
         "//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
         "//src/test/java/com/google/devtools/build/lib:actions_testutil",
@@ -78,6 +81,7 @@
         "//src/test/java/com/google/devtools/build/lib:foundations_testutil",
         "//src/test/java/com/google/devtools/build/lib:packages_testutil",
         "//src/test/java/com/google/devtools/build/lib:testutil",
+        "//src/test/java/com/google/devtools/build/lib/rules/platform:testutil",
         "//third_party:guava",
         "//third_party:guava-testlib",
         "//third_party:jsr305",
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/RegisteredToolchainsFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/RegisteredToolchainsFunctionTest.java
new file mode 100644
index 0000000..4d9c93f
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/RegisteredToolchainsFunctionTest.java
@@ -0,0 +1,142 @@
+// 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.skyframe;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.devtools.build.skyframe.EvaluationResultSubjectFactory.assertThatEvaluationResult;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.testing.EqualsTester;
+import com.google.devtools.build.lib.analysis.platform.DeclaredToolchainInfo;
+import com.google.devtools.build.lib.rules.platform.ToolchainTestCase;
+import com.google.devtools.build.lib.skyframe.util.SkyframeExecutorTestUtils;
+import com.google.devtools.build.skyframe.EvaluationResult;
+import com.google.devtools.build.skyframe.SkyKey;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link RegisteredToolchainsFunction} and {@link RegisteredToolchainsValue}. */
+@RunWith(JUnit4.class)
+public class RegisteredToolchainsFunctionTest extends ToolchainTestCase {
+
+  private EvaluationResult<RegisteredToolchainsValue> requestToolchainsFromSkyframe(
+      SkyKey toolchainsKey) throws InterruptedException {
+    try {
+      getSkyframeExecutor().getSkyframeBuildView().enableAnalysis(true);
+      return SkyframeExecutorTestUtils.evaluate(
+          getSkyframeExecutor(), toolchainsKey, /*keepGoing=*/ false, reporter);
+    } finally {
+      getSkyframeExecutor().getSkyframeBuildView().enableAnalysis(false);
+    }
+  }
+
+  @Test
+  public void testRegisteredToolchains() throws Exception {
+    // Request the toolchains.
+    SkyKey toolchainsKey = RegisteredToolchainsValue.key(targetConfig);
+    EvaluationResult<RegisteredToolchainsValue> result =
+        requestToolchainsFromSkyframe(toolchainsKey);
+    assertThatEvaluationResult(result).hasNoError();
+    assertThatEvaluationResult(result).hasEntryThat(toolchainsKey).isNotNull();
+
+    RegisteredToolchainsValue value = result.get(toolchainsKey);
+    assertThat(value.registeredToolchains()).hasSize(2);
+
+    DeclaredToolchainInfo registeredToolchain1 = value.registeredToolchains().get(0);
+    assertThat(registeredToolchain1).isNotNull();
+
+    assertThat(registeredToolchain1.toolchainType()).isEqualTo(testToolchainType);
+    assertThat(registeredToolchain1.execConstraints()).containsExactly(linuxConstraint);
+    assertThat(registeredToolchain1.targetConstraints()).containsExactly(macConstraint);
+    assertThat(registeredToolchain1.toolchainLabel())
+        .isEqualTo(makeLabel("//toolchain:test_toolchain_1"));
+
+    DeclaredToolchainInfo registeredToolchain2 = value.registeredToolchains().get(1);
+    assertThat(registeredToolchain2).isNotNull();
+
+    assertThat(registeredToolchain2.toolchainType()).isEqualTo(testToolchainType);
+    assertThat(registeredToolchain2.execConstraints()).containsExactly(macConstraint);
+    assertThat(registeredToolchain2.targetConstraints()).containsExactly(linuxConstraint);
+    assertThat(registeredToolchain2.toolchainLabel())
+        .isEqualTo(makeLabel("//toolchain:test_toolchain_2"));
+  }
+
+  @Test
+  public void testRegisteredToolchains_notToolchain() throws Exception {
+    rewriteWorkspace("register_toolchains(", "    '//error:not_a_toolchain')");
+    scratch.file("error/BUILD", "filegroup(name = 'not_a_toolchain')");
+
+    // Request the toolchains.
+    SkyKey toolchainsKey = RegisteredToolchainsValue.key(targetConfig);
+    EvaluationResult<RegisteredToolchainsValue> result =
+        requestToolchainsFromSkyframe(toolchainsKey);
+    assertThatEvaluationResult(result)
+        .hasErrorEntryForKeyThat(toolchainsKey)
+        .hasExceptionThat()
+        .hasMessageThat()
+        .contains("target '//error:not_a_toolchain' does not provide a toolchain");
+  }
+
+  @Test
+  public void testRegisteredToolchains_reload() throws Exception {
+    rewriteWorkspace("register_toolchains('//toolchain:toolchain_1')");
+
+    SkyKey toolchainsKey = RegisteredToolchainsValue.key(targetConfig);
+    EvaluationResult<RegisteredToolchainsValue> result =
+        requestToolchainsFromSkyframe(toolchainsKey);
+    assertThatEvaluationResult(result).hasNoError();
+    RegisteredToolchainsValue value = result.get(toolchainsKey);
+    assertThat(value.registeredToolchains()).hasSize(1);
+    assertThat(value.registeredToolchains().get(0).toolchainLabel())
+        .isEqualTo(makeLabel("//toolchain:test_toolchain_1"));
+
+    // Re-write the WORKSPACE.
+    rewriteWorkspace("register_toolchains('//toolchain:toolchain_2')");
+
+    toolchainsKey = RegisteredToolchainsValue.key(targetConfig);
+    result = requestToolchainsFromSkyframe(toolchainsKey);
+    assertThatEvaluationResult(result).hasNoError();
+    value = result.get(toolchainsKey);
+    assertThat(value.registeredToolchains()).hasSize(1);
+    assertThat(value.registeredToolchains().get(0).toolchainLabel())
+        .isEqualTo(makeLabel("//toolchain:test_toolchain_2"));
+  }
+
+  @Test
+  public void testRegisteredToolchainsValue_equalsAndHashCode() {
+    DeclaredToolchainInfo toolchain1 =
+        DeclaredToolchainInfo.create(
+            makeLabel("//test:toolchain"),
+            ImmutableList.of(),
+            ImmutableList.of(),
+            makeLabel("//test/toolchain_impl_1"));
+    DeclaredToolchainInfo toolchain2 =
+        DeclaredToolchainInfo.create(
+            makeLabel("//test:toolchain"),
+            ImmutableList.of(),
+            ImmutableList.of(),
+            makeLabel("//test/toolchain_impl_2"));
+
+    new EqualsTester()
+        .addEqualityGroup(
+            RegisteredToolchainsValue.create(ImmutableList.of(toolchain1, toolchain2)),
+            RegisteredToolchainsValue.create(ImmutableList.of(toolchain1, toolchain2)))
+        .addEqualityGroup(
+            RegisteredToolchainsValue.create(ImmutableList.of(toolchain1)),
+            RegisteredToolchainsValue.create(ImmutableList.of(toolchain2)),
+            RegisteredToolchainsValue.create(ImmutableList.of(toolchain2, toolchain1)));
+  }
+}