test: login_flow waits a fixed frame count between mouse move and click

The 0.1 s wall-clock waits could fit a single frame under ctest -j3, so the
shopkeeper pick missed about one run in four.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-09-28 14:17:26 +09:00
co-authored by Claude Opus 5.5
parent 3e270dccc1
commit d3fd5f8613
+21 -10
View File
@@ -162,6 +162,17 @@ static bool pump_until(double seconds, const std::function<bool()>& done)
return done(); return done();
} }
// Runs a fixed number of frames: the wait between a mouse move and its click must not depend on how
// many frames a loaded machine fits into a wall-clock slice (ctest -j).
static void pump_frames(int count)
{
for (int i = 0; i < count && PythonBoot::IsAppLooping(); ++i)
{
PythonBoot::UIUpdate();
std::this_thread::sleep_for(std::chrono::milliseconds(16));
}
}
static void dump(FakeLoginServer& server) static void dump(FakeLoginServer& server)
{ {
std::fprintf(stderr, "server events:"); std::fprintf(stderr, "server events:");
@@ -617,7 +628,7 @@ int main(int argc, char** argv)
// Holding ALT (game.GameWindow.ShowName) makes OnRender show every tail in range and project it after // Holding ALT (game.GameWindow.ShowName) makes OnRender show every tail in range and project it after
// app.RenderGame; the item tail keeps that screen rect, which is what textTail.Pick tests. // app.RenderGame; the item tail keeps that screen rect, which is what textTail.Pick tests.
PythonBoot::UIKey(DIK_LMENU, true); PythonBoot::UIKey(DIK_LMENU, true);
pump_until(0.1, [] { return false; }); pump_frames(6);
PythonBoot::UIKey(DIK_LMENU, false); PythonBoot::UIKey(DIK_LMENU, false);
int picked = -1, pick_x = 0, pick_y = 0; int picked = -1, pick_x = 0, pick_y = 0;
for (int y = 0; y < 1200 && picked < 0; y += 2) for (int y = 0; y < 1200 && picked < 0; y += 2)
@@ -677,7 +688,7 @@ int main(int argc, char** argv)
if (main_instance) if (main_instance)
main_instance->NEW_GetPixelPosition(&start); main_instance->NEW_GetPixelPosition(&start);
PythonBoot::UIMouseButton(1, true, 400, 220); PythonBoot::UIMouseButton(1, true, 400, 220);
pump_until(0.1, [] { return false; }); pump_frames(6);
PythonBoot::UIMouseButton(1, false, 400, 220); PythonBoot::UIMouseButton(1, false, 400, 220);
CHECK(main_instance && pump_until(3, [&] { return walked() > 100.0f; })); CHECK(main_instance && pump_until(3, [&] { return walked() > 100.0f; }));
CHECK(main_instance && pump_until(10, [&] { return !main_instance->IsWalking(); })); CHECK(main_instance && pump_until(10, [&] { return !main_instance->IsWalking(); }));
@@ -728,7 +739,7 @@ int main(int argc, char** argv)
auto [sx, sy] = dog_on_screen(); auto [sx, sy] = dog_on_screen();
std::printf("port_login_flow_test: stray dog on screen at %d %d\n", sx, sy); std::printf("port_login_flow_test: stray dog on screen at %d %d\n", sx, sy);
PythonBoot::UIMouseMove(sx, sy); PythonBoot::UIMouseMove(sx, sy);
pump_until(0.1, [] { return false; }); pump_frames(6);
// game.GameWindow.OnMouseLeftButtonDown → player.SetMouseState(MBT_LEFT, MBS_PRESS): MBF_SMART on an // game.GameWindow.OnMouseLeftButtonDown → player.SetMouseState(MBT_LEFT, MBS_PRESS): MBF_SMART on an
// actor is __OnPressActor, which walks into range (__ReserveClickActor) and attacks while the button is // actor is __OnPressActor, which walks into range (__ReserveClickActor) and attacks while the button is
// held; the cursor follows the dog as the camera follows the actor, as a player's hand would. // held; the cursor follows the dog as the camera follows the actor, as a player's hand would.
@@ -761,14 +772,14 @@ int main(int argc, char** argv)
// Zoom IN: positive wheel delta // Zoom IN: positive wheel delta
PythonBoot::UIMouseWheel(120); PythonBoot::UIMouseWheel(120);
pump_until(0.1, [] { return false; }); pump_frames(6);
const float zoomed_in_dist = pkCmrCur->GetDistance(); const float zoomed_in_dist = pkCmrCur->GetDistance();
std::printf("port_login_flow_test: zoomed in camera distance = %f\n", zoomed_in_dist); std::printf("port_login_flow_test: zoomed in camera distance = %f\n", zoomed_in_dist);
CHECK(zoomed_in_dist < initial_dist); CHECK(zoomed_in_dist < initial_dist);
// Zoom OUT: negative wheel delta // Zoom OUT: negative wheel delta
PythonBoot::UIMouseWheel(-240); PythonBoot::UIMouseWheel(-240);
pump_until(0.1, [] { return false; }); pump_frames(6);
const float zoomed_out_dist = pkCmrCur->GetDistance(); const float zoomed_out_dist = pkCmrCur->GetDistance();
std::printf("port_login_flow_test: zoomed out camera distance = %f\n", zoomed_out_dist); std::printf("port_login_flow_test: zoomed out camera distance = %f\n", zoomed_out_dist);
CHECK(zoomed_out_dist > zoomed_in_dist); CHECK(zoomed_out_dist > zoomed_in_dist);
@@ -796,7 +807,7 @@ int main(int argc, char** argv)
bool has_effects = false; bool has_effects = false;
for (int step = 0; step < 10; ++step) for (int step = 0; step < 10; ++step)
{ {
pump_until(0.1, [] { return false; }); pump_frames(6);
if (CEffectInstance::GetRenderingEffectCount() > 0) if (CEffectInstance::GetRenderingEffectCount() > 0)
has_effects = true; has_effects = true;
} }
@@ -833,9 +844,9 @@ int main(int argc, char** argv)
auto [kx, ky] = keeper_on_screen(); auto [kx, ky] = keeper_on_screen();
std::printf("port_login_flow_test: shopkeeper on screen at %d %d\n", kx, ky); std::printf("port_login_flow_test: shopkeeper on screen at %d %d\n", kx, ky);
PythonBoot::UIMouseMove(kx, ky); PythonBoot::UIMouseMove(kx, ky);
pump_until(0.1, [] { return false; }); pump_frames(6);
PythonBoot::UIMouseButton(1, true, kx, ky); PythonBoot::UIMouseButton(1, true, kx, ky);
pump_until(0.1, [] { return false; }); pump_frames(6);
PythonBoot::UIMouseButton(1, false, kx, ky); PythonBoot::UIMouseButton(1, false, kx, ky);
CHECK(pump_until(5, [&] { return server->Has("game2:click " + std::to_string(FakeLoginServer::kShopVID)); })); CHECK(pump_until(5, [&] { return server->Has("game2:click " + std::to_string(FakeLoginServer::kShopVID)); }));
CHECK(pump_until(2, [] { return py_true("__import__('shop').IsOpen() and _stream.curPhaseWindow.interface.dlgShop.IsShow()"); })); CHECK(pump_until(2, [] { return py_true("__import__('shop').IsOpen() and _stream.curPhaseWindow.interface.dlgShop.IsShow()"); }));
@@ -1009,9 +1020,9 @@ int main(int argc, char** argv)
auto [sx, sy] = storekeeper_on_screen(); auto [sx, sy] = storekeeper_on_screen();
std::printf("port_login_flow_test: storekeeper on screen at %d %d\n", sx, sy); std::printf("port_login_flow_test: storekeeper on screen at %d %d\n", sx, sy);
PythonBoot::UIMouseMove(sx, sy); PythonBoot::UIMouseMove(sx, sy);
pump_until(0.1, [] { return false; }); pump_frames(6);
PythonBoot::UIMouseButton(1, true, sx, sy); PythonBoot::UIMouseButton(1, true, sx, sy);
pump_until(0.1, [] { return false; }); pump_frames(6);
PythonBoot::UIMouseButton(1, false, sx, sy); PythonBoot::UIMouseButton(1, false, sx, sy);
CHECK(pump_until(5, [&] { return server->Has("game2:click " + std::to_string(FakeLoginServer::kSafeboxVID)); })); CHECK(pump_until(5, [&] { return server->Has("game2:click " + std::to_string(FakeLoginServer::kSafeboxVID)); }));
CHECK(pump_until(2, [] { CHECK(pump_until(2, [] {