feat: Add GUI automation client and test workflow generator
- Implemented GuiAutomationClient for gRPC communication with the test harness. - Added methods for various GUI actions: Click, Type, Wait, Assert, and Screenshot. - Created TestWorkflowGenerator to convert natural language prompts into structured test workflows. - Enhanced HandleTestCommand to support new command-line arguments for GUI automation. - Updated CMakeLists.txt to include new source files for GUI automation and workflow generation.
This commit is contained in:
251
src/cli/service/gui_automation_client.cc
Normal file
251
src/cli/service/gui_automation_client.cc
Normal file
@@ -0,0 +1,251 @@
|
||||
// gui_automation_client.cc
|
||||
// Implementation of gRPC client for YAZE GUI automation
|
||||
|
||||
#include "cli/service/gui_automation_client.h"
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace cli {
|
||||
|
||||
GuiAutomationClient::GuiAutomationClient(const std::string& server_address)
|
||||
: server_address_(server_address) {}
|
||||
|
||||
absl::Status GuiAutomationClient::Connect() {
|
||||
#ifdef YAZE_WITH_GRPC
|
||||
auto channel = grpc::CreateChannel(server_address_,
|
||||
grpc::InsecureChannelCredentials());
|
||||
if (!channel) {
|
||||
return absl::InternalError("Failed to create gRPC channel");
|
||||
}
|
||||
|
||||
stub_ = yaze::test::ImGuiTestHarness::NewStub(channel);
|
||||
if (!stub_) {
|
||||
return absl::InternalError("Failed to create gRPC stub");
|
||||
}
|
||||
|
||||
// Test connection with a ping
|
||||
auto result = Ping("connection_test");
|
||||
if (!result.ok()) {
|
||||
return absl::UnavailableError(
|
||||
absl::StrFormat("Failed to connect to test harness at %s: %s",
|
||||
server_address_, result.status().message()));
|
||||
}
|
||||
|
||||
connected_ = true;
|
||||
return absl::OkStatus();
|
||||
#else
|
||||
return absl::UnimplementedError(
|
||||
"GUI automation requires YAZE_WITH_GRPC=ON at build time");
|
||||
#endif
|
||||
}
|
||||
|
||||
absl::StatusOr<AutomationResult> GuiAutomationClient::Ping(
|
||||
const std::string& message) {
|
||||
#ifdef YAZE_WITH_GRPC
|
||||
if (!stub_) {
|
||||
return absl::FailedPreconditionError("Not connected. Call Connect() first.");
|
||||
}
|
||||
|
||||
yaze::test::PingRequest request;
|
||||
request.set_message(message);
|
||||
|
||||
yaze::test::PingResponse response;
|
||||
grpc::ClientContext context;
|
||||
|
||||
grpc::Status status = stub_->Ping(&context, request, &response);
|
||||
|
||||
if (!status.ok()) {
|
||||
return absl::InternalError(
|
||||
absl::StrFormat("Ping RPC failed: %s", status.error_message()));
|
||||
}
|
||||
|
||||
AutomationResult result;
|
||||
result.success = true;
|
||||
result.message = absl::StrFormat("Server version: %s (timestamp: %s)",
|
||||
response.yaze_version(),
|
||||
response.timestamp_ms());
|
||||
result.execution_time = std::chrono::milliseconds(0);
|
||||
return result;
|
||||
#else
|
||||
return absl::UnimplementedError("gRPC not available");
|
||||
#endif
|
||||
}
|
||||
|
||||
absl::StatusOr<AutomationResult> GuiAutomationClient::Click(
|
||||
const std::string& target, ClickType type) {
|
||||
#ifdef YAZE_WITH_GRPC
|
||||
if (!stub_) {
|
||||
return absl::FailedPreconditionError("Not connected. Call Connect() first.");
|
||||
}
|
||||
|
||||
yaze::test::ClickRequest request;
|
||||
request.set_target(target);
|
||||
|
||||
switch (type) {
|
||||
case ClickType::kLeft:
|
||||
request.set_type(yaze::test::ClickRequest::LEFT);
|
||||
break;
|
||||
case ClickType::kRight:
|
||||
request.set_type(yaze::test::ClickRequest::RIGHT);
|
||||
break;
|
||||
case ClickType::kMiddle:
|
||||
request.set_type(yaze::test::ClickRequest::MIDDLE);
|
||||
break;
|
||||
case ClickType::kDouble:
|
||||
request.set_type(yaze::test::ClickRequest::DOUBLE);
|
||||
break;
|
||||
}
|
||||
|
||||
yaze::test::ClickResponse response;
|
||||
grpc::ClientContext context;
|
||||
|
||||
grpc::Status status = stub_->Click(&context, request, &response);
|
||||
|
||||
if (!status.ok()) {
|
||||
return absl::InternalError(
|
||||
absl::StrFormat("Click RPC failed: %s", status.error_message()));
|
||||
}
|
||||
|
||||
AutomationResult result;
|
||||
result.success = response.success();
|
||||
result.message = response.message();
|
||||
result.execution_time = std::chrono::milliseconds(
|
||||
std::stoll(response.execution_time_ms()));
|
||||
return result;
|
||||
#else
|
||||
return absl::UnimplementedError("gRPC not available");
|
||||
#endif
|
||||
}
|
||||
|
||||
absl::StatusOr<AutomationResult> GuiAutomationClient::Type(
|
||||
const std::string& target, const std::string& text, bool clear_first) {
|
||||
#ifdef YAZE_WITH_GRPC
|
||||
if (!stub_) {
|
||||
return absl::FailedPreconditionError("Not connected. Call Connect() first.");
|
||||
}
|
||||
|
||||
yaze::test::TypeRequest request;
|
||||
request.set_target(target);
|
||||
request.set_text(text);
|
||||
request.set_clear_first(clear_first);
|
||||
|
||||
yaze::test::TypeResponse response;
|
||||
grpc::ClientContext context;
|
||||
|
||||
grpc::Status status = stub_->Type(&context, request, &response);
|
||||
|
||||
if (!status.ok()) {
|
||||
return absl::InternalError(
|
||||
absl::StrFormat("Type RPC failed: %s", status.error_message()));
|
||||
}
|
||||
|
||||
AutomationResult result;
|
||||
result.success = response.success();
|
||||
result.message = response.message();
|
||||
result.execution_time = std::chrono::milliseconds(
|
||||
std::stoll(response.execution_time_ms()));
|
||||
return result;
|
||||
#else
|
||||
return absl::UnimplementedError("gRPC not available");
|
||||
#endif
|
||||
}
|
||||
|
||||
absl::StatusOr<AutomationResult> GuiAutomationClient::Wait(
|
||||
const std::string& condition, int timeout_ms, int poll_interval_ms) {
|
||||
#ifdef YAZE_WITH_GRPC
|
||||
if (!stub_) {
|
||||
return absl::FailedPreconditionError("Not connected. Call Connect() first.");
|
||||
}
|
||||
|
||||
yaze::test::WaitRequest request;
|
||||
request.set_condition(condition);
|
||||
request.set_timeout_ms(timeout_ms);
|
||||
request.set_poll_interval_ms(poll_interval_ms);
|
||||
|
||||
yaze::test::WaitResponse response;
|
||||
grpc::ClientContext context;
|
||||
|
||||
grpc::Status status = stub_->Wait(&context, request, &response);
|
||||
|
||||
if (!status.ok()) {
|
||||
return absl::InternalError(
|
||||
absl::StrFormat("Wait RPC failed: %s", status.error_message()));
|
||||
}
|
||||
|
||||
AutomationResult result;
|
||||
result.success = response.success();
|
||||
result.message = response.message();
|
||||
result.execution_time = std::chrono::milliseconds(
|
||||
std::stoll(response.elapsed_ms()));
|
||||
return result;
|
||||
#else
|
||||
return absl::UnimplementedError("gRPC not available");
|
||||
#endif
|
||||
}
|
||||
|
||||
absl::StatusOr<AutomationResult> GuiAutomationClient::Assert(
|
||||
const std::string& condition) {
|
||||
#ifdef YAZE_WITH_GRPC
|
||||
if (!stub_) {
|
||||
return absl::FailedPreconditionError("Not connected. Call Connect() first.");
|
||||
}
|
||||
|
||||
yaze::test::AssertRequest request;
|
||||
request.set_condition(condition);
|
||||
|
||||
yaze::test::AssertResponse response;
|
||||
grpc::ClientContext context;
|
||||
|
||||
grpc::Status status = stub_->Assert(&context, request, &response);
|
||||
|
||||
if (!status.ok()) {
|
||||
return absl::InternalError(
|
||||
absl::StrFormat("Assert RPC failed: %s", status.error_message()));
|
||||
}
|
||||
|
||||
AutomationResult result;
|
||||
result.success = response.success();
|
||||
result.message = response.message();
|
||||
result.actual_value = response.actual_value();
|
||||
result.expected_value = response.expected_value();
|
||||
result.execution_time = std::chrono::milliseconds(0);
|
||||
return result;
|
||||
#else
|
||||
return absl::UnimplementedError("gRPC not available");
|
||||
#endif
|
||||
}
|
||||
|
||||
absl::StatusOr<AutomationResult> GuiAutomationClient::Screenshot(
|
||||
const std::string& region, const std::string& format) {
|
||||
#ifdef YAZE_WITH_GRPC
|
||||
if (!stub_) {
|
||||
return absl::FailedPreconditionError("Not connected. Call Connect() first.");
|
||||
}
|
||||
|
||||
yaze::test::ScreenshotRequest request;
|
||||
request.set_region(region);
|
||||
request.set_format(format);
|
||||
|
||||
yaze::test::ScreenshotResponse response;
|
||||
grpc::ClientContext context;
|
||||
|
||||
grpc::Status status = stub_->Screenshot(&context, request, &response);
|
||||
|
||||
if (!status.ok()) {
|
||||
return absl::InternalError(
|
||||
absl::StrFormat("Screenshot RPC failed: %s", status.error_message()));
|
||||
}
|
||||
|
||||
AutomationResult result;
|
||||
result.success = response.success();
|
||||
result.message = response.message();
|
||||
result.execution_time = std::chrono::milliseconds(0);
|
||||
return result;
|
||||
#else
|
||||
return absl::UnimplementedError("gRPC not available");
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace cli
|
||||
} // namespace yaze
|
||||
Reference in New Issue
Block a user