Add rules_cros test rules and emulation toolchain

Add emulation toolchain and test rules to run basic "Hello World"
binaries for rules_cros toolchain verfication.  rules_cros_(cc|rust)_test
rules run the CC and Rust binaries against an individual platform;
rules_cros_toolchain_test fans out to run rules_cros_(cc|rust)_test tests
against all platforms.

BUG=b:217770417
TEST=`bazel-5 test //test:rules_cros_platforms_test`

Change-Id: I415f260fbbd13fb1b9c850cee8f3daa61afc63a6
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/rules_cros/+/3442709
Reviewed-by: Chris McDonald <cjmcdonald@chromium.org>
Tested-by: Sloan Johnson <sloanjohnson@google.com>
diff --git a/cros/repositories.bzl b/cros/repositories.bzl
index ec556b9..2b3613c 100644
--- a/cros/repositories.bzl
+++ b/cros/repositories.bzl
@@ -35,4 +35,6 @@
         "@rules_cros//cros/toolchain/cc:cros-sdk-x86_64-cros-linux-gnu-cc-toolchain",
         "@rules_cros//cros/toolchain/rust:cros-sdk-x86_64-pc-linux-gnu-rust",
         "@rules_cros//cros/toolchain/rust:cros-sdk-x86_64-cros-linux-gnu-rust",
+        "@rules_cros//cros/toolchain/emulation:cros-sdk-x86_64-pc-linux-gnu-emulation",
+        "@rules_cros//cros/toolchain/emulation:cros-sdk-x86_64-cros-linux-gnu-emulation",
     )
