[driver_manager] DriverLoader uses fallback drivers correctly

Update the DriverLoader DriverIndex code so the fallback drivers are
returned correctly.

Also refactor the code so that it is possible to unit-test, and unit
test the fallback driver code.

Bug: 81107
Change-Id: Ia59bb728780d3ea86acb1362a9f84ee46ad28f7b
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/560243
Fuchsia-Auto-Submit: David Gilhooley <dgilhooley@google.com>
Commit-Queue: Auto-Submit <auto-submit@fuchsia-infra.iam.gserviceaccount.com>
Reviewed-by: Suraj Malhotra <surajmalhotra@google.com>
diff --git a/src/devices/bin/driver_manager/BUILD.gn b/src/devices/bin/driver_manager/BUILD.gn
index 2b4ceee2..e876d14 100644
--- a/src/devices/bin/driver_manager/BUILD.gn
+++ b/src/devices/bin/driver_manager/BUILD.gn
@@ -162,6 +162,7 @@
     "coordinator_test_utils.cc",
     "devfs_test.cc",
     "device_tests.cc",
+    "driver_loader_test.cc",
     "init_tests.cc",
     "inspect_test.cc",
     "load_driver_package_test.cc",
diff --git a/src/devices/bin/driver_manager/coordinator.cc b/src/devices/bin/driver_manager/coordinator.cc
index 0284acb..2ce21ef 100644
--- a/src/devices/bin/driver_manager/coordinator.cc
+++ b/src/devices/bin/driver_manager/coordinator.cc
@@ -107,7 +107,9 @@
                          async_dispatcher_t* dispatcher)
     : config_(std::move(config)),
       dispatcher_(dispatcher),
