Centralize legacy app startup

This commit is contained in:
2026-06-04 14:32:39 +02:00
parent 884a6d4940
commit 2bd1b12ade
12 changed files with 521 additions and 13 deletions

View File

@@ -488,6 +488,16 @@ add_test(NAME pp_app_core_app_status_tests COMMAND pp_app_core_app_status_tests)
set_tests_properties(pp_app_core_app_status_tests PROPERTIES
LABELS "app;desktop-fast;fuzz")
add_executable(pp_app_core_app_startup_tests
app_core/app_startup_tests.cpp)
target_link_libraries(pp_app_core_app_startup_tests PRIVATE
pp_app_core
pp_test_harness)
add_test(NAME pp_app_core_app_startup_tests COMMAND pp_app_core_app_startup_tests)
set_tests_properties(pp_app_core_app_startup_tests PROPERTIES
LABELS "app;desktop-fast;fuzz")
add_executable(pp_app_core_document_sharing_tests
app_core/document_sharing_tests.cpp)
target_link_libraries(pp_app_core_document_sharing_tests PRIVATE
@@ -851,6 +861,22 @@ if(TARGET pano_cli)
LABELS "app;integration;desktop-fast;fuzz"
PASS_REGULAR_EXPRESSION "\"command\":\"plan-app-preferences\".*\"scaleSelection\":\\{\"hasSelection\":false,\"index\":0\\}.*\"direction\":\"left-to-right\".*\"timelapse\":\\{\"enabled\":false,\"recordingAction\":\"stop-recording\"\\}.*\"vrControllers\":\\{\"enabled\":false\\}")
add_test(NAME pano_cli_plan_app_startup_smoke
COMMAND pano_cli plan-app-startup
--run-counter 7
--vr-controllers-disabled
--license-invalid)
set_tests_properties(pano_cli_plan_app_startup_smoke PROPERTIES
LABELS "app;integration;desktop-fast"
PASS_REGULAR_EXPRESSION "\"command\":\"plan-app-startup\".*\"runCounter\":7.*\"licenseValid\":false.*\"previousRunCounter\":7.*\"nextRunCounter\":8.*\"savePreferences\":true.*\"startTimelapse\":true.*\"vrControllersEnabled\":false.*\"showLicenseWarning\":true")
add_test(NAME pano_cli_plan_app_startup_rejects_negative_counter
COMMAND pano_cli plan-app-startup --run-counter -1)
set_tests_properties(pano_cli_plan_app_startup_rejects_negative_counter PROPERTIES
LABELS "app;integration;desktop-fast;fuzz"
WILL_FAIL TRUE
PASS_REGULAR_EXPRESSION "\"command\":\"plan-app-startup\".*\"message\":\"run counter must not be negative\"")
add_test(NAME pano_cli_plan_tools_menu_shortcuts_smoke
COMMAND pano_cli plan-tools-menu --command shortcuts)
set_tests_properties(pano_cli_plan_tools_menu_shortcuts_smoke PROPERTIES

View File

@@ -0,0 +1,164 @@
#include "app_core/app_startup.h"
#include "test_harness.h"
#include <limits>
#include <string>
namespace {
class FakeAppStartupServices final : public pp::app::AppStartupServices {
public:
void store_run_counter(int value) override
{
stored_run_counter = value;
call_order += "store-counter;";
}
void save_preferences() override
{
save_calls += 1;
call_order += "save;";
}
void start_timelapse_recording() override
{
timelapse_starts += 1;
call_order += "timelapse;";
}
void apply_vr_controllers_enabled(bool enabled) override
{
vr_controllers_enabled = enabled;
call_order += "vr-controllers;";
}
void show_license_warning() override
{
license_warnings += 1;
call_order += "license;";
}
int stored_run_counter = 0;
int save_calls = 0;
int timelapse_starts = 0;
bool vr_controllers_enabled = false;
int license_warnings = 0;
std::string call_order;
};
void startup_plan_increments_counter_and_enables_requested_side_effects(pp::tests::Harness& harness)
{
const auto plan = pp::app::plan_app_startup(7, true, false, false);
PP_EXPECT(harness, plan);
PP_EXPECT(harness, plan.value().previous_run_counter == 7);
PP_EXPECT(harness, plan.value().next_run_counter == 8);
PP_EXPECT(harness, plan.value().save_preferences);
PP_EXPECT(harness, plan.value().start_timelapse);
PP_EXPECT(harness, !plan.value().vr_controllers_enabled);
PP_EXPECT(harness, plan.value().show_license_warning);
}
void startup_plan_preserves_disabled_optional_work(pp::tests::Harness& harness)
{
const auto plan = pp::app::plan_app_startup(0, false, true, true);
PP_EXPECT(harness, plan);
PP_EXPECT(harness, plan.value().next_run_counter == 1);
PP_EXPECT(harness, !plan.value().start_timelapse);
PP_EXPECT(harness, plan.value().vr_controllers_enabled);
PP_EXPECT(harness, !plan.value().show_license_warning);
}
void startup_plan_rejects_invalid_counters(pp::tests::Harness& harness)
{
const auto negative = pp::app::plan_app_startup(-1, true, true, true);
const auto overflow = pp::app::plan_app_startup(std::numeric_limits<int>::max(), true, true, true);
PP_EXPECT(harness, !negative);
PP_EXPECT(harness, negative.status().code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(harness, !overflow);
PP_EXPECT(harness, overflow.status().code == pp::foundation::StatusCode::out_of_range);
}
void startup_executor_dispatches_in_stable_order(pp::tests::Harness& harness)
{
FakeAppStartupServices services;
const auto plan = pp::app::plan_app_startup(2, true, true, false);
PP_EXPECT(harness, plan);
PP_EXPECT(harness, pp::app::execute_app_startup_plan(plan.value(), services).ok());
PP_EXPECT(harness, services.stored_run_counter == 3);
PP_EXPECT(harness, services.save_calls == 1);
PP_EXPECT(harness, services.timelapse_starts == 1);
PP_EXPECT(harness, services.vr_controllers_enabled);
PP_EXPECT(harness, services.license_warnings == 1);
PP_EXPECT(harness, services.call_order == "store-counter;save;timelapse;vr-controllers;license;");
}
void startup_executor_preserves_no_op_side_effects(pp::tests::Harness& harness)
{
FakeAppStartupServices services;
auto plan = pp::app::plan_app_startup(4, false, false, true).value();
PP_EXPECT(harness, pp::app::execute_app_startup_plan(plan, services).ok());
PP_EXPECT(harness, services.stored_run_counter == 5);
PP_EXPECT(harness, services.save_calls == 1);
PP_EXPECT(harness, services.timelapse_starts == 0);
PP_EXPECT(harness, !services.vr_controllers_enabled);
PP_EXPECT(harness, services.license_warnings == 0);
PP_EXPECT(harness, services.call_order == "store-counter;save;vr-controllers;");
}
void startup_split_executors_keep_persistence_and_runtime_separate(pp::tests::Harness& harness)
{
FakeAppStartupServices persistence_services;
FakeAppStartupServices runtime_services;
const auto plan = pp::app::plan_app_startup(5, true, false, false);
PP_EXPECT(harness, plan);
PP_EXPECT(harness, pp::app::execute_app_startup_persistence_plan(plan.value(), persistence_services).ok());
PP_EXPECT(harness, persistence_services.stored_run_counter == 6);
PP_EXPECT(harness, persistence_services.save_calls == 1);
PP_EXPECT(harness, persistence_services.timelapse_starts == 0);
PP_EXPECT(harness, persistence_services.license_warnings == 0);
PP_EXPECT(harness, persistence_services.call_order == "store-counter;save;");
PP_EXPECT(harness, pp::app::execute_app_startup_runtime_plan(plan.value(), runtime_services).ok());
PP_EXPECT(harness, runtime_services.stored_run_counter == 0);
PP_EXPECT(harness, runtime_services.save_calls == 0);
PP_EXPECT(harness, runtime_services.timelapse_starts == 1);
PP_EXPECT(harness, !runtime_services.vr_controllers_enabled);
PP_EXPECT(harness, runtime_services.license_warnings == 1);
PP_EXPECT(harness, runtime_services.call_order == "timelapse;vr-controllers;license;");
}
void startup_executor_rejects_malformed_counter_state(pp::tests::Harness& harness)
{
FakeAppStartupServices services;
pp::app::AppStartupPlan plan;
plan.previous_run_counter = 3;
plan.next_run_counter = 3;
PP_EXPECT(harness, !pp::app::execute_app_startup_plan(plan, services).ok());
PP_EXPECT(harness, services.call_order.empty());
}
}
int main()
{
pp::tests::Harness harness;
harness.run(
"startup plan increments counter and enables requested side effects",
startup_plan_increments_counter_and_enables_requested_side_effects);
harness.run("startup plan preserves disabled optional work", startup_plan_preserves_disabled_optional_work);
harness.run("startup plan rejects invalid counters", startup_plan_rejects_invalid_counters);
harness.run("startup executor dispatches in stable order", startup_executor_dispatches_in_stable_order);
harness.run("startup executor preserves no-op side effects", startup_executor_preserves_no_op_side_effects);
harness.run(
"startup split executors keep persistence and runtime separate",
startup_split_executors_keep_persistence_and_runtime_separate);
harness.run("startup executor rejects malformed counter state", startup_executor_rejects_malformed_counter_state);
return harness.finish();
}