diff --git a/cros/toolchain/emulation/BUILD b/cros/toolchain/emulation/BUILD
new file mode 100644
index 0000000..fc7f6f7
--- /dev/null
+++ b/cros/toolchain/emulation/BUILD
@@ -0,0 +1,51 @@
+# Copyright 2022 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+load(":toolchain.bzl", "emulation_toolchain")
+
+package(default_visibility = ["//visibility:public"])
+
+emulation_toolchain(
+    name = "cros-sdk-x86_64-pc-linux-gnu-emulation_impl",
+    emulator_path = "/lib64/ld-linux-x86-64.so.2",
+)
+
+emulation_toolchain(
+    name = "cros-sdk-x86_64-cros-linux-gnu-emulation_impl",
+    emulator_path = "qemu-x86_64",
+)
+
+toolchain(
+    name = "cros-sdk-x86_64-pc-linux-gnu-emulation",
+    exec_compatible_with = [
+        "@platforms//cpu:x86_64",
+        "@platforms//os:linux",
+    ],
+    target_compatible_with = [
+        "@platforms//cpu:x86_64",
+        "@platforms//os:linux",
+        "//cros/platforms/vendor:pc",
+    ],
+    toolchain = ":cros-sdk-x86_64-pc-linux-gnu-emulation_impl",
+    toolchain_type = ":toolchain_type",
+)
+
+toolchain(
+    name = "cros-sdk-x86_64-cros-linux-gnu-emulation",
+    exec_compatible_with = [
+        "@platforms//cpu:x86_64",
+        "@platforms//os:linux",
+    ],
+    target_compatible_with = [
+        "@platforms//cpu:x86_64",
+        "@platforms//os:linux",
+        "//cros/platforms/vendor:cros",
+    ],
+    toolchain = ":cros-sdk-x86_64-cros-linux-gnu-emulation_impl",
+    toolchain_type = ":toolchain_type",
+)
+
+toolchain_type(
+    name = "toolchain_type",
+)
diff --git a/cros/toolchain/emulation/toolchain.bzl b/cros/toolchain/emulation/toolchain.bzl
new file mode 100644
index 0000000..3bfdb43
--- /dev/null
+++ b/cros/toolchain/emulation/toolchain.bzl
@@ -0,0 +1,19 @@
+EmuInfo = provider(
+    fields = ["emulator_path"],
+)
+
+def _impl(ctx):
+    toolchain_info = platform_common.ToolchainInfo(
+        emuinfo = EmuInfo(
+            emulator_path = ctx.attr.emulator_path,
+        ),
+    )
+    return [toolchain_info]
+
+emulation_toolchain = rule(
+    implementation = _impl,
+    attrs = {
+        "emulator_path": attr.string(),
+    },
+    provides = [platform_common.ToolchainInfo],
+)
diff --git a/run_tests.sh b/run_tests.sh
new file mode 100755
index 0000000..01bc0e0
--- /dev/null
+++ b/run_tests.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+#
+# Copyright 2022 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# Runs the rules_cros tests.  Must be run in the chroot.
+
+if ! test -f "/etc/cros_chroot_version"; then
+  echo "Must be run in the chroot."
+  exit 1
+fi
+
+cd $(dirname ${BASH_SOURCE[0]})
+bazel-5 test --test_output=all //test:rules_cros_toolchain_test
diff --git a/test/BUILD.bazel b/test/BUILD.bazel
new file mode 100644
index 0000000..454fac4
--- /dev/null
+++ b/test/BUILD.bazel
@@ -0,0 +1,36 @@
+# Copyright 2022 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+load("@rules_rust//rust:defs.bzl", "rust_binary")
+load(":toolchain.bzl", "qemu_test", "toolchain_test")
+
+package(default_visibility = ["//visibility:private"])
+
+rust_binary(
+    name = "rust_toolchain_test",
+    srcs = glob(["src/**/*.rs"]),
+)
+
+cc_binary(
+    name = "cc_toolchain_test",
+    srcs = glob(["src/**/*.c"])
+)
+
+qemu_test(
+    name = "rules_cros_cc_test",
+    dep = ":cc_toolchain_test",
+)
+
+qemu_test(
+    name = "rules_cros_rust_test",
+    dep = ":rust_toolchain_test",
+)
+
+toolchain_test(
+    name = "rules_cros_toolchain_test",
+    deps = [
+        ":rules_cros_cc_test",
+        ":rules_cros_rust_test",
+    ]
+)
diff --git a/test/qemu_test_template.sh b/test/qemu_test_template.sh
new file mode 100644
index 0000000..98d5888
--- /dev/null
+++ b/test/qemu_test_template.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+#
+# Copyright 2022 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+BINARY=${1:-%PATH%}
+echo "Testing that $BINARY outputs "Hello, world!""
+if [ "$(%EMULATOR% "$BINARY")" != "Hello, world!" ]
+then
+  echo "$BINARY failed test"
+  echo
+  exit 1
+fi
+echo
diff --git a/test/src/hello.c b/test/src/hello.c
new file mode 100644
index 0000000..288568c
--- /dev/null
+++ b/test/src/hello.c
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2022 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <stdio.h>
+
+int main (int argc, char **argv)
+{
+        printf("Hello, world!\n");
+}
diff --git a/test/src/main.rs b/test/src/main.rs
new file mode 100644
index 0000000..8c52602
--- /dev/null
+++ b/test/src/main.rs
@@ -0,0 +1,7 @@
+// Copyright 2021 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+fn main() {
+    println!("Hello, world!");
+}
diff --git a/test/toolchain.bzl b/test/toolchain.bzl
new file mode 100644
index 0000000..20f6b09
--- /dev/null
+++ b/test/toolchain.bzl
@@ -0,0 +1,103 @@
+def _transition_impl(settings, attr):
+    _ignore = [settings, attr]
+
+    return {
+        "linux_x86_64": {
+            "//command_line_option:platforms": "//cros/platforms:linux_x86_64"
+        },
+        "cros_x86_64": {
+            "//command_line_option:platforms": "//cros/platforms:cros_x86_64"
+        },
+    }
+
+full_toolchain_fanout = transition(
+    implementation = _transition_impl,
+    inputs = [],
+    outputs = ["//command_line_option:platforms"],
+)
+
+_QEMU_TEST_TEMPLATE = "//test:qemu_test_template.sh"
+
+def _qemu_test_impl(ctx):
+    emu_toolchain = ctx.toolchains["//cros/toolchain/emulation:toolchain_type"]
+    emulator_path = emu_toolchain.emuinfo.emulator_path
+
+    ctx.actions.expand_template(
+        template = ctx.file._template,
+        output = ctx.outputs.executable,
+        substitutions = {
+            "%EMULATOR%": emulator_path,
+            "%PATH%": ctx.files.dep[0].short_path,
+        },
+    )
+
+    runfiles = ctx.runfiles(ctx.files.dep)
+    return [DefaultInfo(runfiles = runfiles)]
+
+qemu_test = rule(
+    implementation = _qemu_test_impl,
+    attrs = {
+        "dep": attr.label(mandatory = True, executable = True, cfg = "target"),
+        "_template": attr.label(
+            default = Label(_QEMU_TEST_TEMPLATE),
+            allow_single_file = True,
+        )
+    },
+    toolchains = ["//cros/toolchain/emulation:toolchain_type"],
+    test = True,
+)
+
+def _run_tests(test, args):
+    return """
+{test} {args}
+((err+=$?))
+""".format(test = test, args = " ".join(args))
+
+def _toolchain_test_impl(ctx):
+    script = ""
+    symlinks = {}
+
+    for platform in ctx.split_attr.deps:
+        for d in ctx.split_attr.deps[platform]:
+            executable = d.files_to_run.executable
+            # Need to specify symlinks with platform appended for the runfiles,
+            # otherwise they will overwrite eachother for each platform.
+            test_path = "{}.{}".format(executable.short_path, platform)
+            symlinks[test_path] = executable
+            args = []
+            for f in d.default_runfiles.files.to_list():
+                if f != executable:
+                    rf_path = "{}.{}".format(f.short_path, platform)
+                    args.append(rf_path)
+                    symlinks[rf_path] = f
+
+            script = "\n".join(
+                [script] +
+                [_run_tests(test_path, args)]
+            )
+
+    script = "\n".join(
+        ["#!/bin/bash"] +
+        ["err=0"] +
+        [script] +
+        ["exit $err"],
+    )
+
+    ctx.actions.write(
+        output = ctx.outputs.executable,
+        content = script,
+    )
+
+    runfiles = ctx.runfiles(symlinks = symlinks)
+    return [DefaultInfo(runfiles = runfiles)]
+
+toolchain_test = rule(
+    implementation = _toolchain_test_impl,
+    attrs = {
+        "deps": attr.label_list(cfg = full_toolchain_fanout),
+        "_allowlist_function_transition": attr.label(
+            default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
+        ),
+    },
+    test = True,
+)