-      driver_loader_(config.boot_args, this),
+      base_resolver_(config_.boot_args),
+      driver_loader_(config_.boot_args, std::move(config_.driver_index), &base_resolver_,
+                     config_.require_system),
       suspend_handler_(this, config.suspend_fallback, config.suspend_timeout),
       inspect_manager_(inspect_manager),
       package_resolver_(config.boot_args) {
@@ -1170,7 +1172,7 @@
   // cannot bind driver to already bound device
   if ((dev->flags & DEV_CTX_BOUND) &&
       !(dev->flags & (DEV_CTX_MULTI_BIND | DEV_CTX_ALLOW_MULTI_COMPOSITE))) {
-    return ZX_ERR_BAD_STATE;
+    return ZX_ERR_ALREADY_BOUND;
   }
   if (!(dev->flags & DEV_CTX_MUST_ISOLATE)) {
     // non-busdev is pretty simple
@@ -1378,7 +1380,7 @@
 
   status = attempt_bind(drv, dev);
   if (status != ZX_OK) {
-    LOGF(ERROR, "Failed to bind driver '%s' to device '%s': %s", drv->name.data(),
+    LOGF(ERROR, "%s: Failed to bind driver '%s' to device '%s': %s", __func__, drv->name.data(),
          dev->name().data(), zx_status_get_string(status));
   }
   if (status == ZX_ERR_NEXT) {
@@ -1461,23 +1463,34 @@
 }
 
 void Coordinator::ScheduleBaseDriverLoading() {
-  auto result = config_.driver_index->WaitForBaseDrivers(
-      [this](fidl::WireResponse<fdf::DriverIndex::WaitForBaseDrivers>* response) {
-        BindAllDevicesDriverIndex();
-      });
-  if (!result.ok()) {
-    // TODO(dgilhooley): Change this back to an ERROR once DriverIndex is included in the build.
-    LOGF(INFO, "Failed to connect to DriverIndex: %d", result.status());
-  }
+  driver_loader_.WaitForBaseDrivers([this]() { BindAllDevicesDriverIndex(); });
 }
 
 zx_status_t Coordinator::MatchAndBindDeviceDriverIndex(const fbl::RefPtr<Device>& dev) {
+  if ((dev->flags & DEV_CTX_BOUND) && !(dev->flags & DEV_CTX_ALLOW_MULTI_COMPOSITE) &&
+      !(dev->flags & DEV_CTX_MULTI_BIND)) {
+    return ZX_ERR_ALREADY_BOUND;
+  }
+
+  if (dev->should_skip_autobind()) {
+    return ZX_ERR_NEXT;
+  }
+
+  if (!dev->is_bindable() && !(dev->is_composite_bindable())) {
+    return ZX_ERR_NEXT;
+  }
+
   auto drivers = driver_loader_.MatchDeviceDriverIndex(dev);
   for (auto driver : drivers) {
     zx_status_t status = AttemptBind(driver, dev);
-    if (status != ZX_OK && status != ZX_ERR_NEXT && status != ZX_ERR_ALREADY_BOUND) {
-      LOGF(ERROR, "Failed to bind driver '%s' to device '%s': %s", driver->libname.data(),
-           dev->name().data(), zx_status_get_string(status));
+    // If we get this here it means we've successfully bound one driver
+    // and the device isn' multi-bind.
+    if (status == ZX_ERR_ALREADY_BOUND) {
+      return ZX_OK;
+    }
+    if (status != ZX_OK) {
+      LOGF(ERROR, "%s: Failed to bind driver '%s' to device '%s': %s", __func__,
+           driver->libname.data(), dev->name().data(), zx_status_get_string(status));
     }
   }
   return ZX_OK;
@@ -1574,8 +1587,8 @@
   for (const Driver* driver : drivers) {
     zx_status_t status = AttemptBind(driver, dev);
     if (status != ZX_OK) {
-      LOGF(ERROR, "Failed to bind driver '%s' to device '%s': %s", driver->name.data(),
-           dev->name().data(), zx_status_get_string(status));
+      LOGF(ERROR, "%s: Failed to bind driver '%s' to device '%s': %s", __func__, dev->name().data(),
+           zx_status_get_string(status));
     }
   }
 
@@ -1606,7 +1619,7 @@
   }
 }
 
-void Coordinator::StartLoadingNonBootDrivers() { driver_loader_.StartSystemLoadingThread(); }
+void Coordinator::StartLoadingNonBootDrivers() { driver_loader_.StartSystemLoadingThread(this); }
 
 void Coordinator::BindFallbackDrivers() {
   for (auto& driver : fallback_drivers_) {
diff --git a/src/devices/bin/driver_manager/coordinator.h b/src/devices/bin/driver_manager/coordinator.h
index 644568b..54b5947 100644
--- a/src/devices/bin/driver_manager/coordinator.h
+++ b/src/devices/bin/driver_manager/coordinator.h
@@ -271,7 +271,6 @@
 
   void DumpState(VmoWriter* vmo) const;
 
-  fidl::WireSharedClient<fdf::DriverIndex>& driver_index() { return config_.driver_index; }
   async_dispatcher_t* dispatcher() const { return dispatcher_; }
   const zx::resource& root_resource() const { return config_.root_resource; }
   fidl::WireSyncClient<fuchsia_boot::Arguments>* boot_args() const { return config_.boot_args; }
@@ -360,6 +359,8 @@
   bool power_manager_registered_ = false;
   LoaderServiceConnector loader_service_connector_;
   fidl::WireSharedClient<fuchsia_power_manager::DriverManagerRegistration> power_manager_client_;
+
+  internal::BasePackageResolver base_resolver_;
   DriverLoader driver_loader_;
 
   // All Drivers
diff --git a/src/devices/bin/driver_manager/driver_loader.cc b/src/devices/bin/driver_manager/driver_loader.cc
index a201956..1506a8e 100644
--- a/src/devices/bin/driver_manager/driver_loader.cc
+++ b/src/devices/bin/driver_manager/driver_loader.cc
@@ -24,13 +24,13 @@
   }
 }
 
-void DriverLoader::StartSystemLoadingThread() {
+void DriverLoader::StartSystemLoadingThread(Coordinator* coordinator) {
   if (system_loading_thread_) {
     LOGF(ERROR, "DriverLoader: StartLoadingThread cannot be called twice!\n");
     return;
   }
 
-  system_loading_thread_ = std::thread([coordinator = coordinator_]() {
+  system_loading_thread_ = std::thread([coordinator]() {
     fbl::unique_fd fd(open("/system", O_RDONLY));
     if (fd.get() < 0) {
       LOGF(WARNING, "Unable to open '/system', system drivers are disabled");
@@ -77,6 +77,20 @@
   return nullptr;
 }
 
+void DriverLoader::WaitForBaseDrivers(fit::callback<void()> callback) {
+  auto result = driver_index_->WaitForBaseDrivers(
+      [this, callback = std::move(callback)](
+          fidl::WireResponse<fdf::DriverIndex::WaitForBaseDrivers>* response) mutable {
+        include_fallback_drivers_ = true;
+        callback();
+      });
+
+  // TODO(dgilhooley): Change this back to an ERROR once DriverIndex is used in all tests.
+  if (result.status() != ZX_OK) {
+    LOGF(INFO, "Failed to connect to DriverIndex: %d", result.status());
+  }
+}
+
 const Driver* DriverLoader::LoadDriverUrlDriverIndex(const std::string& driver_url) {
   // Check if we've already loaded this driver. If we have then return it.
   auto driver = LibnameToDriver(driver_url);
@@ -85,7 +99,7 @@
   }
 
   // We've never seen the driver before so add it, then return it.
-  auto fetched_driver = base_resolver_.FetchDriver(driver_url);
+  auto fetched_driver = base_resolver_->FetchDriver(driver_url);
   if (fetched_driver.is_error()) {
     LOGF(ERROR, "Error fetching driver: %s: %d", driver_url.data(), fetched_driver.error_value());
     return nullptr;
@@ -111,17 +125,14 @@
 
 std::vector<const Driver*> DriverLoader::MatchDeviceDriverIndex(const fbl::RefPtr<Device>& dev,
                                                                 std::string_view libname) {
-  std::vector<const Driver*> matched_drivers;
-
-  if (!coordinator_->driver_index().is_valid()) {
-    return matched_drivers;
+  if (!driver_index_.is_valid()) {
+    return std::vector<const Driver*>();
   }
 
   bool autobind = libname.empty();
 
   fidl::FidlAllocator allocator;
   auto& props = dev->props();
-  fdf::wire::NodeAddArgs args(allocator);
   fidl::VectorView<fdf::wire::NodeProperty> fidl_props(allocator, props.size() + 2);
 
   fidl_props[0] = fdf::wire::NodeProperty(allocator)
@@ -135,9 +146,22 @@
                             .set_key(allocator, props[i].id)
                             .set_value(allocator, props[i].value);
   }
-  args.set_properties(allocator, std::move(fidl_props));
+  return MatchPropertiesDriverIndex(fidl_props, libname);
+}
 
-  auto result = coordinator_->driver_index()->MatchDriversV1_Sync(std::move(args));
+std::vector<const Driver*> DriverLoader::MatchPropertiesDriverIndex(
+    fidl::VectorView<fdf::wire::NodeProperty> props, std::string_view libname) {
+  std::vector<const Driver*> matched_drivers;
+  std::vector<const Driver*> matched_fallback_drivers;
+  if (!driver_index_.is_valid()) {
+    return matched_drivers;
+  }
+
+  fidl::FidlAllocator allocator;
+  fdf::wire::NodeAddArgs args(allocator);
+  args.set_properties(allocator, std::move(props));
+
+  auto result = driver_index_->MatchDriversV1_Sync(std::move(args));
   if (!result.ok()) {
     if (result.status() == ZX_ERR_PEER_CLOSED) {
       return matched_drivers;
@@ -168,8 +192,17 @@
     std::string driver_url(driver.driver_url().get());
     auto loaded_driver = LoadDriverUrlDriverIndex(driver_url);
     if (libname.empty() || MatchesLibnameDriverIndex(driver_url, libname)) {
-      matched_drivers.push_back(loaded_driver);
+      if (loaded_driver->fallback) {
+        if (include_fallback_drivers_ || !libname.empty()) {
+          matched_fallback_drivers.push_back(loaded_driver);
+        }
+      } else {
+        matched_drivers.push_back(loaded_driver);
+      }
     }
   }
+
+  matched_drivers.insert(matched_drivers.end(), matched_fallback_drivers.begin(),
+                         matched_fallback_drivers.end());
   return matched_drivers;
 }
diff --git a/src/devices/bin/driver_manager/driver_loader.h b/src/devices/bin/driver_manager/driver_loader.h
index a091979..c17cea2 100644
--- a/src/devices/bin/driver_manager/driver_loader.h
+++ b/src/devices/bin/driver_manager/driver_loader.h
@@ -5,6 +5,7 @@
 #ifndef SRC_DEVICES_BIN_DRIVER_MANAGER_DRIVER_LOADER_H_
 #define SRC_DEVICES_BIN_DRIVER_MANAGER_DRIVER_LOADER_H_
 
+#include <fuchsia/driver/framework/llcpp/fidl.h>
 #include <threads.h>
 
 #include <fbl/intrusive_double_list.h>
@@ -15,13 +16,18 @@
 
 class Coordinator;
 
+namespace fdf = fuchsia_driver_framework;
+
 class DriverLoader {
  public:
   // Takes in an unowned connection to boot arguments. boot_args must outlive DriverLoader.
-  // Takes in an unowned connection to Coordinator. Coordinator must outlive DriverLoader.
+  // Takes in an unowned connection to base_resolver. base_resolver must outlive DriverLoader.
   explicit DriverLoader(fidl::WireSyncClient<fuchsia_boot::Arguments>* boot_args,
-                        Coordinator* coordinator)
-      : base_resolver_(boot_args), coordinator_(coordinator) {}
+                        fidl::WireSharedClient<fdf::DriverIndex> driver_index,
+                        internal::PackageResolverInterface* base_resolver, bool require_system)
+      : base_resolver_(base_resolver),
+        driver_index_(std::move(driver_index)),
+        include_fallback_drivers_(!require_system) {}
 
   ~DriverLoader();
 
@@ -29,10 +35,17 @@
   // DriverLoader will join this thread when it destructs.
   // `coordinator_` is not thread safe, so any calls to it must be made on the
   // `coordinator_->dispatcher()` thread.
-  void StartSystemLoadingThread();
+  void StartSystemLoadingThread(Coordinator* coordinator);
 
+  // This will schedule a task on the async_dispatcher that will return
+  // when DriverIndex has loaded the base drivers. When the task completes,
+  // `callback` will be called.
+  void WaitForBaseDrivers(fit::callback<void()> callback);
   std::vector<const Driver*> MatchDeviceDriverIndex(const fbl::RefPtr<Device>& dev,
                                                     std::string_view libname = "");
+  std::vector<const Driver*> MatchPropertiesDriverIndex(
+      fidl::VectorView<fdf::wire::NodeProperty> props, std::string_view libname = "");
+
   const Driver* LibnameToDriver(std::string_view libname) const;
 
  private:
@@ -43,9 +56,13 @@
   // Drivers we cached from the DriverIndex.
   fbl::DoublyLinkedList<std::unique_ptr<Driver>> driver_index_drivers_;
 
-  internal::BasePackageResolver base_resolver_;
+  internal::PackageResolverInterface* base_resolver_;
   std::optional<std::thread> system_loading_thread_;
-  Coordinator* coordinator_ = nullptr;
+  fidl::WireSharedClient<fdf::DriverIndex> driver_index_;
+
+  // When this is true we will return DriverIndex fallback drivers.
+  // This is true after the system is loaded (or if require_system is false)
+  bool include_fallback_drivers_;
 };
 
 #endif  // SRC_DEVICES_BIN_DRIVER_MANAGER_DRIVER_LOADER_H_
diff --git a/src/devices/bin/driver_manager/driver_loader_test.cc b/src/devices/bin/driver_manager/driver_loader_test.cc
new file mode 100644
index 0000000..7433da0
--- /dev/null
+++ b/src/devices/bin/driver_manager/driver_loader_test.cc
@@ -0,0 +1,213 @@
+// 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 "src/devices/bin/driver_manager/driver_loader.h"
+
+#include <lib/async-loop/cpp/loop.h>
+#include <lib/async-loop/default.h>
+
+#include <zxtest/zxtest.h>
+
+#include "fbl/ref_ptr.h"
+#include "src/devices/bin/driver_manager/init_task.h"
+#include "src/devices/bin/driver_manager/resume_task.h"
+#include "src/devices/bin/driver_manager/suspend_task.h"
+#include "src/devices/bin/driver_manager/unbind_task.h"
+
+class FakeResolver : public internal::PackageResolverInterface {
+ public:
+  zx::status<std::unique_ptr<Driver>> FetchDriver(const std::string& package_url) override {
+    if (map.count(package_url) != 0) {
+      auto driver = std::move(map[package_url]);
+      map.erase(package_url);
+      return zx::ok(std::move(driver));
+    }
+    return zx::error(ZX_ERR_NOT_FOUND);
+  }
+
+  std::map<std::string, std::unique_ptr<Driver>> map;
+};
+
+class FakeDriverIndex final : public fidl::WireServer<fuchsia_driver_framework::DriverIndex> {
+ public:
+  void MatchDriver(MatchDriverRequestView request, MatchDriverCompleter::Sync& completer) override {
+    completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
+  }
+
+  void WaitForBaseDrivers(WaitForBaseDriversRequestView request,
+                          WaitForBaseDriversCompleter::Sync& completer) override {
+    completer.Reply();
+  }
+
+  // The fake driver index is used only for drivers-as-components and so it
+  // doesn't need any V1 APIs.
+  void MatchDriversV1(MatchDriversV1RequestView request,
+                      MatchDriversV1Completer::Sync& completer) override {
+    fidl::FidlAllocator allocator;
+    fidl::VectorView<fuchsia_driver_framework::wire::MatchedDriver> drivers(allocator,
+                                                                            driver_urls.size());
+    size_t index = 0;
+    for (auto& driver : driver_urls) {
+      drivers[index] = fuchsia_driver_framework::wire::MatchedDriver(allocator);
+      drivers[index].set_driver_url(allocator, fidl::StringView::FromExternal(driver));
+      index++;
+    }
+    completer.ReplySuccess(drivers);
+  }
+
+  std::vector<std::string> driver_urls;
+};
+
+class DriverLoaderTest : public zxtest::Test {
+ public:
+  void SetUp() override {
+    auto endpoints = fidl::CreateEndpoints<fuchsia_driver_framework::DriverIndex>();
+    ASSERT_FALSE(endpoints.is_error());
+    fidl::BindServer(loop.dispatcher(), std::move(endpoints->server), &driver_index_server);
+    driver_index = fidl::WireSharedClient<fuchsia_driver_framework::DriverIndex>(
+        std::move(endpoints->client), loop.dispatcher());
+  }
+
+  void TearDown() override {}
+
+  async::Loop loop = async::Loop(&kAsyncLoopConfigNeverAttachToThread);
+  FakeDriverIndex driver_index_server;
+  FakeResolver resolver;
+  fidl::WireSharedClient<fuchsia_driver_framework::DriverIndex> driver_index;
+};
+
+TEST_F(DriverLoaderTest, TestFallbackGetsRemoved) {
+  std::string not_fallback_libname = "fuchsia_boot:///#not_fallback.so";
+  std::string fallback_libname = "fuchsia_boot:///#fallback.so";
+
+  driver_index_server.driver_urls.push_back(not_fallback_libname);
+  driver_index_server.driver_urls.push_back(fallback_libname);
+
+  auto not_fallback = std::make_unique<Driver>();
+  not_fallback->libname = not_fallback_libname;
+  resolver.map[not_fallback_libname] = std::move(not_fallback);
+
+  auto fallback = std::make_unique<Driver>();
+  fallback->libname = fallback_libname;
+  fallback->fallback = true;
+  resolver.map[fallback_libname] = std::move(fallback);
+
+  DriverLoader driver_loader(nullptr, std::move(driver_index), &resolver, true);
+  loop.StartThread("fidl-thread");
+
+  fidl::VectorView<fuchsia_driver_framework::wire::NodeProperty> props{};
+  auto drivers = driver_loader.MatchPropertiesDriverIndex(std::move(props));
+  ASSERT_EQ(drivers.size(), 1);
+  ASSERT_EQ(drivers[0]->libname, not_fallback_libname);
+}
+
+TEST_F(DriverLoaderTest, TestFallbackAcceptedAfterBaseLoaded) {
+  std::string not_fallback_libname = "fuchsia_boot:///#not_fallback.so";
+  std::string fallback_libname = "fuchsia_boot:///#fallback.so";
+
+  driver_index_server.driver_urls.push_back(not_fallback_libname);
+  driver_index_server.driver_urls.push_back(fallback_libname);
+
+  auto not_fallback = std::make_unique<Driver>();
+  not_fallback->libname = not_fallback_libname;
+  resolver.map[not_fallback_libname] = std::move(not_fallback);
+
+  auto fallback = std::make_unique<Driver>();
+  fallback->libname = fallback_libname;
+  fallback->fallback = true;
+  resolver.map[fallback_libname] = std::move(fallback);
+
+  DriverLoader driver_loader(nullptr, std::move(driver_index), &resolver, true);
+  loop.StartThread("fidl-thread");
+
+  // Wait for base drivers, which is when we load fallback drivers.
+  sync_completion_t base_drivers;
+  driver_loader.WaitForBaseDrivers([&base_drivers]() { sync_completion_signal(&base_drivers); });
+  sync_completion_wait(&base_drivers, ZX_TIME_INFINITE);
+
+  fidl::VectorView<fuchsia_driver_framework::wire::NodeProperty> props{};
+  auto drivers = driver_loader.MatchPropertiesDriverIndex(std::move(props));
+  ASSERT_EQ(drivers.size(), 2);
+  // The non-fallback should always be first.
+  ASSERT_EQ(drivers[0]->libname, not_fallback_libname);
+  ASSERT_EQ(drivers[1]->libname, fallback_libname);
+}
+
+TEST_F(DriverLoaderTest, TestFallbackAcceptedWhenSystemNotRequired) {
+  std::string not_fallback_libname = "fuchsia_boot:///#not_fallback.so";
+  std::string fallback_libname = "fuchsia_boot:///#fallback.so";
+
+  driver_index_server.driver_urls.push_back(not_fallback_libname);
+  driver_index_server.driver_urls.push_back(fallback_libname);
+
+  auto not_fallback = std::make_unique<Driver>();
+  not_fallback->libname = not_fallback_libname;
+  resolver.map[not_fallback_libname] = std::move(not_fallback);
+
+  auto fallback = std::make_unique<Driver>();
+  fallback->libname = fallback_libname;
+  fallback->fallback = true;
+  resolver.map[fallback_libname] = std::move(fallback);
+
+  DriverLoader driver_loader(nullptr, std::move(driver_index), &resolver, false);
+  loop.StartThread("fidl-thread");
+
+  fidl::VectorView<fuchsia_driver_framework::wire::NodeProperty> props{};
+  auto drivers = driver_loader.MatchPropertiesDriverIndex(std::move(props));
+  ASSERT_EQ(drivers.size(), 2);
+  // The non-fallback should always be first.
+  ASSERT_EQ(drivers[0]->libname, not_fallback_libname);
+  ASSERT_EQ(drivers[1]->libname, fallback_libname);
+}
+
+TEST_F(DriverLoaderTest, TestLibname) {
+  std::string name1 = "fuchsia_boot:///#driver1.so";
+  std::string name2 = "fuchsia_boot:///#driver2.so";
+
+  driver_index_server.driver_urls.push_back(name1);
+  driver_index_server.driver_urls.push_back(name2);
+
+  auto driver1 = std::make_unique<Driver>();
+  driver1->libname = name1;
+  resolver.map[name1] = std::move(driver1);
+
+  auto driver2 = std::make_unique<Driver>();
+  driver2->libname = name2;
+  resolver.map[name2] = std::move(driver2);
+
+  DriverLoader driver_loader(nullptr, std::move(driver_index), &resolver, true);
+  loop.StartThread("fidl-thread");
+
+  fidl::VectorView<fuchsia_driver_framework::wire::NodeProperty> props{};
+  auto drivers = driver_loader.MatchPropertiesDriverIndex(std::move(props), name2);
+  ASSERT_EQ(drivers.size(), 1);
+  ASSERT_EQ(drivers[0]->libname, name2);
+}
+
+TEST_F(DriverLoaderTest, TestLibnameConvertToPath) {
+  std::string name1 = "fuchsia-pkg://fuchsia.com/my-package#driver/#driver1.so";
+  std::string name2 = "fuchsia-boot:///#driver/driver2.so";
+
+  driver_index_server.driver_urls.push_back(name1);
+  driver_index_server.driver_urls.push_back(name2);
+
+  auto driver1 = std::make_unique<Driver>();
+  driver1->libname = name1;
+  resolver.map[name1] = std::move(driver1);
+
+  auto driver2 = std::make_unique<Driver>();
+  driver2->libname = name2;
+  resolver.map[name2] = std::move(driver2);
+
+  DriverLoader driver_loader(nullptr, std::move(driver_index), &resolver, true);
+  loop.StartThread("fidl-thread");
+
+  fidl::VectorView<fuchsia_driver_framework::wire::NodeProperty> props{};
+
+  // We can also match libname by the path that the URL turns into.
+  auto drivers =
+      driver_loader.MatchPropertiesDriverIndex(std::move(props), "/boot/driver/driver2.so");
+  ASSERT_EQ(drivers.size(), 1);
+  ASSERT_EQ(drivers[0]->libname, name2);
+}