Modules/LibraryViewer/tests/LibraryViewerAppTests.cpp
Шурупов Илья Викторович e23a95d03f Give every widget app a Debug menu with session recording
Move Sketch2D's session recorder into the framework: SessionRecorder in
GraphicsInput, SessionRecordingControl (REC badge, save and discard
dialogs) in Widgets, and DebugTools in WidgetsApp. Every
WidgetApplication attaches a trailing Debug menu with Debug UI, Record
session and Stop recording and save unless it opts out. Sessions save
environment, raw input and per-frame profiles; apps add their own
state through a snapshot hook, which Sketch2D uses for its document.
2026-10-24 17:46:43 +03:00

125 lines
4.6 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(4u, application.menuBar().titles().size());
CHECK_EQUAL("Debug", application.menuBar().titles().back()->text());
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);
}
}