yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
test_recorder.cc
Go to the documentation of this file.
2
3#include <utility>
4
5#include "absl/strings/ascii.h"
6#include "absl/strings/str_cat.h"
7#include "absl/strings/str_format.h"
8#include "absl/time/clock.h"
9#include "absl/time/time.h"
12
13namespace yaze {
14namespace test {
15namespace {
16
17constexpr absl::Duration kTestCompletionTimeout = absl::Seconds(10);
18constexpr absl::Duration kPollInterval = absl::Milliseconds(50);
19
20#if defined(YAZE_WITH_GRPC)
21const char* HarnessStatusToString(test::HarnessTestStatus status) {
22 switch (status) {
23 case HarnessTestStatus::kQueued:
24 return "queued";
25 case HarnessTestStatus::kRunning:
26 return "running";
27 case HarnessTestStatus::kPassed:
28 return "passed";
29 case HarnessTestStatus::kFailed:
30 return "failed";
31 case HarnessTestStatus::kTimeout:
32 return "timeout";
33 case HarnessTestStatus::kUnspecified:
34 default:
35 return "unspecified";
36 }
37}
38#endif // defined(YAZE_WITH_GRPC)
39
40} // namespace
41
43 bool active)
44 : recorder_(recorder), active_(active) {}
45
47 if (!recorder_ || !active_) {
48 return;
49 }
50 absl::MutexLock lock(&recorder_->mu_);
51 recorder_->suspended_ = false;
52}
53
55 : test_manager_(test_manager) {}
56
57absl::StatusOr<std::string> TestRecorder::Start(
58 const RecordingOptions& options) {
59 absl::MutexLock lock(&mu_);
60 return StartLocked(options);
61}
62
63absl::StatusOr<TestRecorder::StopRecordingSummary> TestRecorder::Stop(
64 const std::string& recording_id, bool discard) {
65 absl::MutexLock lock(&mu_);
66 return StopLocked(recording_id, discard);
67}
68
70 absl::MutexLock lock(&mu_);
71 RecordStepLocked(step);
72}
73
75 absl::MutexLock lock(&mu_);
76 return recording_ && !suspended_;
77}
78
80 absl::MutexLock lock(&mu_);
81 return recording_id_;
82}
83
85 absl::MutexLock lock(&mu_);
86 bool activate = false;
87 if (!suspended_) {
88 suspended_ = true;
89 activate = true;
90 }
91 return ScopedSuspension(this, activate);
92}
93
94absl::StatusOr<std::string> TestRecorder::StartLocked(
95 const RecordingOptions& options) {
96 if (recording_) {
97 return absl::FailedPreconditionError(
98 "A recording session is already active");
99 }
100 if (!test_manager_) {
101 return absl::FailedPreconditionError("TestManager unavailable");
102 }
103 if (options.output_path.empty()) {
104 return absl::InvalidArgumentError(
105 "Recording requires a non-empty output path");
106 }
107
108 recording_ = true;
109 suspended_ = false;
110 options_ = options;
111 if (options_.session_name.empty()) {
112 options_.session_name = "Untitled Recording";
113 }
114 started_at_ = absl::Now();
115 steps_.clear();
116 recording_id_ = GenerateRecordingId();
117 return recording_id_;
118}
119
120absl::StatusOr<TestRecorder::StopRecordingSummary> TestRecorder::StopLocked(
121 const std::string& recording_id, bool discard) {
122 if (!recording_) {
123 return absl::FailedPreconditionError("No active recording session");
124 }
125 if (!recording_id.empty() && recording_id != recording_id_) {
126 return absl::InvalidArgumentError(
127 absl::StrFormat("Recording ID mismatch (expected %s)", recording_id_));
128 }
129
130 StopRecordingSummary summary;
131 summary.step_count = static_cast<int>(steps_.size());
132 summary.duration = absl::Now() - started_at_;
133 summary.output_path = options_.output_path;
134 summary.saved = !discard;
135
136 if (!discard) {
138 TestScript script;
139 script.recording_id = recording_id_;
140 script.name = options_.session_name;
141 script.description = options_.description;
142 script.created_at = started_at_;
143 script.duration = summary.duration;
144
145 for (const auto& step : steps_) {
146 TestScriptStep script_step;
147 script_step.action = ActionTypeToString(step.type);
148 script_step.target = step.target;
149 script_step.click_type = absl::AsciiStrToLower(step.click_type);
150 script_step.text = step.text;
151 script_step.clear_first = step.clear_first;
152 script_step.condition = step.condition;
153 script_step.timeout_ms = step.timeout_ms;
154 script_step.region = step.region;
155 script_step.format = step.format;
156 script_step.expect_success = step.success;
157#if defined(YAZE_WITH_GRPC)
158 script_step.expect_status = ::yaze::test::HarnessStatusToString(step.final_status);
159#else
160 script_step.expect_status.clear();
161#endif
162 if (!step.final_error_message.empty()) {
163 script_step.expect_message = step.final_error_message;
164 } else {
165 script_step.expect_message = step.message;
166 }
167 script_step.expect_assertion_failures = step.assertion_failures;
168 script_step.expect_metrics = step.metrics;
169 script.steps.push_back(std::move(script_step));
170 }
171
173 TestScriptParser::WriteToFile(script, options_.output_path));
174 }
175
176 // Reset state
177 recording_ = false;
178 suspended_ = false;
179 recording_id_.clear();
180 options_ = RecordingOptions{};
181 started_at_ = absl::InfinitePast();
182 steps_.clear();
183
184 return summary;
185}
186
188 if (!recording_ || suspended_) {
189 return;
190 }
191 RecordedStep copy = step;
192 if (copy.captured_at == absl::InfinitePast()) {
193 copy.captured_at = absl::Now();
194 }
195 steps_.push_back(std::move(copy));
196}
197
199 if (!test_manager_) {
200 return absl::FailedPreconditionError("TestManager unavailable");
201 }
202
203#if !defined(YAZE_WITH_GRPC)
204 return absl::OkStatus();
205#else
206 for (auto& step : steps_) {
207 if (step.test_id.empty()) {
208 continue;
209 }
210
211 const absl::Time deadline = absl::Now() + kTestCompletionTimeout;
212 while (absl::Now() < deadline) {
213 absl::StatusOr<test::HarnessTestExecution> execution =
214 test_manager_->GetHarnessTestExecution(step.test_id);
215 if (!execution.ok()) {
216 absl::SleepFor(kPollInterval);
217 continue;
218 }
219
220 step.final_status = execution->status;
221 step.final_error_message = execution->error_message;
222 step.assertion_failures = execution->assertion_failures;
223 step.metrics = execution->metrics;
224
225 if (execution->status == test::HarnessTestStatus::kQueued ||
226 execution->status == test::HarnessTestStatus::kRunning) {
227 absl::SleepFor(kPollInterval);
228 continue;
229 }
230 break;
231 }
232 }
233
234 return absl::OkStatus();
235#endif // defined(YAZE_WITH_GRPC)
236}
237
239 return absl::StrFormat(
240 "rec_%s", absl::FormatTime("%Y%m%dT%H%M%S", absl::Now(),
241 absl::UTCTimeZone()));
242}
243
245 switch (type) {
247 return "click";
249 return "type";
251 return "wait";
253 return "assert";
255 return "screenshot";
257 default:
258 return "unknown";
259 }
260}
261
262} // namespace test
263} // namespace yaze
ScopedSuspension(TestRecorder *recorder, bool active)
void RecordStepLocked(const RecordedStep &step) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_)
static std::string GenerateRecordingId()
absl::StatusOr< StopRecordingSummary > StopLocked(const std::string &recording_id, bool discard) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_)
absl::Status PopulateFinalStatusLocked() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_)
TestManager *const test_manager_
TestRecorder(TestManager *test_manager)
absl::StatusOr< std::string > Start(const RecordingOptions &options)
absl::StatusOr< StopRecordingSummary > Stop(const std::string &recording_id, bool discard)
ScopedSuspension Suspend()
static const char * ActionTypeToString(ActionType type)
absl::StatusOr< std::string > StartLocked(const RecordingOptions &options) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_)
void RecordStep(const RecordedStep &step)
std::string CurrentRecordingId() const
static absl::Status WriteToFile(const TestScript &script, const std::string &path)
#define RETURN_IF_ERROR(expression)
Definition macro.h:53
Main namespace for the application.
Definition controller.cc:20
std::vector< std::string > expect_assertion_failures
std::map< std::string, int32_t > expect_metrics
std::vector< TestScriptStep > steps