blob: 6d4b905edc9f78498cd95a272d7ae8daa86c2a03 [file] [log] [blame]
// 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 <endian.h>
#include <fidl/fuchsia.hardware.radar/cpp/wire.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <lib/fdio/fdio.h>
#include <lib/fit/function.h>
#include <lib/sync/completion.h>
#include <lib/zx/channel.h>
#include <stdio.h>
#include <future>
#include <zxtest/zxtest.h>
namespace {
using BurstReaderProvider = fuchsia_hardware_radar::RadarBurstReaderProvider;
using BurstReader = fuchsia_hardware_radar::RadarBurstReader;
using BurstRequest = fuchsia_hardware_radar::wire::RadarBurstReaderOnBurstRequest;
constexpr size_t kBurstSize = 23247;
class RadarIntegrationTest : public zxtest::Test {
public:
RadarIntegrationTest() : loop_(&kAsyncLoopConfigNeverAttachToThread) {}
void SetUp() override { loop_.StartThread("radar-integration-test dispatcher"); }
protected:
std::future<void> MakeRadarClient(fidl::WireSharedClient<BurstReader>* out_client) {
return MakeRadarClient({}, out_client);
}
std::future<void> MakeRadarClient(fit::function<void(const BurstRequest&)> burst_handler,
fidl::WireSharedClient<BurstReader>* out_client) {
std::future<void> out_client_torn_down;
MakeRadarClient(std::move(burst_handler), out_client, &out_client_torn_down);
return out_client_torn_down;
}
static void CheckBurst(const std::array<uint8_t, kBurstSize>& burst) {
uint32_t config_id;
memcpy(&config_id, burst.data(), sizeof(config_id));
EXPECT_EQ(config_id, 0);
EXPECT_EQ(burst[4], 30); // Burst rate in Hz.
EXPECT_EQ(burst[5], 20); // Chirps per burst.
uint16_t chirp_rate_hz;
memcpy(&chirp_rate_hz, &burst[6], sizeof(chirp_rate_hz));
EXPECT_EQ(be16toh(chirp_rate_hz), 3000);
uint16_t samples_per_chirp;
memcpy(&samples_per_chirp, &burst[8], sizeof(samples_per_chirp));
EXPECT_EQ(be16toh(samples_per_chirp), 256);
EXPECT_EQ(burst[10], 0x07); // RX channel mask.
uint64_t driver_timestamp, host_timestamp;
mempcpy(&driver_timestamp, &burst[11], sizeof(driver_timestamp));
mempcpy(&host_timestamp, &burst[19], sizeof(host_timestamp));
EXPECT_EQ(driver_timestamp, host_timestamp);
}
private:
void MakeRadarClient(fit::function<void(const BurstRequest&)> burst_handler,
fidl::WireSharedClient<BurstReader>* out_client,
std::future<void>* out_client_torn_down) {
auto provider_client_end_or = component::Connect<BurstReaderProvider>();
ASSERT_OK(provider_client_end_or.status_value());
fidl::WireSyncClient<BurstReaderProvider> provider_client(
std::move(provider_client_end_or.value()));
auto [client_end, server_end] = fidl::Endpoints<BurstReader>::Create();
const auto result = provider_client->Connect(std::move(server_end));
ASSERT_OK(result.status());
ASSERT_TRUE(result->is_ok(), "%d", static_cast<int>(result->error_value()));
std::promise<void> client_torn_down_promise;
*out_client_torn_down = client_torn_down_promise.get_future();
out_client->Bind(std::move(client_end), loop_.dispatcher(),
std::make_unique<EventHandler>(std::move(burst_handler),
std::move(client_torn_down_promise)));
}
class EventHandler : public fidl::WireAsyncEventHandler<BurstReader> {
public:
explicit EventHandler(fit::function<void(const BurstRequest&)> burst_handler,
std::promise<void> client_torn_down_promise)
: burst_handler_(std::move(burst_handler)),
client_torn_down_promise_(std::move(client_torn_down_promise)) {}
~EventHandler() override { client_torn_down_promise_.set_value(); }
void OnBurst(fidl::WireEvent<BurstReader::OnBurst>* event) override {
if (burst_handler_) {
burst_handler_(*event);
}
}
void on_fidl_error(fidl::UnbindInfo info) override {}
private:
fit::function<void(const BurstRequest&)> burst_handler_;
std::promise<void> client_torn_down_promise_;
};
async::Loop loop_;
};
TEST_F(RadarIntegrationTest, BurstSize) {
fidl::WireSharedClient<BurstReader> client;
ASSERT_NO_FAILURES(MakeRadarClient(&client));
auto result = client.sync()->GetBurstProperties();
ASSERT_OK(result.status());
EXPECT_EQ(result->size, kBurstSize);
}
TEST_F(RadarIntegrationTest, Reconnect) {
fidl::WireSharedClient<BurstReader> client1;
std::future<void> client1_torn_down;
ASSERT_NO_FAILURES(client1_torn_down = MakeRadarClient(&client1));
{
const auto result = client1.sync()->GetBurstProperties();
ASSERT_OK(result.status());
EXPECT_EQ(result->size, kBurstSize);
}
// Unbind and close our end of the channel. We should eventually be able to reconnect, after the
// driver has cleaned up after the last client.
client1 = {};
client1_torn_down.wait();
fidl::WireSharedClient<BurstReader> client2;
ASSERT_NO_FAILURES(MakeRadarClient(&client2));
{
const auto result = client2.sync()->GetBurstProperties();
ASSERT_OK(result.status());
EXPECT_EQ(result->size, kBurstSize);
}
}
TEST_F(RadarIntegrationTest, BurstFormat) {
fidl::Arena allocator;
sync_completion_t completion;
sync_completion_reset(&completion);
uint32_t received_id = {};
fidl::WireSharedClient<BurstReader> client;
ASSERT_NO_FAILURES(MakeRadarClient(
[&](const BurstRequest& request) {
if (request.is_burst()) {
received_id = request.burst().vmo_id;
sync_completion_signal(&completion);
}
},
&client));
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(kBurstSize, 0, &vmo));
{
fidl::VectorView<zx::vmo> vmo_dup(allocator, 1);
ASSERT_OK(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_dup[0]));
fidl::VectorView<uint32_t> vmo_id(allocator, 1);
vmo_id[0] = 1234;
const auto result = client.sync()->RegisterVmos(vmo_id, vmo_dup);
ASSERT_OK(result);
ASSERT_TRUE(result->is_ok(), "%d", static_cast<int>(result->error_value()));
}
EXPECT_OK(client->StartBursts().status());
sync_completion_wait(&completion, ZX_TIME_INFINITE);
EXPECT_OK(client.sync()->StopBursts().status());
EXPECT_EQ(received_id, 1234);
std::array<uint8_t, kBurstSize> burst;
ASSERT_OK(vmo.read(burst.data(), 0, burst.size()));
ASSERT_NO_FATAL_FAILURE(CheckBurst(burst));
{
fidl::VectorView<uint32_t> vmo_id(allocator, 1);
vmo_id[0] = 1234;
const auto result = client.sync()->UnregisterVmos(vmo_id);
ASSERT_OK(result.status());
ASSERT_TRUE(result->is_ok(), "%d", static_cast<int>(result->error_value()));
ASSERT_EQ(result->value()->vmos.count(), 1);
EXPECT_TRUE(result->value()->vmos[0].is_valid());
}
}
TEST_F(RadarIntegrationTest, ReadManyBursts) {
constexpr uint32_t kVmoCount = 10;
constexpr uint32_t kBurstCount = 303; // Read for about 10 seconds.
fidl::Arena allocator;
sync_completion_t completion;
sync_completion_reset(&completion);
uint32_t received_burst_count = 0;
fidl::WireSharedClient<BurstReader> client;
ASSERT_NO_FAILURES(MakeRadarClient(
[&](const BurstRequest& request) {
if (request.is_burst()) {
[[maybe_unused]] auto call_result = client->UnlockVmo(request.burst().vmo_id);
if (++received_burst_count >= kBurstCount) {
sync_completion_signal(&completion);
}
}
},
&client));
std::vector<zx::vmo> vmos(kVmoCount);
{
fidl::VectorView<zx::vmo> vmo_dups(allocator, kVmoCount);
fidl::VectorView<uint32_t> vmo_ids(allocator, kVmoCount);
for (uint32_t i = 0; i < kVmoCount; i++) {
ASSERT_OK(zx::vmo::create(kBurstSize, 0, &vmos[i]));
ASSERT_OK(vmos[i].duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_dups[i]));
vmo_ids[i] = i;
}
const auto result = client.sync()->RegisterVmos(vmo_ids, vmo_dups);
ASSERT_OK(result.status());
ASSERT_TRUE(result->is_ok(), "%d", static_cast<int>(result->error_value()));
}
EXPECT_OK(client->StartBursts().status());
sync_completion_wait(&completion, ZX_TIME_INFINITE);
EXPECT_OK(client.sync()->StopBursts().status());
EXPECT_GE(received_burst_count, kBurstCount);
{
fidl::VectorView<uint32_t> vmo_ids(allocator, kVmoCount);
for (uint32_t i = 0; i < kVmoCount; i++) {
vmo_ids[i] = i;
}
const auto result = client.sync()->UnregisterVmos(vmo_ids);
ASSERT_OK(result.status());
ASSERT_TRUE(result->is_ok(), "%d", static_cast<int>(result->error_value()));
ASSERT_EQ(result->value()->vmos.count(), kVmoCount);
for (size_t i = 0; i < kVmoCount; i++) {
EXPECT_TRUE(result->value()->vmos[i].is_valid());
}
}
}
TEST_F(RadarIntegrationTest, ReadManyBurstsMultipleClients) {
constexpr uint32_t kVmoCount = 10;
constexpr uint32_t kBurstCount = 303; // Read for about 10 seconds.
fidl::Arena allocator;
struct {
fidl::WireSharedClient<BurstReader> client;
sync_completion_t completion;
uint32_t received_burst_count = 0;
} clients[3] = {};
for (auto& client : clients) {
sync_completion_reset(&client.completion);
ASSERT_NO_FAILURES(MakeRadarClient(
[&](const BurstRequest& request) {
if (request.is_burst()) {
[[maybe_unused]] auto call_result = client.client->UnlockVmo(request.burst().vmo_id);
if (++client.received_burst_count >= kBurstCount) {
sync_completion_signal(&client.completion);
}
}
},
&client.client));
std::vector<zx::vmo> vmos(kVmoCount);
fidl::VectorView<zx::vmo> vmo_dups(allocator, kVmoCount);
fidl::VectorView<uint32_t> vmo_ids(allocator, kVmoCount);
for (uint32_t i = 0; i < kVmoCount; i++) {
ASSERT_OK(zx::vmo::create(kBurstSize, 0, &vmos[i]));
ASSERT_OK(vmos[i].duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_dups[i]));
vmo_ids[i] = i;
}
const auto result = client.client.sync()->RegisterVmos(vmo_ids, vmo_dups);
ASSERT_OK(result.status());
ASSERT_TRUE(result->is_ok(), "%d", static_cast<int>(result->error_value()));
}
for (auto& client : clients) {
EXPECT_OK(client.client->StartBursts().status());
}
for (auto& client : clients) {
sync_completion_wait(&client.completion, ZX_TIME_INFINITE);
}
for (auto& client : clients) {
EXPECT_OK(client.client.sync()->StopBursts().status());
}
for (auto& client : clients) {
EXPECT_GE(client.received_burst_count, kBurstCount);
}
for (auto& client : clients) {
fidl::VectorView<uint32_t> vmo_ids(allocator, kVmoCount);
for (uint32_t i = 0; i < kVmoCount; i++) {
vmo_ids[i] = i;
}
const auto result = client.client.sync()->UnregisterVmos(vmo_ids);
ASSERT_OK(result.status());
ASSERT_TRUE(result->is_ok(), "%d", static_cast<int>(result->error_value()));
ASSERT_EQ(result->value()->vmos.count(), kVmoCount);
for (size_t i = 0; i < kVmoCount; i++) {
EXPECT_TRUE(result->value()->vmos[i].is_valid());
}
}
}
} // namespace