add testing and plan milestones

This commit is contained in:
2026-01-16 19:50:59 +01:00
parent 25b0603913
commit 2e097e4e54
8 changed files with 2540 additions and 28 deletions

View File

@@ -20,6 +20,8 @@
#include "hot_reload.h"
#include "desktop_platform.h"
#include "testing/ui_inspector.h"
#include "testing/action_recorder.h"
#include "testing/action_player.h"
// Command-line options
struct Options {
@@ -31,6 +33,8 @@ struct Options {
std::string output_dir = "dump";
std::string log_file; // If set, write logs to this file
std::string hierarchy_file; // If set, dump UI hierarchy to this file each frame
std::string record_file; // If set, record actions to JSON file
std::string playback_file; // If set, play back actions from JSON file
};
// Global log file stream
@@ -89,6 +93,43 @@ static mosis::IKernel* g_kernel = nullptr;
static std::filesystem::path g_assets_path;
static mosis::testing::UIInspector g_ui_inspector;
// Recording/playback state
static std::unique_ptr<mosis::testing::ActionRecorder> g_recorder;
static std::unique_ptr<mosis::testing::ActionPlayer> g_player;
static std::string g_record_file_path;
// Key callback for F5 (recording control)
bool HandleKeyDown(Rml::Context* context, Rml::Input::KeyIdentifier key, int key_modifier, float native_dp_ratio, bool priority) {
// F5: Toggle recording / Save recording
if (key == Rml::Input::KI_F5 && g_recorder) {
if (g_recorder->IsRecording()) {
g_recorder->StopRecording();
if (g_recorder->SaveToFile(g_record_file_path)) {
LogMessage("Recording saved to: " + g_record_file_path);
} else {
LogMessage("ERROR: Failed to save recording to: " + g_record_file_path);
}
} else {
g_recorder->StartRecording();
LogMessage("Recording started (press F5 to stop and save)");
}
return true; // Consumed
}
// F6: Pause/resume playback
if (key == Rml::Input::KI_F6 && g_player) {
if (g_player->IsPlaying()) {
g_player->Pause();
LogMessage("Playback paused");
} else if (g_player->IsPaused()) {
g_player->Resume();
LogMessage("Playback resumed");
}
return true; // Consumed
}
return false; // Not consumed, let RmlUi handle it
}
int main(int argc, const char* argv[])
{
@@ -206,6 +247,25 @@ int main(int argc, const char* argv[])
// Start kernel
kernel->Start();
// Initialize recording if enabled
if (!opts.record_file.empty()) {
g_recorder = std::make_unique<mosis::testing::ActionRecorder>(opts.width, opts.height);
g_record_file_path = opts.record_file;
LogMessage("Recording mode enabled. Press F5 to start recording.");
}
// Initialize playback if enabled
if (!opts.playback_file.empty()) {
g_player = std::make_unique<mosis::testing::ActionPlayer>(context);
if (g_player->LoadFromFile(opts.playback_file)) {
LogMessage("Loaded playback file: " + opts.playback_file);
g_player->Start();
} else {
LogMessage("ERROR: Failed to load playback file: " + opts.playback_file);
g_player.reset();
}
}
// Setup hot-reload
std::unique_ptr<mosis::desktop::HotReload> hot_reload;
if (!opts.dump_mode) {
@@ -228,8 +288,16 @@ int main(int argc, const char* argv[])
hot_reload->CheckForChanges();
}
// Process events and update
running = Backend::ProcessEvents(context);
// Process events and update (with key callback for F5/F6 control)
running = Backend::ProcessEvents(context, HandleKeyDown);
// Update playback if active
if (g_player && g_player->IsPlaying()) {
g_player->Update();
if (g_player->IsFinished()) {
LogMessage("Playback complete");
}
}
// Update kernel (processes tasks, updates time, etc.)
kernel->Update();
@@ -249,6 +317,16 @@ int main(int argc, const char* argv[])
}
// Cleanup
// Stop and save recording if still active
if (g_recorder && g_recorder->IsRecording()) {
g_recorder->StopRecording();
if (g_recorder->SaveToFile(g_record_file_path)) {
LogMessage("Recording saved on exit to: " + g_record_file_path);
}
}
g_recorder.reset();
g_player.reset();
kernel->Stop();
kernel.reset();
g_kernel = nullptr;
@@ -282,11 +360,19 @@ void PrintUsage(const char* program)
std::cout << " --output DIR Output directory for dump mode (default: dump)" << std::endl;
std::cout << " --log FILE Write log output to file (for automated testing)" << std::endl;
std::cout << " --hierarchy FILE Continuously dump UI hierarchy to JSON file" << std::endl;
std::cout << " --record FILE Record actions to JSON file (F5 to start/stop)" << std::endl;
std::cout << " --playback FILE Play back recorded actions from JSON file" << std::endl;
std::cout << std::endl;
std::cout << "Recording Controls:" << std::endl;
std::cout << " F5 Start/stop recording (when --record is enabled)" << std::endl;
std::cout << " F6 Pause/resume playback (when --playback is enabled)" << std::endl;
std::cout << std::endl;
std::cout << "Examples:" << std::endl;
std::cout << " " << program << " assets/apps/home/home.rml" << std::endl;
std::cout << " " << program << " assets/apps/home/home.rml --resolution 720x1280" << std::endl;
std::cout << " " << program << " assets/apps/home/home.rml --dump" << std::endl;
std::cout << " " << program << " assets/apps/home/home.rml --record test.json" << std::endl;
std::cout << " " << program << " assets/apps/home/home.rml --playback test.json" << std::endl;
}
Options ParseOptions(int argc, const char* argv[])
@@ -313,6 +399,10 @@ Options ParseOptions(int argc, const char* argv[])
opts.log_file = argv[++i];
} else if (arg == "--hierarchy" && i + 1 < argc) {
opts.hierarchy_file = argv[++i];
} else if (arg == "--record" && i + 1 < argc) {
opts.record_file = argv[++i];
} else if (arg == "--playback" && i + 1 < argc) {
opts.playback_file = argv[++i];
} else if (arg[0] != '-') {
opts.document_path = arg;
}