[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);
+}