blob: 934839092c60ef989bf089f214d0fdec825afbdd [file] [log] [blame]
// 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 "../radar-reader-proxy.h"
#include <fidl/fuchsia.hardware.radar/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <functional>
#include <optional>
#include <vector>
#include <zxtest/zxtest.h>
#include "radar-proxy-test.h"
namespace radar {
namespace {
using RadarReaderProxyTest = RadarProxyTest<RadarReaderProxy>;
TEST_F(RadarReaderProxyTest, Connect) {
AddRadarDevice();
zx::result endpoints = fidl::CreateEndpoints<fuchsia_hardware_radar::RadarBurstReader>();
ASSERT_TRUE(endpoints.is_ok());
auto result = dut_client_->Connect(std::move(endpoints->server));
EXPECT_TRUE(result.is_ok());
fidl::SyncClient radar_client(std::move(endpoints->client));
EXPECT_EQ(radar_client->GetBurstProperties().value_or(kInvalidProperties()).size(), 12345);
}
TEST_F(RadarReaderProxyTest, Reconnect) {
AddRadarDevice();
async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
fidl::Client service_client(dut_client_.TakeClientEnd(), loop.dispatcher());
zx::result endpoints = fidl::CreateEndpoints<fuchsia_hardware_radar::RadarBurstReader>();
ASSERT_TRUE(endpoints.is_ok());
service_client->Connect(std::move(endpoints->server)).Then([&](auto& result) {
EXPECT_TRUE(result.is_ok());
});
fidl::SyncClient radar_client(std::move(endpoints->client));
EXPECT_EQ(radar_client->GetBurstProperties().value_or(kInvalidProperties()).size(), 12345);
// Close the Provider connection between the proxy and the radar driver. Everything should still
// work as long as the Reader connection remains open.
UnbindRadarDevice();
// Verify that Connect() still works.
endpoints = fidl::CreateEndpoints<fuchsia_hardware_radar::RadarBurstReader>();
ASSERT_TRUE(endpoints.is_ok());
service_client->Connect(std::move(endpoints->server)).Then([&](auto& result) {
EXPECT_TRUE(result.is_ok());
});
fidl::SyncClient new_radar_client(std::move(endpoints->client));
EXPECT_EQ(new_radar_client->GetBurstProperties().value_or(kInvalidProperties()).size(), 12345);
// Verify that the original connection still works.
EXPECT_EQ(radar_client->GetBurstProperties().value_or(kInvalidProperties()).size(), 12345);
UnbindReaderAndWaitForConnectionAttempt();
// Client connections were closed, new requests should now fail.
EXPECT_FALSE(radar_client->GetBurstProperties().is_ok());
endpoints = fidl::CreateEndpoints<fuchsia_hardware_radar::RadarBurstReader>();
ASSERT_TRUE(endpoints.is_ok());
new_radar_client = fidl::SyncClient(std::move(endpoints->client));
// Send a connect request while there is no radar driver. It should be completed (and the
// connection usable) after a new radar driver is added.
service_client->Connect(std::move(endpoints->server)).Then([&](auto& result) {
EXPECT_TRUE(result.is_ok());
});
// Signal that a new device was added to /dev/class/radar.
AddRadarDevice();
// Verify that the previous connect request suceeded.
EXPECT_EQ(new_radar_client->GetBurstProperties().value_or(kInvalidProperties()).size(), 12345);
// Subsequent connect requests should be immediately fulfilled.
endpoints = fidl::CreateEndpoints<fuchsia_hardware_radar::RadarBurstReader>();
ASSERT_TRUE(endpoints.is_ok());
service_client->Connect(std::move(endpoints->server)).Then([&](auto& result) {
EXPECT_TRUE(result.is_ok());
loop.Quit();
});
new_radar_client = fidl::SyncClient(std::move(endpoints->client));
EXPECT_EQ(new_radar_client->GetBurstProperties().value_or(kInvalidProperties()).size(), 12345);
// Ensure that all of our previous async requests got replies.
loop.Run();
}
TEST_F(RadarReaderProxyTest, DelayedConnect) {
async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
fidl::Client service_client(dut_client_.TakeClientEnd(), loop.dispatcher());
zx::result endpoints = fidl::CreateEndpoints<fuchsia_hardware_radar::RadarBurstReader>();
ASSERT_TRUE(endpoints.is_ok());
// Attempt to connect before a radar device is available. Our request should be put in a queue and
// completed when a device is added.
service_client->Connect(std::move(endpoints->server)).Then([&](auto& result) {
EXPECT_TRUE(result.is_ok());
});
fidl::Client radar_client(std::move(endpoints->client), loop.dispatcher());
uint32_t burst_size = 0;
radar_client->GetBurstProperties().Then([&](auto& result) {
burst_size = result.is_ok() ? result->size() : 0;
loop.Quit();
});
loop.RunUntilIdle();
// Signal to the proxy that a device was added. This should cause it to process any connect
// requests that came in before this point.
AddRadarDevice();
// The loop will be stopped after the burst size call completes.
loop.Run();
EXPECT_EQ(burst_size, 12345);
}
TEST_F(RadarReaderProxyTest, RegisterVmosError) {
AddRadarDevice();
struct : fidl::AsyncEventHandler<fuchsia_hardware_radar::RadarBurstReader> {
async::Loop loop{&kAsyncLoopConfigNeverAttachToThread};
fidl::Client<fuchsia_hardware_radar::RadarBurstReader> client;
void on_fidl_error(fidl::UnbindInfo info) override { loop.Quit(); }
void Bind(fidl::ClientEnd<fuchsia_hardware_radar::RadarBurstReader> client_end) {
client.Bind(std::move(client_end), loop.dispatcher(), this);
}
} radar_client;
zx::result endpoints = fidl::CreateEndpoints<fuchsia_hardware_radar::RadarBurstReader>();
ASSERT_TRUE(endpoints.is_ok());
{
auto result = dut_client_->Connect(std::move(endpoints->server));
EXPECT_TRUE(result.is_ok());
}
radar_client.Bind(std::move(endpoints->client));
{
std::vector<zx::vmo> vmos;
EXPECT_OK(zx::vmo::create(12345, 0, &vmos.emplace_back()));
radar_client.client->RegisterVmos({std::vector<uint32_t>{1}, std::move(vmos)})
.Then([&](auto& result) { EXPECT_TRUE(result.is_ok()); });
}
{
std::vector<zx::vmo> vmos;
EXPECT_OK(zx::vmo::create(12345, 0, &vmos.emplace_back()));
// The call to radar-proxy should succeed, however the call from radar-proxy to the underlying
// driver should fail. When that happens radar-proxy should send an epitaph to the client.
radar_client.client->RegisterVmos({std::vector<uint32_t>{2}, std::move(vmos)})
.Then([&](auto& result) { EXPECT_TRUE(result.is_ok()); });
}
// Run until on_fidl_error receives the epitaph and stops us.
radar_client.loop.Run();
}
TEST_F(RadarReaderProxyTest, DeviceWithInvalidBurstSizeIsRejected) {
AddRadarDevice();
async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
fidl::Client service_client(dut_client_.TakeClientEnd(), loop.dispatcher());
zx::result endpoints = fidl::CreateEndpoints<fuchsia_hardware_radar::RadarBurstReader>();
ASSERT_TRUE(endpoints.is_ok());
fidl::SyncClient radar_client(std::move(endpoints->client));
service_client->Connect(std::move(endpoints->server)).Then([&](auto& result) {
EXPECT_TRUE(result.is_ok());
EXPECT_EQ(radar_client->GetBurstProperties().value_or(kInvalidProperties()).size(), 12345);
UnbindRadarDevice();
UnbindReaderAndWaitForConnectionAttempt();
zx::result endpoints = fidl::CreateEndpoints<fuchsia_hardware_radar::RadarBurstReader>();
ASSERT_TRUE(endpoints.is_ok());
radar_client = fidl::SyncClient(std::move(endpoints->client));
// With no connected device this request should be put in the queue and completed later.
service_client->Connect(std::move(endpoints->server)).Then([&](auto& result) {
EXPECT_TRUE(result.is_ok());
EXPECT_EQ(radar_client->GetBurstProperties().value_or(kInvalidProperties()).size(), 12345);
loop.Quit();
});
// The burst size reported by this device does not match the original value, so it should be
// ignored.
fake_driver_.set_burst_size(12346);
AddRadarDevice();
// This device matches the original, so it should be accepted and used to complete the connect
// request.
fake_driver_.set_burst_size(12345);
AddRadarDevice();
});
loop.Run();
}
// A client wrapper that supports receiving OnBurst() events. If no burst callback is specified then
// VMOs will not be unlocked.
struct BurstReaderClient : fidl::AsyncEventHandler<fuchsia_hardware_radar::RadarBurstReader> {
public:
BurstReaderClient(fidl::ClientEnd<fuchsia_hardware_radar::RadarBurstReader> client_end,
async_dispatcher_t* dispatcher, std::function<void(uint32_t)> callback)
: client(std::move(client_end), dispatcher, this), burst_callback(std::move(callback)) {}
void OnBurst(fidl::Event<fuchsia_hardware_radar::RadarBurstReader::OnBurst>& event) override {
if (event.burst()) {
burst_count++;
if (burst_callback) {
burst_callback(event.burst()->vmo_id());
EXPECT_TRUE(client->UnlockVmo(event.burst()->vmo_id()).is_ok());
}
} else {
error_status = event.error().value();
}
}
fidl::Client<fuchsia_hardware_radar::RadarBurstReader>& operator->() { return client; }
fidl::Client<fuchsia_hardware_radar::RadarBurstReader> client;
uint32_t burst_count = 0;
fuchsia_hardware_radar::StatusCode error_status = fuchsia_hardware_radar::StatusCode::kSuccess;
private:
const std::function<void(uint32_t)> burst_callback;
};
class RadarReaderProxyOnBurstTest : public RadarReaderProxyTest {
public:
RadarReaderProxyOnBurstTest() : loop_(&kAsyncLoopConfigNeverAttachToThread) {}
void SetUp() override {
RadarReaderProxyTest::SetUp();
AddRadarDevice();
const std::function burst_callback([&](uint32_t) { BurstCallback(); });
radar_clients_.reserve(4);
for (uint32_t i = 0; i < 4; i++) {
zx::result endpoints = fidl::CreateEndpoints<fuchsia_hardware_radar::RadarBurstReader>();
ASSERT_TRUE(endpoints.is_ok());
EXPECT_TRUE(dut_client_->Connect(std::move(endpoints->server)).is_ok());
radar_clients_.emplace_back(std::move(endpoints->client), loop_.dispatcher(),
// Make the last client not unlock VMOs to test error cases.
i == 3 ? std::function<void(uint32_t)>{} : burst_callback);
std::vector<zx::vmo> vmos;
EXPECT_OK(zx::vmo::create(12345, 0, &vmos.emplace_back()));
EXPECT_OK(zx::vmo::create(12345, 0, &vmos.emplace_back()));
EXPECT_OK(zx::vmo::create(12345, 0, &vmos.emplace_back()));
EXPECT_OK(zx::vmo::create(12345, 0, &vmos.emplace_back()));
EXPECT_OK(zx::vmo::create(12345, 0, &vmos.emplace_back()));
radar_clients_.back()
->RegisterVmos({std::vector<uint32_t>{1, 2, 3, 4, 5}, std::move(vmos)})
.Then([&](auto& result) { EXPECT_TRUE(result.is_ok()); });
}
}
protected:
std::vector<BurstReaderClient> radar_clients_;
async::Loop loop_;
private:
// Assume that there are always this many connected clients with bursts started. A counter will be
// compared to this value to ensure that the driver doesn't send a new burst until all clients
// have unlocked their VMOs.
static constexpr uint32_t kClientsWithBurstsStarted = 2;
// Keep running until all clients receive this many bursts, then quit the loop.
static constexpr uint32_t kReceiveBurstCount = 10;
void BurstCallback() {
static uint32_t client_lockstep_counter = 0;
static uint32_t burst_count = 0;
// Increment the counter until all clients have received this burst. At that point we can either
// send another burst, or stop the loop.
if (++client_lockstep_counter == kClientsWithBurstsStarted) {
client_lockstep_counter = 0;
if (++burst_count == kReceiveBurstCount) {
burst_count = 0;
loop_.Quit();
} else {
fake_driver_.SendBurst();
}
}
}
};
TEST_F(RadarReaderProxyOnBurstTest, OnlyStartedClientsReceiveBursts) {
// Clients 0 and 1 start bursts. Verify that client 2 does not receive bursts.
EXPECT_TRUE(radar_clients_[0]->StartBursts().is_ok());
EXPECT_TRUE(radar_clients_[1]->StartBursts().is_ok());
// Use the GetBurstProperties() reply as a trigger to start sending bursts. At that point we know
// that radar-proxy has already processed our StartBursts() call, so any new bursts will be
// forwarded to clients.
radar_clients_[0]->GetBurstProperties().Then([&](auto& result) { fake_driver_.SendBurst(); });
loop_.Run();
EXPECT_EQ(radar_clients_[0].burst_count, 10);
EXPECT_EQ(radar_clients_[1].burst_count, 10);
EXPECT_EQ(radar_clients_[2].burst_count, 0);
}
TEST_F(RadarReaderProxyOnBurstTest, DriverErrorsSentToStartedClients) {
// Clients 1 and 2 start bursts. The driver reports a burst error, which gets sent to only to
// these clients.
EXPECT_TRUE(radar_clients_[1]->StartBursts().is_ok());
EXPECT_TRUE(radar_clients_[2]->StartBursts().is_ok());
radar_clients_[0]->GetBurstProperties().Then([&](auto& result) {
fake_driver_.SendError(fuchsia_hardware_radar::StatusCode::kSensorError);
fake_driver_.SendBurst();
});
loop_.Run();
EXPECT_EQ(radar_clients_[0].burst_count, 0);
EXPECT_EQ(radar_clients_[1].burst_count, 10);
EXPECT_EQ(radar_clients_[2].burst_count, 10);
EXPECT_EQ(radar_clients_[0].error_status, fuchsia_hardware_radar::StatusCode::kSuccess);
EXPECT_EQ(radar_clients_[1].error_status, fuchsia_hardware_radar::StatusCode::kSensorError);
EXPECT_EQ(radar_clients_[2].error_status, fuchsia_hardware_radar::StatusCode::kSensorError);
}
TEST_F(RadarReaderProxyOnBurstTest, OnlyOffendingClientReceivesOutOfVmosError) {
// Start bursts for client 3, which we configured to not unlock VMOs. Client 3 should get a burst
// for every registered VMO (five), then a kOutOfVmos error. Other clients should continue to get
// bursts delivered like normal.
EXPECT_TRUE(radar_clients_[1]->StartBursts().is_ok());
EXPECT_TRUE(radar_clients_[2]->StartBursts().is_ok());
EXPECT_TRUE(radar_clients_[3]->StartBursts().is_ok());
radar_clients_[0]->GetBurstProperties().Then([&](auto& result) { fake_driver_.SendBurst(); });
loop_.Run();
EXPECT_EQ(radar_clients_[1].burst_count, 10);
EXPECT_EQ(radar_clients_[2].burst_count, 10);
EXPECT_EQ(radar_clients_[3].burst_count, 5);
EXPECT_EQ(radar_clients_[1].error_status, fuchsia_hardware_radar::StatusCode::kSuccess);
EXPECT_EQ(radar_clients_[2].error_status, fuchsia_hardware_radar::StatusCode::kSuccess);
EXPECT_EQ(radar_clients_[3].error_status, fuchsia_hardware_radar::StatusCode::kOutOfVmos);
}
TEST_F(RadarReaderProxyOnBurstTest, DisconnectedClientStopsReceivingBursts) {
// Client 1 disconnects, now clients 0 and 2 get bursts.
EXPECT_TRUE(radar_clients_[0]->StartBursts().is_ok());
EXPECT_TRUE(radar_clients_[1]->StartBursts().is_ok());
EXPECT_TRUE(radar_clients_[2]->StartBursts().is_ok());
radar_clients_[1].client = fidl::Client<fuchsia_hardware_radar::RadarBurstReader>();
radar_clients_[0]->GetBurstProperties().Then([&](auto& result) { fake_driver_.SendBurst(); });
loop_.Run();
EXPECT_EQ(radar_clients_[0].burst_count, 10);
EXPECT_EQ(radar_clients_[1].burst_count, 0);
EXPECT_EQ(radar_clients_[2].burst_count, 10);
}
TEST_F(RadarReaderProxyOnBurstTest, BurstsStoppedAfterAllClientsDisconnect) {
// All clients disconnect, bursts should be stopped.
EXPECT_TRUE(radar_clients_[0]->StartBursts().is_ok());
EXPECT_TRUE(radar_clients_[1]->StartBursts().is_ok());
EXPECT_TRUE(radar_clients_[2]->StartBursts().is_ok());
radar_clients_[0]->GetBurstProperties().Then([&](auto& result) { fake_driver_.SendBurst(); });
radar_clients_.clear();
EXPECT_OK(fake_driver_.WaitForBurstsStopped());
}
TEST_F(RadarReaderProxyOnBurstTest, StoppedClientStopsReceivingBursts) {
// A client that stops bursts should not receive any more bursts.
EXPECT_TRUE(radar_clients_[0]->StartBursts().is_ok());
EXPECT_TRUE(radar_clients_[1]->StartBursts().is_ok());
radar_clients_[0]->GetBurstProperties().Then([&](auto& result) { fake_driver_.SendBurst(); });
loop_.Run();
loop_.ResetQuit();
EXPECT_EQ(radar_clients_[0].burst_count, 10);
EXPECT_EQ(radar_clients_[1].burst_count, 10);
EXPECT_EQ(radar_clients_[2].burst_count, 0);
radar_clients_[0].burst_count = 0;
radar_clients_[1].burst_count = 0;
radar_clients_[2].burst_count = 0;
radar_clients_[1]->StopBursts().Then([&](auto& result) { EXPECT_TRUE(result.is_ok()); });
EXPECT_TRUE(radar_clients_[2]->StartBursts().is_ok());
radar_clients_[0]->GetBurstProperties().Then([&](auto& result) { fake_driver_.SendBurst(); });
loop_.Run();
EXPECT_EQ(radar_clients_[0].burst_count, 10);
EXPECT_EQ(radar_clients_[1].burst_count, 0);
EXPECT_EQ(radar_clients_[2].burst_count, 10);
}
class RadarReaderProxyInjectionTest : public RadarReaderProxyTest {
public:
RadarReaderProxyInjectionTest() : loop_(&kAsyncLoopConfigNeverAttachToThread) {}
void SetUp() override {
fake_driver_.set_burst_size(16);
RadarReaderProxyTest::SetUp();
AddRadarDevice();
{
zx::result endpoints = fidl::CreateEndpoints<fuchsia_hardware_radar::RadarBurstReader>();
ASSERT_TRUE(endpoints.is_ok());
reader_client_.emplace(std::move(endpoints->client), loop_.dispatcher(),
fit::bind_member<&RadarReaderProxyInjectionTest::OnBurst>(this));
EXPECT_TRUE(dut_client_->Connect(std::move(endpoints->server)).is_ok());
}
{
std::vector<uint32_t> vmo_ids;
std::vector<zx::vmo> vmos;
for (uint32_t i = 0; i < 15; i++) {
EXPECT_OK(zx::vmo::create(16, 0, &vmos.emplace_back()));
EXPECT_OK(vmos.back().duplicate(ZX_RIGHT_SAME_RIGHTS, &registered_vmos_.emplace_back()));
vmo_ids.emplace_back(i + 1);
}
reader_client_.value()
->RegisterVmos({std::move(vmo_ids), std::move(vmos)})
.Then([](auto& result) { EXPECT_TRUE(result.is_ok()); });
}
{
zx::result endpoints = fidl::CreateEndpoints<fuchsia_hardware_radar::RadarBurstInjector>();
ASSERT_TRUE(endpoints.is_ok());
injector_client_.emplace(std::move(endpoints->client), loop_.dispatcher(),
[&](uint32_t bursts_id) {
received_vmo_ids_.push_back(bursts_id);
if (on_bursts_delivered_) {
on_bursts_delivered_();
}
});
BindInjector(std::move(endpoints->server));
}
}
protected:
struct BurstInjectorClient : fidl::AsyncEventHandler<fuchsia_hardware_radar::RadarBurstInjector> {
public:
BurstInjectorClient(fidl::ClientEnd<fuchsia_hardware_radar::RadarBurstInjector> client_end,
async_dispatcher_t* dispatcher, std::function<void(uint32_t)> callback)
: client(std::move(client_end), dispatcher, this),
bursts_delivered_callback(std::move(callback)) {}
void OnBurstsDelivered(
fidl::Event<fuchsia_hardware_radar::RadarBurstInjector::OnBurstsDelivered>& event)
override {
bursts_delivered_callback(event.bursts_id());
}
fidl::Client<fuchsia_hardware_radar::RadarBurstInjector>& operator->() { return client; }
fidl::Client<fuchsia_hardware_radar::RadarBurstInjector> client;
fuchsia_hardware_radar::StatusCode error_status = fuchsia_hardware_radar::StatusCode::kSuccess;
private:
const std::function<void(uint32_t)> bursts_delivered_callback;
};
fidl::Request<fuchsia_hardware_radar::RadarBurstInjector::EnqueueBursts> CreateInjectionVmo(
uint8_t count, uint8_t offset = 0) {
zx::vmo vmo;
EXPECT_OK(zx::vmo::create(count * 16, 0, &vmo));
for (uint8_t i = 0; i < count; i++) {
uint8_t byte = i + offset;
EXPECT_OK(vmo.write(&byte, i * 16, sizeof(byte)));
}
return {{std::move(vmo), count}};
}
async::Loop loop_;
std::optional<BurstReaderClient> reader_client_;
std::optional<BurstInjectorClient> injector_client_;
std::vector<uint8_t> received_burst_headers_;
std::vector<zx::vmo> registered_vmos_;
std::vector<uint32_t> received_vmo_ids_;
fit::function<void(size_t)> on_burst_;
fit::function<void(void)> on_bursts_delivered_;
private:
void OnBurst(uint32_t vmo_id) {
ASSERT_GE(vmo_id, 1);
ASSERT_LE(vmo_id, 15);
uint8_t byte = 0;
EXPECT_OK(registered_vmos_[vmo_id - 1].read(&byte, 0, sizeof(byte)));
received_burst_headers_.push_back(byte);
if (on_burst_) {
on_burst_(received_burst_headers_.size());
}
}
};
TEST_F(RadarReaderProxyInjectionTest, InjectBursts) {
on_burst_ = [&](size_t burst_count) {
if (burst_count == 2) {
// Start burst injection after two real bursts.
injector_client_.value()->StartBurstInjection().Then(
[&](auto& result) { EXPECT_TRUE(result.is_ok()); });
// Immediately send a request to stop injection, which will get completed after all three
// burst buffers (containing a total of eight bursts) have been sent.
injector_client_.value()->StopBurstInjection().Then([&](auto& result) {
EXPECT_TRUE(result.is_ok());
// Send a burst from the driver to continue the test from the burst callback.
fake_driver_.SendBurst();
});
} else if (burst_count == 12) {
// Stop the loop after 12 total bursts.
loop_.Quit();
} else if (burst_count < 2 || burst_count > 10) {
fake_driver_.SendBurst();
}
};
EXPECT_TRUE(reader_client_.value()->StartBursts().is_ok());
injector_client_.value()->EnqueueBursts(CreateInjectionVmo(3, 0)).Then([&](auto& result) {
EXPECT_TRUE(result.is_ok());
});
injector_client_.value()->EnqueueBursts(CreateInjectionVmo(2, 3)).Then([&](auto& result) {
EXPECT_TRUE(result.is_ok());
});
injector_client_.value()->EnqueueBursts(CreateInjectionVmo(3, 5)).Then([&](auto& result) {
EXPECT_TRUE(result.is_ok());
// Send a burst to the client to trigger the the test.
reader_client_.value()->GetBurstProperties().Then(
[&](auto& result) { fake_driver_.SendBurst(); });
});
loop_.Run();
const std::vector<uint8_t> kExpectedBurstHeaders = {0xff, 0xff, 0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07, 0xff, 0xff};
ASSERT_EQ(received_burst_headers_.size(), kExpectedBurstHeaders.size());
EXPECT_EQ(received_burst_headers_, kExpectedBurstHeaders);
const std::vector<uint32_t> kExpectedVmoIds = {1, 2, 3};
ASSERT_EQ(received_vmo_ids_.size(), kExpectedVmoIds.size());
EXPECT_EQ(received_vmo_ids_, kExpectedVmoIds);
}
TEST_F(RadarReaderProxyInjectionTest, InjectionPausedWithNoEnqueuedVmos) {
on_bursts_delivered_ = [&]() {
if (received_vmo_ids_.size() == 1) {
// The last burst from the original VMO has been received, now enqueue a new one. We should
// still not miss any injected bursts.
injector_client_.value()->EnqueueBursts(CreateInjectionVmo(2, 3)).Then([&](auto& result) {
EXPECT_TRUE(result.is_ok());
});
} else if (received_vmo_ids_.size() == 2) {
injector_client_.value()->StopBurstInjection().Then([&](auto& result) {
EXPECT_TRUE(result.is_ok());
// We know that the last bursts will have been sent on the reader client channel by the time
// StopBurstInjection() replies. We can ensure that all bursts have been handled by sending
// another request with the reader client then stopping the loop when the response is
// received.
reader_client_.value()->GetBurstProperties().Then([&](auto& result) { loop_.Quit(); });
});
}
};
// Start burst injection, then enqueue a burst VMO. Clients should not miss any injected bursts.
injector_client_.value()->StartBurstInjection().Then([&](auto& result) {
EXPECT_TRUE(result.is_ok());
EXPECT_TRUE(reader_client_.value()->StartBursts().is_ok());
reader_client_.value()->GetBurstProperties().Then([&](auto& result) {
injector_client_.value()->EnqueueBursts(CreateInjectionVmo(3, 0)).Then([&](auto& result) {
EXPECT_TRUE(result.is_ok());
});
});
});
loop_.Run();
const std::vector<uint8_t> kExpectedBurstHeaders = {0, 1, 2, 3, 4};
ASSERT_EQ(received_burst_headers_.size(), kExpectedBurstHeaders.size());
EXPECT_EQ(received_burst_headers_, kExpectedBurstHeaders);
// The buffer ID counter should be reset after the first one is returned to us.
const std::vector<uint32_t> kExpectedVmoIds = {1, 1};
ASSERT_EQ(received_vmo_ids_.size(), kExpectedVmoIds.size());
EXPECT_EQ(received_vmo_ids_, kExpectedVmoIds);
}
TEST_F(RadarReaderProxyInjectionTest, BurstsNotDeliveredToStopppedClients) {
zx::result endpoints = fidl::CreateEndpoints<fuchsia_hardware_radar::RadarBurstReader>();
ASSERT_TRUE(endpoints.is_ok());
EXPECT_TRUE(dut_client_->Connect(std::move(endpoints->server)).is_ok());
BurstReaderClient started_client(std::move(endpoints->client), loop_.dispatcher(), [&](uint32_t) {
if (started_client.burst_count == 3) {
loop_.Quit();
}
});
for (uint32_t i = 0; i < 3; i++) {
std::vector<zx::vmo> vmos;
EXPECT_OK(zx::vmo::create(16, 0, &vmos.emplace_back()));
started_client->RegisterVmos({std::vector<uint32_t>{i}, std::move(vmos)})
.Then([&](auto& result) { EXPECT_TRUE(result.is_ok()); });
}
EXPECT_TRUE(started_client->StartBursts().is_ok());
started_client->GetBurstProperties().Then([&](auto& result) {
injector_client_.value()->EnqueueBursts(CreateInjectionVmo(3, 0)).Then([&](auto& result) {
EXPECT_TRUE(result.is_ok());
});
injector_client_.value()->StartBurstInjection().Then(
[&](auto& result) { EXPECT_TRUE(result.is_ok()); });
});
loop_.Run();
// The original (stopped) client should receive zero bursts, while the started client should
// receive three.
EXPECT_EQ(reader_client_->burst_count, 0);
EXPECT_EQ(started_client.burst_count, 3);
}
TEST_F(RadarReaderProxyInjectionTest, BurstsReceivedDuringInjectionAreIgnored) {
on_burst_ = [&](size_t) {
if (received_burst_headers_.size() == 5 && received_vmo_ids_.size() == 1) {
loop_.Quit();
}
};
on_bursts_delivered_ = [&]() {
if (received_burst_headers_.size() == 5 && received_vmo_ids_.size() == 1) {
loop_.Quit();
}
};
injector_client_.value()->EnqueueBursts(CreateInjectionVmo(5)).Then([&](auto& result) {
EXPECT_TRUE(result.is_ok());
});
EXPECT_TRUE(reader_client_.value()->StartBursts().is_ok());
reader_client_.value()->GetBurstProperties().Then([&](auto& result) {
injector_client_.value()->StartBurstInjection().Then([&](auto& result) {
EXPECT_TRUE(result.is_ok());
// Send three bursts from the driver after injection has been enabled and the client has been
// started. All three should be ignored in favor of the injected bursts.
fake_driver_.SendBurst();
fake_driver_.SendBurst();
fake_driver_.SendBurst();
});
});
loop_.Run();
const std::vector<uint8_t> kExpectedBursts = {0, 1, 2, 3, 4};
ASSERT_EQ(received_burst_headers_.size(), kExpectedBursts.size());
EXPECT_EQ(received_burst_headers_, kExpectedBursts);
const std::vector<uint32_t> kExpectedVmoIds = {1};
ASSERT_EQ(received_vmo_ids_.size(), kExpectedVmoIds.size());
EXPECT_EQ(received_vmo_ids_, kExpectedVmoIds);
}
TEST_F(RadarReaderProxyInjectionTest, CallsFailsWithBadState) {
injector_client_.value()->StartBurstInjection().Then(
[&](auto& result) { EXPECT_TRUE(result.is_ok()); });
injector_client_.value()->StartBurstInjection().Then([&](auto& result) {
ASSERT_FALSE(result.is_ok());
ASSERT_TRUE(result.error_value().is_domain_error());
EXPECT_EQ(result.error_value().domain_error(), fuchsia_hardware_radar::StatusCode::kBadState);
});
injector_client_.value()->StopBurstInjection().Then(
[&](auto& result) { EXPECT_TRUE(result.is_ok()); });
injector_client_.value()->StopBurstInjection().Then([&](auto& result) {
ASSERT_FALSE(result.is_ok());
ASSERT_TRUE(result.error_value().is_domain_error());
EXPECT_EQ(result.error_value().domain_error(), fuchsia_hardware_radar::StatusCode::kBadState);
loop_.Quit();
});
loop_.Run();
}
TEST_F(RadarReaderProxyInjectionTest, OnlyOneInjectorCanConnect) {
struct : public fidl::AsyncEventHandler<fuchsia_hardware_radar::RadarBurstInjector> {
void on_fidl_error(fidl::UnbindInfo info) override {
EXPECT_EQ(info.status(), ZX_ERR_ALREADY_BOUND);
fidl_error_callback();
}
fit::callback<void(void)> fidl_error_callback;
} client_disconnect_waiter;
client_disconnect_waiter.fidl_error_callback = [&]() { loop_.Quit(); };
zx::result endpoints = fidl::CreateEndpoints<fuchsia_hardware_radar::RadarBurstInjector>();
ASSERT_TRUE(endpoints.is_ok());
BindInjector(std::move(endpoints->server));
fidl::Client<fuchsia_hardware_radar::RadarBurstInjector> client(
std::move(endpoints->client), loop_.dispatcher(), &client_disconnect_waiter);
loop_.Run();
}
TEST_F(RadarReaderProxyTest, ConnectInjectorBeforeRadarDevice) {
async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
zx::result endpoints = fidl::CreateEndpoints<fuchsia_hardware_radar::RadarBurstInjector>();
ASSERT_TRUE(endpoints.is_ok());
BindInjector(std::move(endpoints->server));
fidl::Client<fuchsia_hardware_radar::RadarBurstInjector> client(std::move(endpoints->client),
loop.dispatcher());
// Issue two requests before adding the radar device. They should be fulfilled after the proxy
// connects.
client->StartBurstInjection().Then([&](auto& result) { EXPECT_TRUE(result.is_ok()); });
client->StopBurstInjection().Then([&](auto& result) {
EXPECT_TRUE(result.is_ok());
loop.Quit();
});
AddRadarDevice();
loop.Run();
}
} // namespace
} // namespace radar