blob: 7154dad1e8ca67fe911ea2d36dbabc03c8ca8032 [file] [log] [blame]
// Copyright 2023 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 <fidl/fuchsia.hardware.usb.phy/cpp/driver/fidl.h>
#include <lib/async_patterns/testing/cpp/dispatcher_bound.h>
#include <lib/driver/outgoing/cpp/outgoing_directory.h>
#include <queue>
#include <ddktl/device.h>
#include <fbl/auto_lock.h>
#include <usb-phy/usb-phy.h>
#include <zxtest/zxtest.h>
#include "src/devices/testing/mock-ddk/mock-device.h"
namespace {
class FakeUsbPhyServerBase {
public:
~FakeUsbPhyServerBase() { EXPECT_TRUE(expected_connected_.empty()); }
zx_status_t ConnectStatusChanged(bool connected) {
fbl::AutoLock _(&lock_);
EXPECT_GT(expected_connected_.size(), 0);
EXPECT_EQ(expected_connected_.front(), connected);
expected_connected_.pop();
return ZX_OK;
}
void ExpectConnectStatusChanged(bool expected_connected) {
fbl::AutoLock _(&lock_);
expected_connected_.push(expected_connected);
}
private:
fbl::Mutex lock_;
std::queue<bool> expected_connected_ __TA_GUARDED(lock_);
};
template <class FakeUsbPhyServer>
class UsbPhyTest : public zxtest::Test {
public:
void GetClient(std::string_view fragment_name) {
auto result = usb_phy::UsbPhyClient::Create(root_.get(), fragment_name);
ASSERT_TRUE(result.is_ok());
client_ = std::move(*result);
}
void GetClient() {
auto result = usb_phy::UsbPhyClient::Create(root_.get());
ASSERT_TRUE(result.is_ok());
client_ = std::move(*result);
}
void TestConnectStatusChanged() {
incoming_.SyncCall(
[](IncomingNamespace* ns) { ns->fake_phy_server_.ExpectConnectStatusChanged(true); });
client_->ConnectStatusChanged(true);
incoming_.SyncCall(
[](IncomingNamespace* ns) { ns->fake_phy_server_.ExpectConnectStatusChanged(false); });
client_->ConnectStatusChanged(false);
}
protected:
std::shared_ptr<zx_device_t> root_ = MockDevice::FakeRootParent();
fdf::UnownedSynchronizedDispatcher dispatcher_ =
fdf_testing::DriverRuntime::GetInstance()->StartBackgroundDispatcher();
struct IncomingNamespace {
fdf::OutgoingDirectory outgoing =
fdf::OutgoingDirectory::Create(fdf::Dispatcher::GetCurrent()->get());
FakeUsbPhyServer fake_phy_server_;
};
async_patterns::TestDispatcherBound<IncomingNamespace> incoming_{dispatcher_->async_dispatcher(),
std::in_place};
std::optional<usb_phy::UsbPhyClient> client_;
};
// FIDL Tests.
namespace fidl_tests {
class FakeUsbPhyFidlServer : public FakeUsbPhyServerBase,
public fdf::Server<fuchsia_hardware_usb_phy::UsbPhy> {
public:
void ConnectStatusChanged(ConnectStatusChangedRequest& request,
ConnectStatusChangedCompleter::Sync& completer) override {
auto status = FakeUsbPhyServerBase::ConnectStatusChanged(request.connected());
if (status != ZX_OK) {
completer.Reply(fit::error(status));
return;
}
completer.Reply(fit::ok());
}
void handle_unknown_method(fidl::UnknownMethodMetadata<fuchsia_hardware_usb_phy::UsbPhy> metadata,
fidl::UnknownMethodCompleter::Sync& completer) override {
ASSERT_TRUE(false);
}
fuchsia_hardware_usb_phy::Service::InstanceHandler GetInstanceHandler() {
return fuchsia_hardware_usb_phy::Service::InstanceHandler({
.device = bindings_.CreateHandler(this, fdf::Dispatcher::GetCurrent()->get(),
fidl::kIgnoreBindingClosure),
});
}
private:
fdf::ServerBindingGroup<fuchsia_hardware_usb_phy::UsbPhy> bindings_;
};
class UsbPhyFidlTest : public UsbPhyTest<FakeUsbPhyFidlServer> {
public:
void SetUpAsFragment() {
auto io_eps = fidl::CreateEndpoints<fuchsia_io::Directory>();
ASSERT_OK(io_eps);
incoming_.SyncCall([&](IncomingNamespace* ns) {
ASSERT_OK(ns->outgoing.AddService<fuchsia_hardware_usb_phy::Service>(
ns->fake_phy_server_.GetInstanceHandler()),
"phy");
ASSERT_OK(ns->outgoing.Serve(std::move(io_eps->server)));
});
root_->AddFidlService(fuchsia_hardware_usb_phy::Service::Name, std::move(io_eps->client),
"phy");
GetClient("phy");
}
void SetUpAsParent() {
auto io_eps = fidl::CreateEndpoints<fuchsia_io::Directory>();
ASSERT_OK(io_eps);
incoming_.SyncCall([&](IncomingNamespace* ns) {
ASSERT_OK(ns->outgoing.AddService<fuchsia_hardware_usb_phy::Service>(
ns->fake_phy_server_.GetInstanceHandler()));
ASSERT_OK(ns->outgoing.Serve(std::move(io_eps->server)));
});
root_->AddFidlService(fuchsia_hardware_usb_phy::Service::Name, std::move(io_eps->client));
GetClient();
}
};
TEST_F(UsbPhyFidlTest, AsFragment) {
SetUpAsFragment();
TestConnectStatusChanged();
}
TEST_F(UsbPhyFidlTest, AsParent) {
SetUpAsParent();
TestConnectStatusChanged();
}
} // namespace fidl_tests
// Banjo Tests
namespace banjo_tests {
class FakeUsbPhyBanjoServer
: public FakeUsbPhyServerBase,
public ddk::UsbPhyProtocol<FakeUsbPhyBanjoServer, ddk::base_protocol> {
public:
void UsbPhyConnectStatusChanged(bool connected) {
ASSERT_OK(FakeUsbPhyServerBase::ConnectStatusChanged(connected));
}
const usb_phy_protocol_ops_t* PhyOps() { return &usb_phy_protocol_ops_; }
};
class UsbPhyBanjoTest : public UsbPhyTest<FakeUsbPhyBanjoServer> {
public:
void SetUpAsFragment() {
incoming_.SyncCall([&](IncomingNamespace* ns) {
root_->AddProtocol(ZX_PROTOCOL_USB_PHY, ns->fake_phy_server_.PhyOps(), &ns->fake_phy_server_,
"phy");
});
GetClient("phy");
}
void SetUpAsParent() {
incoming_.SyncCall([&](IncomingNamespace* ns) {
root_->AddProtocol(ZX_PROTOCOL_USB_PHY, ns->fake_phy_server_.PhyOps(), &ns->fake_phy_server_);
});
GetClient();
}
};
TEST_F(UsbPhyBanjoTest, AsFragment) {
SetUpAsFragment();
TestConnectStatusChanged();
}
TEST_F(UsbPhyBanjoTest, AsParent) {
SetUpAsParent();
TestConnectStatusChanged();
}
} // namespace banjo_tests
} // namespace