blob: 3403f26018aa028ae6c3903ec7735ef8e0895168 [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 <arpa/inet.h>
#include <fuchsia/gpu/agis/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/sys/cpp/component_context.h>
#include <lib/syslog/global.h>
#include <lib/zxio/zxio.h>
#include <thread>
#include <gtest/gtest.h>
#include <sdk/lib/zxio/include/lib/zxio/zxio.h>
namespace {
std::atomic<int> outstanding = 0;
zx_koid_t ProcessKoid() {
zx::unowned<zx::process> process = zx::process::self();
zx_info_handle_basic_t info;
zx_status_t status = zx_object_get_info(process->get(), ZX_INFO_HANDLE_BASIC, &info, sizeof(info),
nullptr /* actual */, nullptr /* avail */);
EXPECT_EQ(status, ZX_OK);
return info.koid;
}
std::string ProcessName() {
zx::unowned<zx::process> process = zx::process::self();
char process_name[ZX_MAX_NAME_LEN];
process->get_property(ZX_PROP_NAME, process_name, sizeof(process_name));
return process_name;
}
uint64_t TimeMS() {
return std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
}
} // namespace
// AgisTest uses asynchronous FIDL entrypoints to provide reusable code for real
// world usage of the agis service.
class AgisTest : public testing::Test {
protected:
void SetUp() override {
num_vtcs_ = 0;
loop_ = std::make_unique<async::Loop>(&kAsyncLoopConfigAttachToCurrentThread);
std::unique_ptr<sys::ComponentContext> context = sys::ComponentContext::Create();
context->svc()->Connect(component_registry_.NewRequest(loop_->dispatcher()));
component_registry_.set_error_handler([this](zx_status_t status) {
FX_LOG(ERROR, "agis-test", "AgisTest::ErrHandler ComponentRegistry");
loop_->Quit();
});
context->svc()->Connect(observer_.NewRequest(loop_->dispatcher()));
observer_.set_error_handler([this](zx_status_t status) {
FX_LOG(ERROR, "agis-test", "AgisTest::ErrHandler Observer");
loop_->Quit();
});
process_koid_ = ProcessKoid();
process_name_ = ProcessName();
time_ms_ = TimeMS();
}
void TearDown() override {
if (loop_) {
LoopWait();
loop_->Quit();
}
}
void LoopWait() {
while (outstanding) {
EXPECT_EQ(loop_->RunUntilIdle(), ZX_OK);
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
}
void Register(uint64_t id, zx_koid_t process_koid, std::string process_name) {
outstanding++;
component_registry_->Register(
id, process_koid, std::move(process_name),
[&](fuchsia::gpu::agis::ComponentRegistry_Register_Result result) {
fuchsia::gpu::agis::ComponentRegistry_Register_Response response(
std::move(result.response()));
EXPECT_FALSE(result.err());
zx::socket gapii_socket = response.ResultValue_();
EXPECT_NE(gapii_socket, ZX_HANDLE_INVALID);
outstanding--;
});
}
void Unregister(uint64_t id) {
outstanding++;
component_registry_->Unregister(
id, [&](fuchsia::gpu::agis::ComponentRegistry_Unregister_Result result) {
EXPECT_FALSE(result.err());
outstanding--;
});
}
void Vtcs() {
outstanding++;
observer_->Vtcs([&](fuchsia::gpu::agis::Observer_Vtcs_Result result) {
fuchsia::gpu::agis::Observer_Vtcs_Response response(std::move(result.response()));
std::vector<fuchsia::gpu::agis::Vtc> vtcs(response.ResultValue_());
for (auto &vtc : vtcs) {
FX_LOGF(INFO, "agis-test", "AgisTest::Vtc \"%lu\" \"%s\"", vtc.process_koid(),
vtc.process_name().c_str());
}
num_vtcs_ = vtcs.size();
outstanding--;
});
}
std::unique_ptr<async::Loop> loop_;
fuchsia::gpu::agis::ComponentRegistryPtr component_registry_;
fuchsia::gpu::agis::ObserverPtr observer_;
size_t num_vtcs_;
zx_koid_t process_koid_;
std::string process_name_;
// |time_ms| is used as the unique vtc ID throughout all tests.
uint64_t time_ms_;
};
TEST_F(AgisTest, Register) {
Register(time_ms_, process_koid_, process_name_);
outstanding++;
component_registry_->Register(time_ms_, process_koid_, process_name_,
[&](fuchsia::gpu::agis::ComponentRegistry_Register_Result result) {
EXPECT_EQ(result.err(),
fuchsia::gpu::agis::Error::ALREADY_REGISTERED);
outstanding--;
});
Unregister(time_ms_);
}
TEST_F(AgisTest, Unregister) {
Register(time_ms_, process_koid_, process_name_);
Unregister(time_ms_);
component_registry_->Unregister(
time_ms_, [&](fuchsia::gpu::agis::ComponentRegistry_Unregister_Result result) {
EXPECT_TRUE(result.is_err());
EXPECT_EQ(result.err(), fuchsia::gpu::agis::Error::NOT_FOUND);
});
}
TEST_F(AgisTest, Vtcs) {
Register(time_ms_, process_koid_, process_name_);
Register(time_ms_ + 1, process_koid_, process_name_ + "+1");
LoopWait();
Vtcs();
LoopWait();
EXPECT_EQ(num_vtcs_, 2ul);
Unregister(time_ms_);
Unregister(time_ms_ + 1);
LoopWait();
Vtcs();
LoopWait();
EXPECT_EQ(num_vtcs_, 0ul);
}
TEST_F(AgisTest, MaxVtcs) {
uint32_t i = 0;
for (i = 0; i < fuchsia::gpu::agis::MAX_VTCS; i++) {
Register(time_ms_ + i, process_koid_, process_name_ + "+" + std::to_string(i));
}
outstanding++;
component_registry_->Register(time_ms_ + i, process_koid_,
process_name_ + "+" + std::to_string(i),
[&](fuchsia::gpu::agis::ComponentRegistry_Register_Result result) {
EXPECT_EQ(result.err(), fuchsia::gpu::agis::Error::VTCS_EXCEEDED);
outstanding--;
});
for (i = 0; i < fuchsia::gpu::agis::MAX_VTCS; i++) {
Unregister(time_ms_ + i);
}
}
TEST_F(AgisTest, UsableSocket) {
// Register and retrieve the socket for the server.
outstanding++;
zx::socket gapii_socket;
component_registry_->Register(time_ms_, process_koid_, process_name_,
[&](fuchsia::gpu::agis::ComponentRegistry_Register_Result result) {
fuchsia::gpu::agis::ComponentRegistry_Register_Response response(
std::move(result.response()));
EXPECT_FALSE(result.err());
gapii_socket = response.ResultValue_();
outstanding--;
});
LoopWait();
EXPECT_NE(gapii_socket, ZX_HANDLE_INVALID);
// Get the socket endpoint assignment for the client.
outstanding++;
zx::socket agi_socket;
std::string process_name;
zx_koid_t process_koid;
observer_->Vtcs([&](fuchsia::gpu::agis::Observer_Vtcs_Result result) {
fuchsia::gpu::agis::Observer_Vtcs_Response response(std::move(result.response()));
EXPECT_FALSE(result.err());
std::vector<fuchsia::gpu::agis::Vtc> vtcs(response.ResultValue_());
EXPECT_EQ(vtcs.size(), 1ul);
vtcs.front().agi_socket().duplicate(ZX_RIGHT_SAME_RIGHTS, &agi_socket);
process_name = vtcs.front().process_name();
process_koid = vtcs.front().process_koid();
outstanding--;
});
LoopWait();
EXPECT_NE(agi_socket, ZX_HANDLE_INVALID);
EXPECT_EQ(process_name, process_name_);
EXPECT_EQ(process_koid, process_koid_);
// Send message from gapii end.
const char message[] = "AGIS Server Message";
size_t actual = 0;
zx_status_t status = gapii_socket.write(0u, message, sizeof(message), &actual);
ASSERT_EQ(status, ZX_OK);
EXPECT_EQ(actual, sizeof(message));
// Read message from agi end.
char buffer[sizeof(message)];
actual = 0;
status = agi_socket.read(0u, &buffer, sizeof(buffer), &actual);
ASSERT_EQ(status, ZX_OK);
EXPECT_EQ(actual, sizeof(message));
EXPECT_EQ(strcmp(message, buffer), 0);
}
TEST(AgisDisconnect, Main) {
zx_koid_t process_koid = ProcessKoid();
std::string process_name = ProcessName();
uint64_t time_ms = TimeMS();
bool disconnect_outstanding = false;
auto loop = std::make_unique<async::Loop>(&kAsyncLoopConfigAttachToCurrentThread);
std::unique_ptr<sys::ComponentContext> context = sys::ComponentContext::Create();
auto loop_wait = [&disconnect_outstanding, &loop]() {
while (disconnect_outstanding) {
EXPECT_EQ(loop->RunUntilIdle(), ZX_OK);
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
};
// Create a component_registry, register |time_ms| as the ID and verify its presence.
{
fuchsia::gpu::agis::ComponentRegistryPtr component_registry;
context->svc()->Connect(component_registry.NewRequest(loop->dispatcher()));
component_registry.set_error_handler([&loop](zx_status_t status) {
FX_LOGF(ERROR, "agis-test", "Register Disconnect ComponentRegistry ErrHandler - status %d",
status);
if (loop) {
loop->Quit();
}
});
disconnect_outstanding = true;
component_registry->Register(time_ms, process_koid, process_name,
[&](fuchsia::gpu::agis::ComponentRegistry_Register_Result result) {
EXPECT_FALSE(result.err());
disconnect_outstanding = false;
});
loop_wait();
fuchsia::gpu::agis::ObserverPtr observer;
context->svc()->Connect(observer.NewRequest(loop->dispatcher()));
observer.set_error_handler([&loop](zx_status_t status) {
FX_LOGF(ERROR, "agis-test", "Register Disconnect Observer ErrHandler - status %d", status);
if (loop) {
loop->Quit();
}
});
disconnect_outstanding = true;
observer->Vtcs([&](fuchsia::gpu::agis::Observer_Vtcs_Result result) {
fuchsia::gpu::agis::Observer_Vtcs_Response response(std::move(result.response()));
EXPECT_FALSE(result.err());
std::vector<fuchsia::gpu::agis::Vtc> vtcs(response.ResultValue_());
EXPECT_EQ(vtcs.size(), 1ul);
bool found = false;
for (const auto &vtc : vtcs) {
if (vtc.process_koid() == process_koid) {
EXPECT_EQ(vtc.process_name(), process_name);
found = true;
break;
}
}
EXPECT_TRUE(found);
disconnect_outstanding = false;
});
loop_wait();
}
// Create a new observer and verify that |process_koid| is no longer registered.
fuchsia::gpu::agis::ObserverPtr observer;
context->svc()->Connect(observer.NewRequest(loop->dispatcher()));
observer.set_error_handler([&loop](zx_status_t status) {
FX_LOGF(ERROR, "agis-test", "Verify Disconnect Observer ErrHandler - status %d", status);
if (loop) {
loop->Quit();
}
});
bool found = true;
while (found) {
disconnect_outstanding = true;
observer->Vtcs([&](fuchsia::gpu::agis::Observer_Vtcs_Result result) {
EXPECT_FALSE(result.err());
auto vtcs(result.response().ResultValue_());
bool component_found = false;
for (const auto &vtc : vtcs) {
if (vtc.process_koid() == process_koid) {
EXPECT_EQ(vtc.process_name(), process_name);
component_found = true;
break;
}
}
found = component_found;
disconnect_outstanding = false;
});
loop_wait();
}
// Self-documenting no-op.
EXPECT_FALSE(found);
loop->Quit();
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}