| // Copyright 2022 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 "overnet_usb.h" |
| |
| #include <fuchsia/hardware/usb/function/cpp/banjo-mock.h> |
| #include <lib/ddk/metadata.h> |
| #include <lib/driver/testing/cpp/driver_test.h> |
| #include <lib/sync/completion.h> |
| #include <zircon/compiler.h> |
| |
| #include <algorithm> |
| #include <cstdint> |
| #include <memory> |
| #include <optional> |
| #include <string_view> |
| #include <utility> |
| #include <vector> |
| |
| #include <zxtest/zxtest.h> |
| |
| #include "fbl/auto_lock.h" |
| #include "fbl/mutex.h" |
| #include "fidl/fuchsia.hardware.overnet/cpp/markers.h" |
| #include "fidl/fuchsia.hardware.usb.function/cpp/fidl.h" |
| #include "lib/driver/compat/cpp/banjo_server.h" |
| #include "lib/driver/compat/cpp/device_server.h" |
| #include "lib/fidl/cpp/wire/channel.h" |
| #include "lib/fidl/cpp/wire/internal/transport_channel.h" |
| #include "src/devices/usb/lib/usb-endpoint/testing/fake-usb-endpoint-server.h" |
| |
| // NOLINTBEGIN(misc-use-anonymous-namespace) |
| // NOLINTBEGIN(readability-convert-member-functions-to-static) |
| // NOLINTBEGIN(readability-container-data-pointer) |
| |
| static constexpr uint8_t kBulkOutEndpoint = 0; |
| static constexpr uint8_t kBulkInEndpoint = 1; |
| |
| bool operator==(const usb_request_complete_callback_t& lhs, |
| const usb_request_complete_callback_t& rhs) { |
| // Comparison of these struct is not useful. Return true always. |
| return true; |
| } |
| |
| bool operator==(const usb_ss_ep_comp_descriptor_t& lhs, const usb_ss_ep_comp_descriptor_t& rhs) { |
| // Comparison of these struct is not useful. Return true always. |
| return true; |
| } |
| |
| bool operator==(const usb_endpoint_descriptor_t& lhs, const usb_endpoint_descriptor_t& rhs) { |
| // Comparison of these struct is not useful. Return true always. |
| return true; |
| } |
| |
| bool operator==(const usb_request_t& lhs, const usb_request_t& rhs) { |
| // Only comparing endpoint address. Use ExpectCallWithMatcher for more specific |
| // comparisons. |
| return lhs.header.ep_address == rhs.header.ep_address; |
| } |
| |
| bool operator==(const usb_function_interface_protocol_t& lhs, |
| const usb_function_interface_protocol_t& rhs) { |
| // Comparison of these struct is not useful. Return true always. |
| return true; |
| } |
| |
| // A fake endpoint that allows for more complex behaviour in responding to completion requests |
| // by requiring that there be outstanding requests when you attempt to fulfill them. |
| class FakeEndpoint : public fake_usb_endpoint::FakeEndpoint { |
| public: |
| void Connect(async_dispatcher_t* dispatcher, |
| fidl::ServerEnd<fuchsia_hardware_usb_endpoint::Endpoint> server) override { |
| binding_ref_.emplace(fidl::BindServer(dispatcher, std::move(server), this)); |
| } |
| |
| // QueueRequests: adds requests to a queue, which will be replied to when RequestComplete() is |
| // called. |
| void QueueRequests(QueueRequestsRequest& request, |
| QueueRequestsCompleter::Sync& completer) override { |
| FDF_LOG(DEBUG, "QueueRequests"); |
| fbl::AutoLock _(&lock_); |
| // Add request to queue. |
| requests_.insert(requests_.end(), std::make_move_iterator(request.req().begin()), |
| std::make_move_iterator(request.req().end())); |
| } |
| |
| void CancelAll(CancelAllCompleter::Sync& completer) override { |
| fbl::AutoLock _(&lock_); |
| for (auto& request : requests_) { |
| SendRequestComplete(fuchsia_hardware_usb_request::Request(std::move(request)), |
| ZX_ERR_IO_NOT_PRESENT, 0); |
| } |
| requests_.erase(requests_.begin(), requests_.end()); |
| fake_usb_endpoint::FakeEndpoint::CancelAll(completer); |
| } |
| |
| // Returns the next waiting request. The caller is responsible for ensuring that |
| // a request is waiting in the queue. |
| fuchsia_hardware_usb_request::Request GetNextRequest() { |
| fbl::AutoLock _(&lock_); |
| EXPECT_GT(requests_.size(), 0); |
| auto next_request = fuchsia_hardware_usb_request::Request(std::move(requests_.front())); |
| requests_.erase(requests_.begin()); |
| return next_request; |
| } |
| |
| void SendRequestComplete(fuchsia_hardware_usb_request::Request request, zx_status_t status, |
| size_t actual) { |
| auto completion = std::move(fuchsia_hardware_usb_endpoint::Completion() |
| .request(std::move(request)) |
| .status(status) |
| .transfer_size(actual)); |
| |
| ASSERT_TRUE(binding_ref_); |
| std::vector<fuchsia_hardware_usb_endpoint::Completion> completions; |
| completions.emplace_back(std::move(completion)); |
| EXPECT_TRUE(fidl::SendEvent(*binding_ref_)->OnCompletion(std::move(completions)).is_ok()); |
| } |
| |
| // RegisterVmos: stores the vmo mapping |
| void RegisterVmos(RegisterVmosRequest& request, RegisterVmosCompleter::Sync& completer) override { |
| fbl::AutoLock lock(&lock_); |
| std::vector<fuchsia_hardware_usb_endpoint::VmoHandle> ret; |
| for (const auto& vmo_id : request.vmo_ids()) { |
| zx::vmo vmo; |
| auto status = zx::vmo::create(*vmo_id.size(), 0, &vmo); |
| if (status != ZX_OK) { |
| continue; |
| } |
| zx::vmo dup_vmo; |
| EXPECT_OK(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &dup_vmo)); |
| vmos_.emplace(*vmo_id.id(), std::move(dup_vmo)); |
| ret.emplace_back(std::move( |
| fuchsia_hardware_usb_endpoint::VmoHandle().id(*vmo_id.id()).vmo(std::move(vmo)))); |
| } |
| completer.Reply(std::move(ret)); |
| } |
| // UnregisterVmos: stores the vmo mapping |
| void UnregisterVmos(UnregisterVmosRequest& request, |
| UnregisterVmosCompleter::Sync& completer) override { |
| fbl::AutoLock lock(&lock_); |
| for (const auto& vmo_id : request.vmo_ids()) { |
| vmos_.erase(vmo_id); |
| } |
| completer.Reply({{}, {}}); |
| } |
| |
| void WithVmo(uint64_t vmo_id, std::function<void(zx::vmo&)> cb) { |
| fbl::AutoLock lock(&lock_); |
| auto vmo = vmos_.find(vmo_id); |
| EXPECT_NE(vmo, vmos_.end()); |
| cb(vmo->second); |
| } |
| |
| size_t pending_request_count() { |
| fbl::AutoLock _(&lock_); |
| return requests_.size(); |
| } |
| |
| private: |
| std::optional<fidl::ServerBindingRef<fuchsia_hardware_usb_endpoint::Endpoint>> binding_ref_; |
| |
| fbl::Mutex lock_; |
| std::vector<fuchsia_hardware_usb_request::Request> requests_ __TA_GUARDED(lock_); |
| std::unordered_map<uint64_t, zx::vmo> vmos_ __TA_GUARDED(lock_); |
| }; |
| |
| class FakeFunction : public ddk::MockUsbFunction { |
| public: |
| zx_status_t UsbFunctionAllocEp(uint8_t direction, uint8_t* out_address) override { |
| if (direction == USB_DIR_OUT) { |
| *out_address = kBulkOutEndpoint; |
| } else if (direction == USB_DIR_IN) { |
| // This unfortunately relies on the order of endpoint allocation in the driver. |
| *out_address = kBulkInEndpoint; |
| } else { |
| ddk::MockUsbFunction::UsbFunctionAllocEp(direction, out_address); |
| return ZX_ERR_BAD_STATE; |
| } |
| return ddk::MockUsbFunction::UsbFunctionAllocEp(direction, out_address); |
| } |
| |
| zx_status_t UsbFunctionConfigEp(const usb_endpoint_descriptor_t* ep_desc, |
| const usb_ss_ep_comp_descriptor_t* ss_comp_desc) override { |
| // Overriding method to handle valid cases where nullptr is passed. The generated mock tries to |
| // dereference it without checking. |
| usb_endpoint_descriptor_t ep{}; |
| usb_ss_ep_comp_descriptor_t ss{}; |
| const usb_endpoint_descriptor_t* arg1 = ep_desc ? ep_desc : &ep; |
| const usb_ss_ep_comp_descriptor_t* arg2 = ss_comp_desc ? ss_comp_desc : &ss; |
| return ddk::MockUsbFunction::UsbFunctionConfigEp(arg1, arg2); |
| } |
| |
| size_t UsbFunctionGetRequestSize() override { |
| ddk::MockUsbFunction::UsbFunctionGetRequestSize(); |
| return usb::BorrowedRequest<>::RequestSize(sizeof(usb_request_t)); |
| } |
| |
| compat::DeviceServer::BanjoConfig GetBanjoConfig() { |
| compat::DeviceServer::BanjoConfig config{.default_proto_id = ZX_PROTOCOL_USB_FUNCTION}; |
| config.callbacks[ZX_PROTOCOL_USB_FUNCTION] = banjo_server_.callback(); |
| return config; |
| } |
| |
| private: |
| compat::BanjoServer banjo_server_{ZX_PROTOCOL_USB_FUNCTION, this, GetProto()->ops}; |
| }; |
| |
| class TestCallback : public fidl::WireServer<fuchsia_hardware_overnet::Callback> { |
| public: |
| TestCallback(size_t expected_calls, std::function<void(zx::socket)> callback) |
| : expected_calls_(expected_calls), callback_(std::move(callback)) {} |
| ~TestCallback() { |
| FDF_LOG(DEBUG, "Destroying TestCallback %zu==%zu", expected_calls_, actual_calls_); |
| ASSERT_EQ(expected_calls_, actual_calls_); |
| } |
| void NewLink(::fuchsia_hardware_overnet::wire::CallbackNewLinkRequest* request, |
| NewLinkCompleter::Sync& completer) override { |
| actual_calls_++; |
| FDF_LOG(DEBUG, "calling callback %zu", actual_calls_); |
| callback_(std::move(request->socket)); |
| completer.Reply(); |
| } |
| |
| private: |
| size_t expected_calls_; |
| size_t actual_calls_ = 0; |
| std::function<void(zx::socket)> callback_; |
| |
| DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(TestCallback); |
| }; |
| |
| using FakeUsb = fake_usb_endpoint::FakeUsbFidlProvider<fuchsia_hardware_usb_function::UsbFunction, |
| FakeEndpoint>; |
| |
| class OvernetUsbEnvironment : public fdf_testing::Environment { |
| public: |
| zx::result<> Serve(fdf::OutgoingDirectory& to_driver_vfs) override { |
| dispatcher_ = fdf::Dispatcher::GetCurrent()->async_dispatcher(); |
| device_server_.Initialize("default", std::nullopt, function_.GetBanjoConfig()); |
| EXPECT_EQ(ZX_OK, device_server_.Serve(dispatcher_, &to_driver_vfs)); |
| |
| fake_usb_ = std::make_unique<FakeUsb>(dispatcher_); |
| fuchsia_hardware_usb_function::UsbFunctionService::InstanceHandler handler({ |
| .device = usb_function_bindings_.CreateHandler( |
| fake_usb_.get(), fdf::Dispatcher::GetCurrent()->async_dispatcher(), |
| fidl::kIgnoreBindingClosure), |
| }); |
| auto result = to_driver_vfs.AddService<fuchsia_hardware_usb_function::UsbFunctionService>( |
| std::move(handler)); |
| EXPECT_EQ(ZX_OK, result.status_value()); |
| fake_usb_->ExpectConnectToEndpoint(kBulkOutEndpoint); |
| fake_usb_->ExpectConnectToEndpoint(kBulkInEndpoint); |
| |
| auto endpoints = fidl::Endpoints<fuchsia_hardware_overnet::Usb>::Create(); |
| |
| return zx::ok(); |
| } |
| |
| async_dispatcher_t* dispatcher_; |
| compat::DeviceServer device_server_; |
| FakeFunction function_; |
| std::unique_ptr<FakeUsb> fake_usb_; |
| fidl::ServerBindingGroup<fuchsia_hardware_usb_function::UsbFunction> usb_function_bindings_; |
| }; |
| |
| class OvernetUsbTestConfig final { |
| public: |
| using DriverType = OvernetUsb; |
| using EnvironmentType = OvernetUsbEnvironment; |
| }; |
| |
| class OvernetUsbTest : public zxtest::Test { |
| public: |
| fuchsia_hardware_usb_request::Request WaitForRequestOn(uint8_t endpoint) { |
| auto& runtime = driver_test().runtime(); |
| size_t request_count; |
| fuchsia_hardware_usb_request::Request request; |
| FDF_LOG(DEBUG, "Waiting for request on endpoint %d", endpoint); |
| do { |
| runtime.RunUntilIdle(); |
| driver_test().RunInEnvironmentTypeContext( |
| [&request, &request_count, endpoint](OvernetUsbEnvironment& env) mutable { |
| auto& ep = env.fake_usb_->fake_endpoint(endpoint); |
| request_count = ep.pending_request_count(); |
| if (request_count > 0) { |
| request = ep.GetNextRequest(); |
| } |
| }); |
| } while (request_count == 0); |
| |
| return request; |
| } |
| // seems to be getting stuck waiting for the second buffer on the out endpoint, which really |
| // shouldn't happen? |
| bool SendTx(const uint8_t* tx, size_t size) { |
| FDF_LOG(DEBUG, "SendTx(%zu)", size); |
| |
| auto request = WaitForRequestOn(kBulkOutEndpoint); |
| FDF_LOG(DEBUG, "got request on out endpoint"); |
| driver_test().RunInEnvironmentTypeContext( |
| [request = std::move(request), tx, size](OvernetUsbEnvironment& env) mutable { |
| auto& out_ep = env.fake_usb_->fake_endpoint(kBulkOutEndpoint); |
| auto& data = request.data(); |
| ASSERT_EQ(data->size(), 1); |
| auto& buffer = data->front().buffer(); |
| ASSERT_EQ(buffer->Which(), fuchsia_hardware_usb_request::Buffer::Tag::kVmoId); |
| auto vmo_id = buffer->vmo_id().value(); |
| out_ep.WithVmo(vmo_id, [tx, size](zx::vmo& vmo) { ASSERT_OK(vmo.write(tx, 0, size)); }); |
| data->at(0).size(size); |
| out_ep.SendRequestComplete(std::move(request), ZX_OK, size); |
| }); |
| return true; |
| } |
| |
| std::optional<std::vector<uint8_t>> GetRx() { |
| std::vector<uint8_t> ret; |
| auto request = WaitForRequestOn(kBulkInEndpoint); |
| driver_test().RunInEnvironmentTypeContext( |
| [&ret, request = std::move(request)](OvernetUsbEnvironment& env) mutable { |
| auto& in_ep = env.fake_usb_->fake_endpoint(kBulkInEndpoint); |
| FDF_LOG(DEBUG, "Got request on in endpoint"); |
| auto& data = request.data(); |
| ASSERT_EQ(data->size(), 1); |
| auto& buffer = data->front().buffer(); |
| ASSERT_EQ(buffer->Which(), fuchsia_hardware_usb_request::Buffer::Tag::kVmoId); |
| uint64_t vmo_id = buffer->vmo_id().value(); |
| ret.resize(data->front().size().value()); |
| size_t offset = data->front().offset().value_or(0); |
| FDF_LOG(DEBUG, "reading %zu bytes from incoming vmo at offset %zu", ret.size(), offset); |
| in_ep.WithVmo(vmo_id, [&ret, offset](zx::vmo& vmo) { |
| ASSERT_OK(vmo.read(&*ret.begin(), offset, ret.size())); |
| }); |
| in_ep.SendRequestComplete(std::move(request), ZX_OK, 0); |
| }); |
| return std::optional(ret); |
| } |
| |
| bool GetRxConcatExpect(const uint8_t* data, size_t len) { |
| FDF_LOG(DEBUG, "GetRxConcatExpect(%zu)", len); |
| while (len != 0) { |
| auto got = GetRx(); |
| if (!got.has_value()) { |
| FDF_LOG(ERROR, "No value returned from GetRx"); |
| return false; |
| } |
| if (got->size() > len) { |
| FDF_LOG(ERROR, "returned size %zu was greater than expected %zu", got->size(), len); |
| return false; |
| } |
| if (!std::equal(got->begin(), got->end(), data)) { |
| FDF_LOG(ERROR, "returned data did not match expectation"); |
| return false; |
| } |
| len -= got->size(); |
| data += got->size(); |
| } |
| return true; |
| } |
| |
| bool SocketReadExpect(zx::socket* socket, const uint8_t* data, size_t len) { |
| FDF_LOG(DEBUG, "SocketReadExpect(%zu)", len); |
| std::vector<uint8_t> buf(len, 0); |
| |
| while (len > 0) { |
| FDF_LOG(DEBUG, "reading loop iteration, need %zu bytes still", len); |
| zx_signals_t pending; |
| size_t actual; |
| if (socket->wait_one(ZX_SOCKET_READABLE, zx::time::infinite(), &pending) != ZX_OK) { |
| return false; |
| } |
| if ((pending & ZX_SOCKET_READABLE) == 0) { |
| return false; |
| } |
| if (socket->read(0, buf.data(), len, &actual) != ZX_OK) { |
| return false; |
| } |
| if (!std::equal(buf.begin(), buf.begin() + static_cast<ssize_t>(actual), data)) { |
| return false; |
| } |
| len -= actual; |
| data += actual; |
| } |
| return true; |
| } |
| |
| bool SocketWriteAll(zx::socket* socket, const uint8_t* data, size_t len) { |
| FDF_LOG(DEBUG, "SocketWriteAll(%zu)", len); |
| while (len > 0) { |
| zx_signals_t pending; |
| size_t actual; |
| zx_status_t res = socket->wait_one(ZX_SOCKET_WRITABLE, zx::time::infinite(), &pending); |
| if (res != ZX_OK) { |
| FDF_LOG(ERROR, "error while waiting on socket: %d", res); |
| return false; |
| } |
| if ((pending & ZX_SOCKET_WRITABLE) == 0) { |
| FDF_LOG(ERROR, "socket not writeable (%x)", pending); |
| return false; |
| } |
| res = socket->write(0, data, len, &actual); |
| if (res != ZX_OK) { |
| FDF_LOG(ERROR, "error while writing to socket: %d", res); |
| return false; |
| } |
| FDF_LOG(DEBUG, "wrote %zu bytes to socket", actual); |
| len -= actual; |
| data += actual; |
| } |
| return true; |
| } |
| |
| void SetUp() override { |
| driver_test().RunInEnvironmentTypeContext([](OvernetUsbEnvironment& env) { |
| env.function_.ExpectAllocStringDesc(ZX_OK, "Overnet USB interface", 1); |
| env.function_.ExpectAllocInterface(ZX_OK, 1); |
| env.function_.ExpectAllocEp(ZX_OK, USB_DIR_OUT, kBulkOutEndpoint); |
| env.function_.ExpectAllocEp(ZX_OK, USB_DIR_IN, kBulkInEndpoint); |
| env.function_.ExpectSetInterface(ZX_OK, {}); |
| env.function_.ExpectGetRequestSize(0); |
| }); |
| ASSERT_OK(driver_test().StartDriver().status_value()); |
| auto device = driver_test().Connect<fuchsia_hardware_overnet::UsbService::Device>(); |
| EXPECT_OK(device.status_value()); |
| client_.Bind(std::move(device.value())); |
| } |
| |
| void TearDown() override { |
| FDF_LOG(DEBUG, "TearDown start"); |
| |
| driver_test().RunInEnvironmentTypeContext([](OvernetUsbEnvironment& env) { |
| env.function_.ExpectCancelAll(ZX_OK, kBulkInEndpoint); |
| env.function_.ExpectCancelAll(ZX_OK, kBulkOutEndpoint); |
| env.function_.ExpectSetInterface(ZX_OK, {}); |
| }); |
| |
| zx::result<> result = driver_test().StopDriver(); |
| ASSERT_EQ(ZX_OK, result.status_value()); |
| FDF_LOG(DEBUG, "TearDown finished"); |
| } |
| |
| void ConfigureDevice(usb_speed_t speed) { |
| driver_test().RunInEnvironmentTypeContext([](OvernetUsbEnvironment& env) { |
| env.function_.ExpectConfigEp(ZX_OK, {}, {}); |
| env.function_.ExpectConfigEp(ZX_OK, {}, {}); |
| }); |
| |
| driver_test().RunInDriverContext([speed](OvernetUsb& device) { |
| zx_status_t status = device.UsbFunctionInterfaceSetConfigured(/*configured=*/true, speed); |
| ASSERT_OK(status); |
| }); |
| } |
| |
| void UnconfigureDevice() { |
| FDF_LOG(DEBUG, "Unconfiguring device"); |
| driver_test().RunInEnvironmentTypeContext([](OvernetUsbEnvironment& env) { |
| env.function_.ExpectCancelAll(ZX_OK, kBulkInEndpoint); |
| env.function_.ExpectCancelAll(ZX_OK, kBulkOutEndpoint); |
| env.function_.ExpectDisableEp(ZX_OK, kBulkInEndpoint); |
| env.function_.ExpectDisableEp(ZX_OK, kBulkOutEndpoint); |
| env.function_.ExpectSetInterface(ZX_OK, {}); |
| }); |
| |
| driver_test().RunInDriverContext([](OvernetUsb& device) { |
| zx_status_t status = |
| device.UsbFunctionInterfaceSetConfigured(/*configured=*/false, USB_SPEED_FULL); |
| ASSERT_OK(status); |
| }); |
| } |
| |
| void ResetWithSetInterface() { |
| FDF_LOG(DEBUG, "Resetting device by calling SetInterface on it"); |
| |
| driver_test().RunInEnvironmentTypeContext([](OvernetUsbEnvironment& env) { |
| env.function_.ExpectConfigEp(ZX_OK, {}, {}); |
| env.function_.ExpectConfigEp(ZX_OK, {}, {}); |
| }); |
| |
| driver_test().RunInDriverContext( |
| [](auto& driver) { ASSERT_OK(driver.UsbFunctionInterfaceSetInterface(1, 0)); }); |
| } |
| |
| std::unique_ptr<TestCallback> SetupCallback(size_t expected_calls, |
| std::function<void(zx::socket)> callback) { |
| auto callback_obj = SetTestCallback(expected_calls, std::move(callback)); |
| EXPECT_TRUE(callback_obj); |
| driver_test().runtime().RunUntilIdle(); |
| return callback_obj; |
| } |
| |
| std::unique_ptr<TestCallback> SetTestCallback(size_t expected_calls, |
| std::function<void(zx::socket)> callback) const { |
| auto dispatcher = fdf::Dispatcher::GetCurrent()->async_dispatcher(); |
| auto ret = std::make_unique<TestCallback>(expected_calls, callback); |
| auto endpoints = fidl::CreateEndpoints<fuchsia_hardware_overnet::Callback>(); |
| if (!endpoints.is_ok()) { |
| return nullptr; |
| } |
| fidl::BindServer(dispatcher, std::move(endpoints->server), ret.get()); |
| if (!client_->SetCallback(std::move(endpoints->client)).ok()) { |
| return nullptr; |
| } |
| return ret; |
| } |
| |
| fdf_testing::BackgroundDriverTest<OvernetUsbTestConfig>& driver_test() { return driver_test_; } |
| fdf_testing::BackgroundDriverTest<OvernetUsbTestConfig> driver_test_; |
| fidl::WireSyncClient<fuchsia_hardware_overnet::Usb> client_; |
| }; |
| |
| TEST_F(OvernetUsbTest, Startup) { FDF_LOG(DEBUG, "startup"); } |
| |
| TEST_F(OvernetUsbTest, ConfigureAndUnconfigure) { |
| ConfigureDevice(USB_SPEED_FULL); |
| UnconfigureDevice(); |
| } |
| |
| TEST_F(OvernetUsbTest, SocketGet) { |
| ConfigureDevice(USB_SPEED_FULL); |
| std::atomic_bool callback_called = false; |
| auto callback = SetupCallback(1, [&callback_called](zx::socket sock) { |
| FDF_LOG(DEBUG, "got socket"); |
| callback_called = true; |
| }); |
| while (!callback_called) { |
| driver_test().runtime().RunUntilIdle(); |
| } |
| FDF_LOG(DEBUG, "Callback setup"); |
| UnconfigureDevice(); |
| } |
| |
| TEST_F(OvernetUsbTest, DataFromTarget) { |
| ConfigureDevice(USB_SPEED_FULL); |
| std::vector<zx::socket> sockets; |
| auto callback = |
| SetupCallback(1, [&sockets](zx::socket socket) { sockets.emplace_back(std::move(socket)); }); |
| while (sockets.size() < 1) { |
| driver_test().runtime().RunUntilIdle(); |
| } |
| |
| std::string_view test_data = |
| "A basket of biscuits, a basket of mixed biscuits and a biscuit mixer."; |
| ASSERT_TRUE(SocketWriteAll(&sockets[0], reinterpret_cast<const uint8_t*>(test_data.data()), |
| test_data.size())); |
| |
| ASSERT_TRUE( |
| GetRxConcatExpect(reinterpret_cast<const uint8_t*>(test_data.data()), test_data.size())); |
| |
| std::string_view test_data_b = "Aluminum, linoleum, magnesium, petroleum."; |
| ASSERT_TRUE(SocketWriteAll(&sockets[0], reinterpret_cast<const uint8_t*>(test_data_b.data()), |
| test_data_b.size())); |
| ASSERT_TRUE( |
| GetRxConcatExpect(reinterpret_cast<const uint8_t*>(test_data_b.data()), test_data_b.size())); |
| UnconfigureDevice(); |
| } |
| |
| TEST_F(OvernetUsbTest, DataFromHost) { |
| ConfigureDevice(USB_SPEED_FULL); |
| std::vector<zx::socket> sockets; |
| auto callback = |
| SetupCallback(1, [&sockets](zx::socket socket) { sockets.emplace_back(std::move(socket)); }); |
| while (sockets.size() < 1) { |
| driver_test().runtime().RunUntilIdle(); |
| } |
| |
| std::string_view test_data = |
| "A basket of biscuits, a basket of mixed biscuits and a biscuit mixer."; |
| ASSERT_TRUE(SendTx(reinterpret_cast<const uint8_t*>(test_data.data()), test_data.size())); |
| ASSERT_TRUE(SocketReadExpect(&sockets[0], reinterpret_cast<const uint8_t*>(test_data.data()), |
| test_data.size())); |
| |
| std::string_view test_data_b = "Aluminum, linoleum, magnesium, petroleum."; |
| ASSERT_TRUE(SendTx(reinterpret_cast<const uint8_t*>(test_data_b.data()), test_data_b.size())); |
| ASSERT_TRUE(SocketReadExpect(&sockets[0], reinterpret_cast<const uint8_t*>(test_data_b.data()), |
| test_data_b.size())); |
| UnconfigureDevice(); |
| } |
| |
| TEST_F(OvernetUsbTest, Reset) { |
| ConfigureDevice(USB_SPEED_FULL); |
| std::vector<zx::socket> sockets; |
| auto callback = |
| SetupCallback(2, [&sockets](zx::socket socket) { sockets.emplace_back(std::move(socket)); }); |
| while (sockets.size() < 1) { |
| driver_test().runtime().RunUntilIdle(); |
| } |
| |
| std::string_view test_data = |
| "A basket of biscuits, a basket of mixed biscuits and a biscuit mixer."; |
| ASSERT_TRUE(SendTx(reinterpret_cast<const uint8_t*>(test_data.data()), test_data.size())); |
| ASSERT_TRUE(SocketReadExpect(&sockets[0], reinterpret_cast<const uint8_t*>(test_data.data()), |
| test_data.size())); |
| |
| std::string_view test_data_b = "Aluminum, linoleum, magnesium, petroleum."; |
| ASSERT_TRUE(SocketWriteAll(&sockets[0], reinterpret_cast<const uint8_t*>(test_data_b.data()), |
| test_data_b.size())); |
| ASSERT_TRUE( |
| GetRxConcatExpect(reinterpret_cast<const uint8_t*>(test_data_b.data()), test_data_b.size())); |
| ResetWithSetInterface(); |
| // wait for the socket reset to work its way through and produce a new socket |
| while (sockets.size() < 2) { |
| driver_test().runtime().RunUntilIdle(); |
| } |
| |
| zx_signals_t pending; |
| sockets[0].wait_one(ZX_SOCKET_PEER_CLOSED, zx::time::infinite(), &pending); |
| ASSERT_NE(pending & ZX_SOCKET_PEER_CLOSED, 0u); |
| |
| std::string_view test_data_c = "Around the rugged rocks the ragged rascals ran."; |
| ASSERT_TRUE(SendTx(reinterpret_cast<const uint8_t*>(test_data_c.data()), test_data_c.size())); |
| ASSERT_TRUE(SocketReadExpect(&sockets[1], reinterpret_cast<const uint8_t*>(test_data_c.data()), |
| test_data_c.size())); |
| |
| std::string_view test_data_d = "A proper copper coffee pot."; |
| ASSERT_TRUE(SocketWriteAll(&sockets[1], reinterpret_cast<const uint8_t*>(test_data_d.data()), |
| test_data_d.size())); |
| ASSERT_TRUE( |
| GetRxConcatExpect(reinterpret_cast<const uint8_t*>(test_data_d.data()), test_data_d.size())); |
| UnconfigureDevice(); |
| } |
| |
| TEST_F(OvernetUsbTest, ResetMoreData) { |
| ConfigureDevice(USB_SPEED_FULL); |
| std::vector<zx::socket> sockets; |
| auto callback = |
| SetupCallback(2, [&sockets](zx::socket socket) { sockets.emplace_back(std::move(socket)); }); |
| while (sockets.size() < 1) { |
| driver_test().runtime().RunUntilIdle(); |
| } |
| |
| std::string_view test_data_a = |
| "A basket of biscuits, a basket of mixed biscuits and a biscuit mixer."; |
| std::string_view test_data_b = "Aluminum, linoleum, magnesium, petroleum."; |
| std::string_view test_data_c = "Around the rugged rocks the ragged rascals ran."; |
| std::string_view test_data_d = "A proper copper coffee pot."; |
| |
| for (int i = 0; i < 50; i++) { |
| ASSERT_TRUE(SendTx(reinterpret_cast<const uint8_t*>(test_data_a.data()), test_data_a.size())); |
| ASSERT_TRUE(SocketReadExpect(&sockets[0], reinterpret_cast<const uint8_t*>(test_data_a.data()), |
| test_data_a.size())); |
| |
| ASSERT_TRUE(SocketWriteAll(&sockets[0], reinterpret_cast<const uint8_t*>(test_data_b.data()), |
| test_data_b.size())); |
| ASSERT_TRUE(GetRxConcatExpect(reinterpret_cast<const uint8_t*>(test_data_b.data()), |
| test_data_b.size())); |
| |
| ASSERT_TRUE(SendTx(reinterpret_cast<const uint8_t*>(test_data_c.data()), test_data_c.size())); |
| ASSERT_TRUE(SocketReadExpect(&sockets[0], reinterpret_cast<const uint8_t*>(test_data_c.data()), |
| test_data_c.size())); |
| |
| ASSERT_TRUE(SocketWriteAll(&sockets[0], reinterpret_cast<const uint8_t*>(test_data_d.data()), |
| test_data_d.size())); |
| ASSERT_TRUE(GetRxConcatExpect(reinterpret_cast<const uint8_t*>(test_data_d.data()), |
| test_data_d.size())); |
| } |
| ResetWithSetInterface(); |
| // wait for the socket reset to work its way through and produce a new socket |
| while (sockets.size() < 2) { |
| driver_test().runtime().RunUntilIdle(); |
| } |
| |
| zx_signals_t pending; |
| sockets[0].wait_one(ZX_SOCKET_PEER_CLOSED, zx::time::infinite(), &pending); |
| ASSERT_NE(pending & ZX_SOCKET_PEER_CLOSED, 0u); |
| |
| for (int i = 0; i < 50; i++) { |
| ASSERT_TRUE(SendTx(reinterpret_cast<const uint8_t*>(test_data_a.data()), test_data_a.size())); |
| ASSERT_TRUE(SocketReadExpect(&sockets[1], reinterpret_cast<const uint8_t*>(test_data_a.data()), |
| test_data_a.size())); |
| |
| ASSERT_TRUE(SocketWriteAll(&sockets[1], reinterpret_cast<const uint8_t*>(test_data_b.data()), |
| test_data_b.size())); |
| ASSERT_TRUE(GetRxConcatExpect(reinterpret_cast<const uint8_t*>(test_data_b.data()), |
| test_data_b.size())); |
| |
| ASSERT_TRUE(SendTx(reinterpret_cast<const uint8_t*>(test_data_c.data()), test_data_c.size())); |
| ASSERT_TRUE(SocketReadExpect(&sockets[1], reinterpret_cast<const uint8_t*>(test_data_c.data()), |
| test_data_c.size())); |
| |
| ASSERT_TRUE(SocketWriteAll(&sockets[1], reinterpret_cast<const uint8_t*>(test_data_d.data()), |
| test_data_d.size())); |
| ASSERT_TRUE(GetRxConcatExpect(reinterpret_cast<const uint8_t*>(test_data_d.data()), |
| test_data_d.size())); |
| } |
| UnconfigureDevice(); |
| } |
| // NOLINTEND(readability-container-data-pointer) |
| // NOLINTEND(readability-convert-member-functions-to-static) |
| // NOLINTEND(misc-use-anonymous-namespace) |