[vulkan_loader] Add integration test

Route /pkg from a component as system-lib, and use that to test whether
vulkan-loader can load executable VMOs.

Change-Id: I7cfade3e989e56b0629b79db5ef47da1c1d9555d
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/490017
Fuchsia-Auto-Submit: John Bauman <jbauman@google.com>
Reviewed-by: Bryan Henry <bryanhenry@google.com>
Reviewed-by: Craig Stout <cstout@google.com>
Commit-Queue: Auto-Submit <auto-submit@fuchsia-infra.iam.gserviceaccount.com>
diff --git a/src/graphics/BUILD.gn b/src/graphics/BUILD.gn
index 0b6a803..c2dc26e 100644
--- a/src/graphics/BUILD.gn
+++ b/src/graphics/BUILD.gn
@@ -12,6 +12,7 @@
 group("tests") {
   testonly = true
   deps = [
+    "bin/vulkan_loader:tests",
     "display:tests",
     "drivers:tests",
     "lib/compute:tests",
diff --git a/src/graphics/bin/vulkan_loader/BUILD.gn b/src/graphics/bin/vulkan_loader/BUILD.gn
index e5bc6c1..f3652b0 100644
--- a/src/graphics/bin/vulkan_loader/BUILD.gn
+++ b/src/graphics/bin/vulkan_loader/BUILD.gn
@@ -4,6 +4,11 @@
 
 import("//src/sys/build/components.gni")
 
+group("tests") {
+  testonly = true
+  deps = [ ":vulkan_loader_tests" ]
+}
+
 executable("bin") {
   output_name = "vulkan_loader"
 
@@ -30,3 +35,65 @@
 fuchsia_package("vulkan_loader") {
   deps = [ ":vulkan_loader_cmp" ]
 }
+
+executable("test_bin") {
+  testonly = true
+  output_name = "vulkan_loader_test"
+  sources = [ "test.cc" ]
+  deps = [
+    "//sdk/fidl/fuchsia.vulkan.loader",
+    "//sdk/lib/fdio",
+    "//src/lib/fxl",
+    "//src/lib/fxl/test:gtest_main",
+    "//third_party/googletest:gtest",
+  ]
+}
+
+executable("pkg-server-bin") {
+  testonly = true
+  output_name = "pkg-server"
+
+  sources = [ "pkg-server-main.cc" ]
+
+  deps = [
+    "//sdk/fidl/fuchsia.io",
+    "//sdk/lib/fdio",
+    "//sdk/lib/fidl/cpp",
+    "//sdk/lib/sys/cpp",
+    "//src/lib/fxl",
+    "//src/lib/storage/vfs/cpp",
+    "//zircon/system/ulib/async-loop:async-loop-cpp",
+    "//zircon/system/ulib/async-loop:async-loop-default",
+  ]
+}
+
+fuchsia_component("vulkan_loader_test_driver") {
+  testonly = true
+  manifest = "meta/vulkan_loader_test_driver.cml"
+  deps = [ ":test_bin" ]
+}
+
+fuchsia_component("vulkan_loader_test_lib_provider") {
+  testonly = true
+  manifest = "meta/vulkan_loader_test_lib_provider.cml"
+  deps = [ ":pkg-server-bin" ]
+}
+
+fuchsia_component("vulkan_loader_test") {
+  testonly = true
+  manifest = "meta/vulkan_loader_test.cml"
+}
+
+fuchsia_test_package("vulkan_loader_tests") {
+  test_components = [ ":vulkan_loader_test" ]
+  deps = [
+    ":vulkan_loader_cmp",
+    ":vulkan_loader_test_driver",
+    ":vulkan_loader_test_lib_provider",
+  ]
+  test_specs = {
+    log_settings = {
+      max_severity = "ERROR"
+    }
+  }
+}
diff --git a/src/graphics/bin/vulkan_loader/meta/vulkan_loader_test.cml b/src/graphics/bin/vulkan_loader/meta/vulkan_loader_test.cml
new file mode 100644
index 0000000..f257270
--- /dev/null
+++ b/src/graphics/bin/vulkan_loader/meta/vulkan_loader_test.cml
@@ -0,0 +1,46 @@
+{
+    children: [
+        {
+            name: "test_driver",
+            url: "fuchsia-pkg://fuchsia.com/vulkan_loader_tests#meta/vulkan_loader_test_driver.cm",
+        },
+        {
+            name: "vulkan_loader",
+            url: "fuchsia-pkg://fuchsia.com/vulkan_loader_tests#meta/vulkan_loader.cm",
+        },
+        {
+            name: "test_lib_provider",
+            url: "fuchsia-pkg://fuchsia.com/vulkan_loader_tests#meta/vulkan_loader_test_lib_provider.cm",
+        },
+    ],
+    offer: [
+        {
+            protocol: [
+                "fuchsia.logger.Log",
+                "fuchsia.logger.LogSink",
+            ],
+            from: "parent",
+            to: [
+                "#test_driver",
+                "#test_lib_provider",
+                "#vulkan_loader",
+            ],
+        },
+        {
+            protocol: "fuchsia.vulkan.loader.Loader",
+            from: "#vulkan_loader",
+            to: [ "#test_driver" ],
+        },
+        {
+            directory: "system-lib",
+            from: "#test_lib_provider",
+            to: [ "#vulkan_loader" ],
+        },
+    ],
+    expose: [
+        {
+            protocol: "fuchsia.test.Suite",
+            from: "#test_driver",
+        },
+    ],
+}
diff --git a/src/graphics/bin/vulkan_loader/meta/vulkan_loader_test_driver.cml b/src/graphics/bin/vulkan_loader/meta/vulkan_loader_test_driver.cml
new file mode 100644
index 0000000..0ba0cda
--- /dev/null
+++ b/src/graphics/bin/vulkan_loader/meta/vulkan_loader_test_driver.cml
@@ -0,0 +1,12 @@
+{
+    include: [
+        "sdk/lib/diagnostics/syslog/client.shard.cml",
+        "src/sys/test_runners/gtest/default.shard.cml",
+    ],
+    program: {
+        binary: "bin/vulkan_loader_test",
+    },
+    use: [
+        { protocol: "fuchsia.vulkan.loader.Loader" },
+    ],
+}
diff --git a/src/graphics/bin/vulkan_loader/meta/vulkan_loader_test_lib_provider.cml b/src/graphics/bin/vulkan_loader/meta/vulkan_loader_test_lib_provider.cml
new file mode 100644
index 0000000..e9c67bd
--- /dev/null
+++ b/src/graphics/bin/vulkan_loader/meta/vulkan_loader_test_lib_provider.cml
@@ -0,0 +1,25 @@
+{
+    include: [
+        "sdk/lib/diagnostics/syslog/client.shard.cml",
+        "sdk/lib/diagnostics/syslog/elf_stdio.shard.cml",
+    ],
+    program: {
+        binary: "bin/pkg-server",
+    },
+    capabilities: [
+        {
+            // Use /bin because we know that the pkg-server executable will always be there.
+            directory: "bin",
+            rights: [ "rx*" ],
+            path: "/pkg/bin",
+        },
+    ],
+    expose: [
+        {
+            directory: "bin",
+            from: "self",
+            as: "system-lib",
+            rights: [ "rx*" ],
+        },
+    ],
+}
diff --git a/src/graphics/bin/vulkan_loader/pkg-server-main.cc b/src/graphics/bin/vulkan_loader/pkg-server-main.cc
new file mode 100644
index 0000000..7cbe63e
--- /dev/null
+++ b/src/graphics/bin/vulkan_loader/pkg-server-main.cc
@@ -0,0 +1,52 @@
+// Copyright 2021 The Fuchsia 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 <fuchsia/io/cpp/fidl.h>
+#include <lib/async-loop/cpp/loop.h>
+#include <lib/async-loop/default.h>
+#include <lib/fdio/directory.h>
+#include <lib/fdio/io.h>
+#include <lib/sys/cpp/component_context.h>
+#include <lib/syslog/cpp/macros.h>
+#include <zircon/processargs.h>
+
+#include "src/lib/fxl/command_line.h"
+#include "src/lib/fxl/log_settings_command_line.h"
+#include "src/lib/storage/vfs/cpp/pseudo_dir.h"
+#include "src/lib/storage/vfs/cpp/remote_dir.h"
+#include "src/lib/storage/vfs/cpp/synchronous_vfs.h"
+#include "src/lib/storage/vfs/cpp/vfs.h"
+#include "src/lib/storage/vfs/cpp/vfs_types.h"
+
+// Serve /pkg as the outgoing directory.
+int main(int argc, const char* const* argv) {
+  async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
+  fxl::SetLogSettingsFromCommandLine(fxl::CommandLineFromArgcArgv(argc, argv));
+  fidl::InterfaceHandle<fuchsia::io::Directory> pkg_dir;
+  zx_status_t status;
+  status = fdio_open("/pkg", fuchsia::io::OPEN_RIGHT_READABLE,
+                     pkg_dir.NewRequest().TakeChannel().release());
+  if (status != ZX_OK) {
+    FX_PLOGST(FATAL, nullptr, status) << "Failed to open package";
+    return -1;
+  }
+
+  // Use fs:: instead of vfs:: because vfs doesn't support executable directories.
+  fs::SynchronousVfs vfs(loop.dispatcher());
+  auto root = fbl::MakeRefCounted<fs::PseudoDir>();
+  auto remote = fbl::MakeRefCounted<fs::RemoteDir>(
+      fidl::ClientEnd<fuchsia_io::Directory>(pkg_dir.TakeChannel()));
+  root->AddEntry("pkg", remote);
+  zx::channel dir_request = zx::channel(zx_take_startup_handle(PA_DIRECTORY_REQUEST));
+  status = vfs.Serve(root, fidl::ServerEnd<fuchsia_io::Node>(std::move(dir_request)),
+                     fs::VnodeConnectionOptions::ReadExec());
+
+  if (status != ZX_OK) {
+    FX_PLOGST(FATAL, nullptr, status) << "Failed to serve outgoing.";
+    return -1;
+  }
+
+  loop.Run();
+  return 0;
+}
diff --git a/src/graphics/bin/vulkan_loader/test.cc b/src/graphics/bin/vulkan_loader/test.cc
new file mode 100644
index 0000000..3fbc69b
--- /dev/null
+++ b/src/graphics/bin/vulkan_loader/test.cc
@@ -0,0 +1,26 @@
+// Copyright 2021 The Fuchsia 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 <fuchsia/vulkan/loader/cpp/fidl.h>
+#include <lib/fdio/directory.h>
+#include <lib/zx/vmo.h>
+
+#include <gtest/gtest.h>
+
+TEST(VulkanLoader, SystemLoad) {
+  fuchsia::vulkan::loader::LoaderSyncPtr loader;
+  EXPECT_EQ(ZX_OK, fdio_service_connect("/svc/fuchsia.vulkan.loader.Loader",
+                                        loader.NewRequest().TakeChannel().release()));
+
+  zx::vmo vmo_out;
+  // The test instance reads from /pkg/bin, and this executable is guaranteed to be there.
+  EXPECT_EQ(ZX_OK, loader->Get("pkg-server", &vmo_out));
+  EXPECT_TRUE(vmo_out.is_valid());
+  zx_info_handle_basic_t handle_info;
+  EXPECT_EQ(ZX_OK, vmo_out.get_info(ZX_INFO_HANDLE_BASIC, &handle_info, sizeof(handle_info),
+                                    nullptr, nullptr));
+  EXPECT_TRUE(handle_info.rights & ZX_RIGHT_EXECUTE);
+  EXPECT_EQ(ZX_OK, loader->Get("not-present", &vmo_out));
+  EXPECT_FALSE(vmo_out.is_valid());
+}