blob: e465a6da161d9c94f738624a76abe2d699a48d5b [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.radar/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <lib/fit/function.h>
#include <lib/zx/time.h>
#include <lib/zx/vmo.h>
#include <map>
#include <memory>
#include <vector>
#include <zxtest/zxtest.h>
namespace {
constexpr uint32_t kBurstSize = 23247;
enum RadarEvent {
kOnBurst,
kOnBurstsDelivered,
};
class ReaderClient : public fidl::AsyncEventHandler<fuchsia_hardware_radar::RadarBurstReader> {
public:
ReaderClient(fidl::ClientEnd<fuchsia_hardware_radar::RadarBurstReader> client_end,
async_dispatcher_t* dispatcher, fit::function<void(RadarEvent)> on_test_event)
: client_(std::move(client_end), dispatcher, this),
on_test_event_(std::move(on_test_event)) {}
fidl::Client<fuchsia_hardware_radar::RadarBurstReader>& operator->() { return client_; }
size_t real_bursts() const { return real_bursts_; }
size_t injected_bursts() const { return injected_bursts_; }
void RegisterVmos(uint32_t count) {
EXPECT_EQ(vmos_.size(), 0);
std::vector<uint32_t> vmo_ids;
std::vector<zx::vmo> vmos;
for (uint32_t i = 0; i < count; i++) {
vmo_ids.emplace_back(i);
vmos.emplace_back();
EXPECT_OK(zx::vmo::create(kBurstSize, 0, &vmos.back()));
EXPECT_OK(vmos.back().duplicate(ZX_RIGHT_SAME_RIGHTS, &vmos_.emplace_back()));
}
client_->RegisterVmos({std::move(vmo_ids), std::move(vmos)}).Then([&](const auto& result) {
EXPECT_TRUE(result.is_ok());
});
}
private:
void OnBurst(fidl::Event<fuchsia_hardware_radar::RadarBurstReader::OnBurst>& event) override {
EXPECT_TRUE(event.burst() || event.error());
// The radar driver might report real errors -- just ignore them.
if (event.burst()) {
const uint32_t vmo_id = event.burst()->vmo_id();
ASSERT_LE(vmo_id, vmos_.size());
uint32_t header;
EXPECT_OK(vmos_[vmo_id].read(&header, 0, sizeof(header)));
(header == 0 ? real_bursts_ : injected_bursts_)++;
EXPECT_TRUE(client_->UnlockVmo(vmo_id).is_ok());
on_test_event_(kOnBurst);
}
}
fidl::Client<fuchsia_hardware_radar::RadarBurstReader> client_;
const fit::function<void(RadarEvent)> on_test_event_;
std::vector<zx::vmo> vmos_;
size_t real_bursts_ = 0;
size_t injected_bursts_ = 0;
};
class InjectorClient : public fidl::AsyncEventHandler<fuchsia_hardware_radar::RadarBurstInjector> {
public:
InjectorClient(fidl::ClientEnd<fuchsia_hardware_radar::RadarBurstInjector> client_end,
async_dispatcher_t* dispatcher, fit::function<void(RadarEvent)> on_test_event)
: client_(std::move(client_end), dispatcher, this),
on_test_event_(std::move(on_test_event)) {}
fidl::Client<fuchsia_hardware_radar::RadarBurstInjector>& operator->() { return client_; }
void EnqueueBursts(uint32_t count) {
zx::vmo vmo;
EXPECT_OK(zx::vmo::create(static_cast<size_t>(count) * kBurstSize, 0, &vmo));
for (uint32_t i = 0; i < count; i++) {
const size_t offset = static_cast<size_t>(i) * kBurstSize;
EXPECT_OK(vmo.write(&kInjectedBurstHeader, offset, sizeof(kInjectedBurstHeader)));
}
client_->EnqueueBursts({{std::move(vmo), count}}).Then([&](const auto& result) {
EXPECT_TRUE(result.is_ok());
});
}
private:
// Use any non-zero value to indicate an injected burst.
static constexpr uint32_t kInjectedBurstHeader = 0xffff'ffff;
void OnBurstsDelivered(
fidl::Event<fuchsia_hardware_radar::RadarBurstInjector::OnBurstsDelivered>& event) override {
on_test_event_(kOnBurstsDelivered);
}
fidl::Client<fuchsia_hardware_radar::RadarBurstInjector> client_;
const fit::function<void(RadarEvent)> on_test_event_;
};
TEST(RadarInjectionIntegrationTest, InjectBursts) {
std::optional<ReaderClient> reader1;
std::optional<ReaderClient> reader2;
std::optional<InjectorClient> injector;
async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
enum TestState {
kReceivedRealBurstsStart,
kBurstsInjected,
kStopOneClient,
kMoreBurstsInjected,
kReceivedRealBurstsEnd,
kTestEnd,
};
TestState current_state = kReceivedRealBurstsStart;
size_t burst_count_after_injection = UINT32_MAX;
const auto on_test_event = [&](const RadarEvent event) {
TestState next_state = current_state;
switch (current_state) {
case kReceivedRealBurstsStart:
// Both clients have received at least one real burst. Enqueue six bursts to be injected and
// start injection.
if (reader1->real_bursts() >= 1 && reader2->real_bursts() >= 1) {
injector->EnqueueBursts(3);
injector->EnqueueBursts(3);
(*injector)->StartBurstInjection().Then(
[&](const auto& result) { EXPECT_TRUE(result.is_ok()); });
next_state = kBurstsInjected;
}
break;
case kBurstsInjected:
// One of our bursts VMOs was just returned. Enqueue another three bursts to be injected.
if (event == kOnBurstsDelivered) {
injector->EnqueueBursts(3);
next_state = kStopOneClient;
}
break;
case kStopOneClient:
// All nine bursts have been sent to clients. Stop one of the clients, then enqueue three
// more bursts.
if (reader2->injected_bursts() >= 9) {
(*reader2)->StopBursts().Then([&](const auto& result) {
EXPECT_TRUE(result.is_ok());
injector->EnqueueBursts(3);
});
next_state = kMoreBurstsInjected;
} else if (event == kOnBurstsDelivered) {
// Continue injecting bursts until clients receive them all. There's a chance that some
// will be missed if we don't manage to unlock in time.
injector->EnqueueBursts(3);
}
break;
case kMoreBurstsInjected:
// The next three bursts have been sent to clients. Stop burst injection.
if (reader1->injected_bursts() >= 12) {
(*injector)->StopBurstInjection().Then([&](const auto& result) {
burst_count_after_injection = reader1->real_bursts();
EXPECT_TRUE(result.is_ok());
});
next_state = kReceivedRealBurstsEnd;
} else if (event == kOnBurstsDelivered) {
injector->EnqueueBursts(3);
}
break;
case kReceivedRealBurstsEnd:
// At least one more real burst has been received. Stop the client to flush any in-flight
// bursts.
if (reader1->real_bursts() > burst_count_after_injection) {
(*reader1)->StopBursts().Then(
[&](const fidl::Result<fuchsia_hardware_radar::RadarBurstReader::StopBursts>&
result) {
EXPECT_TRUE(result.is_ok());
loop.Quit();
});
next_state = kTestEnd;
}
break;
case kTestEnd:
break;
}
current_state = next_state;
};
auto provider_client_end = component::Connect<fuchsia_hardware_radar::RadarBurstReaderProvider>();
ASSERT_TRUE(provider_client_end.is_ok());
fidl::SyncClient<fuchsia_hardware_radar::RadarBurstReaderProvider> provider_client(
std::move(provider_client_end.value()));
{
auto endpoints = fidl::Endpoints<fuchsia_hardware_radar::RadarBurstReader>::Create();
const auto result = provider_client->Connect(std::move(endpoints.server));
EXPECT_TRUE(result.is_ok());
reader1.emplace(std::move(endpoints.client), loop.dispatcher(), on_test_event);
EXPECT_NO_FAILURES(reader1->RegisterVmos(10));
}
{
auto endpoints = fidl::Endpoints<fuchsia_hardware_radar::RadarBurstReader>::Create();
const auto result = provider_client->Connect(std::move(endpoints.server));
EXPECT_TRUE(result.is_ok());
reader2.emplace(std::move(endpoints.client), loop.dispatcher(), on_test_event);
EXPECT_NO_FAILURES(reader2->RegisterVmos(10));
}
auto injector_client_end = component::Connect<fuchsia_hardware_radar::RadarBurstInjector>();
ASSERT_TRUE(injector_client_end.is_ok());
injector.emplace(std::move(injector_client_end.value()), loop.dispatcher(), on_test_event);
EXPECT_TRUE((*reader1)->StartBursts().is_ok());
EXPECT_TRUE((*reader2)->StartBursts().is_ok());
loop.Run();
}
TEST(RadarInjectionIntegrationTest, BurstsResumedAfterInjectorDisconnects) {
std::optional<ReaderClient> reader;
std::optional<InjectorClient> injector;
async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
enum TestState {
kReceivedRealBurstsStart,
kDisconnectInjector,
kReceivedRealBurstsEnd,
kTestEnd,
};
TestState current_state = kReceivedRealBurstsStart;
size_t burst_count_after_injection = UINT32_MAX;
const auto on_test_event = [&](const RadarEvent event) {
TestState next_state = current_state;
switch (current_state) {
case kReceivedRealBurstsStart:
// The client has received at least one real burst. Enqueue three bursts to be injected and
// start injection.
if (reader->real_bursts() >= 1) {
injector->EnqueueBursts(3);
(*injector)->StartBurstInjection().Then(
[&](const auto& result) { EXPECT_TRUE(result.is_ok()); });
next_state = kDisconnectInjector;
}
break;
case kDisconnectInjector:
// The client has received all three injected bursts. Disconnect the injector and wait for
// radar-proxy to restart bursts from the driver.
if (reader->injected_bursts() >= 3) {
injector.reset();
burst_count_after_injection = reader->real_bursts();
next_state = kReceivedRealBurstsEnd;
} else if (event == kOnBurstsDelivered) {
injector->EnqueueBursts(3);
}
break;
case kReceivedRealBurstsEnd:
// The client has received additional bursts from the driver.
if (reader->real_bursts() > burst_count_after_injection) {
(*reader)->StopBursts().Then([&](const auto& result) {
EXPECT_TRUE(result.is_ok());
loop.Quit();
});
next_state = kTestEnd;
}
break;
case kTestEnd:
break;
}
current_state = next_state;
};
{
auto provider_client_end =
component::Connect<fuchsia_hardware_radar::RadarBurstReaderProvider>();
ASSERT_TRUE(provider_client_end.is_ok());
fidl::SyncClient<fuchsia_hardware_radar::RadarBurstReaderProvider> provider_client(
std::move(provider_client_end.value()));
auto endpoints = fidl::Endpoints<fuchsia_hardware_radar::RadarBurstReader>::Create();
const auto result = provider_client->Connect(std::move(endpoints.server));
EXPECT_TRUE(result.is_ok());
reader.emplace(std::move(endpoints.client), loop.dispatcher(), on_test_event);
EXPECT_NO_FAILURES(reader->RegisterVmos(10));
}
{
auto injector_client_end = component::Connect<fuchsia_hardware_radar::RadarBurstInjector>();
ASSERT_TRUE(injector_client_end.is_ok());
injector.emplace(std::move(injector_client_end.value()), loop.dispatcher(), on_test_event);
}
EXPECT_TRUE((*reader)->StartBursts().is_ok());
loop.Run();
}
TEST(RadarInjectionIntegrationTest, OnlyOneInjectorCanConnect) {
async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
auto injector_client_end = component::Connect<fuchsia_hardware_radar::RadarBurstInjector>();
ASSERT_TRUE(injector_client_end.is_ok());
fidl::Client<fuchsia_hardware_radar::RadarBurstInjector> injector(
std::move(injector_client_end.value()), loop.dispatcher());
injector->GetBurstProperties().Then([&](const auto& result) {
EXPECT_TRUE(result.is_ok());
auto injector_new_client_end = component::Connect<fuchsia_hardware_radar::RadarBurstInjector>();
ASSERT_TRUE(injector_new_client_end.is_ok());
fidl::SyncClient<fuchsia_hardware_radar::RadarBurstInjector> injector_new(
std::move(injector_new_client_end.value()));
EXPECT_FALSE(injector_new->GetBurstProperties().is_ok());
loop.Quit();
});
loop.Run();
}
} // namespace