Modules/LibraryViewer/tests/LibraryViewerAppTests.cpp
Шурупов Илья Викторович b061d4b600 Stop window resizing from falling behind the compositor
Every configure the compositor sent during an interactive resize ran a
whole frame inside Wayland event dispatch, including one at each stale
intermediate size, so a fast drag queued more frames than the app could
draw and the window lagged further and further behind. Configures and
input now only enter the coalescing queue during dispatch; each frame
applies the newest configure, acknowledges it and delivers the merged
input, and the main loop waits for the compositor's frame callback
before drawing again, so it renders at most once per display refresh.
The synchronous refresh path is removed. Platform input is muted before
the queue while tests inject theirs into it. Leo keeps every pointer
sample so strokes stay smooth; other applications merge motion.
2026-10-15 15:09:46 +03:00

124 lines
4.5 KiB
C++

#include "AppDriver.hpp"
#include "LibraryViewerApplication.hpp"
#include "UnitTest++/UnitTest++.h"
using namespace tp;
using namespace tp::ui;
namespace {
constexpr halnf kPaintBudgetMs = 4;
constexpr int kMeasuredFrames = 30;
constexpr ualni kPaintedWidgetsBudget = 150;
halnf averagePaintMs(LibraryViewerApplication& application, AppDriver& driver) {
halnf total = 0;
for (int frame = 0; frame < kMeasuredFrames; frame++) {
application.ui().resetStats();
application.gui().requestPaint();
driver.frame();
total += (halnf) application.ui().stats().paintNs / 1.0e6f;
}
return total / kMeasuredFrames;
}
}
SUITE(LibraryViewerIntegration) {
TEST(ThousandsOfTracksPaintWithinBudget) {
LibraryViewerApplication application("library/Library.json");
AppDriver driver(application);
CHECK(application.library().mTraks.size() > 2000);
const halnf atTop = averagePaintMs(application, driver);
CHECK(atTop < kPaintBudgetMs);
CHECK(application.ui().stats().paintedWidgets < kPaintedWidgetsBudget);
for (int notch = 0; notch < 200; notch++)
driver.scroll({ 300, 300 }, -3);
driver.advance(500);
const halnf scrolled = averagePaintMs(application, driver);
CHECK(scrolled < kPaintBudgetMs);
printf("library paint: %.3f ms at top, %.3f ms scrolled\n", atTop, scrolled);
}
TEST(ClickingTrackSelectsIt) {
LibraryViewerApplication application("library/Library.json");
AppDriver driver(application);
driver.click(*application.gui().visibleTracks()[0]);
CHECK(application.gui().selectedTrack() != nullptr);
}
}
SUITE(LibraryViewerChrome) {
TEST(DefaultMenuBarCarriesLibraryMenus) {
LibraryViewerApplication application("library/Library.json");
AppDriver driver(application);
CHECK(application.isMenuBarVisible());
CHECK_EQUAL(3u, application.menuBar().titles().size());
CHECK(application.menuBar().windowRect().pos.y < application.gui().windowRect().pos.y + 1);
const RectF player = application.gui().player.windowRect();
CHECK(player.pos.y + player.size.y > application.gui().windowRect().size.y - 4);
}
TEST(SearchingNarrowsTheRealLibrary) {
LibraryViewerApplication application("library/Library.json");
AppDriver driver(application);
const size_t all = application.gui().visibleTracks().size();
driver.click(application.gui().search);
driver.type("jack");
CHECK(application.gui().visibleTracks().size() < all);
CHECK(!application.gui().visibleTracks().empty());
}
}
SUITE(LibraryViewerInputCoalescing) {
TEST(MotionBurstWithinOneFrameIsDeliveredOnce) {
LibraryViewerApplication application("library/Library.json");
AppDriver driver(application);
const CoalescingStatistics before = application.window().inputStatistics();
WindowInputObserver& input = application.window().injectInput();
for (int step = 0; step < 50; step++)
input.onPointerMoved({ 100.0f + (halnf) step, 200.0f });
driver.frame();
const CoalescingStatistics after = application.window().inputStatistics();
CHECK_EQUAL(50u, after.received - before.received);
CHECK_EQUAL(1u, after.delivered - before.delivered);
}
TEST(ClickInsideBurstStillLandsOnItsPosition) {
LibraryViewerApplication application("library/Library.json");
AppDriver driver(application);
const RectF target = application.gui().visibleTracks()[1]->windowRect();
WindowInputObserver& input = application.window().injectInput();
input.onPointerMoved({ 5, 5 });
input.onPointerMoved(target.center());
input.onPointerButton(InputID::MOUSE1, true);
input.onPointerButton(InputID::MOUSE1, false);
input.onPointerMoved({ 5, 5 });
driver.frame();
CHECK(application.gui().selectedTrack() == &application.gui().visibleTracks()[1]->track());
}
}
SUITE(LibraryViewerResize) {
TEST(MaximizeAppliesOneConfigurePerFrameAndRelayouts) {
LibraryViewerApplication application("library/Library.json");
AppDriver driver(application);
const Vec2F before = application.window().getSize();
const CoalescingStatistics start = application.window().inputStatistics();
application.window().toggleMaximize();
for (int frame = 0; frame < 30 && application.window().getSize() == before; frame++)
driver.frame();
const CoalescingStatistics end = application.window().inputStatistics();
CHECK(application.window().getSize().x > before.x || application.window().getSize().y > before.y);
CHECK(end.configuresApplied - start.configuresApplied >= 1);
CHECK(end.configuresApplied - start.configuresApplied <= end.configuresReceived - start.configuresReceived);
CHECK_CLOSE(application.window().getSize().x, application.gui().windowRect().size.x, 1.0f);
}
}