Files
Tushar Rohilla e5295f36aa Add Afterglow 1.0.6, a PS5 internet radio and music visualizer
222 stations in 17 genres, 56 visualizer scenes, Ogg and Opus playback, station logos, crossfade, My Music from USB, a command box and live BPM sync.
2026-10-16 00:59:10 -04:00

182 lines
6.1 KiB
C++

// ps5-homebrew-ui - Behaviour tests for the Paper Library design.
// Copyright (C) 2026 BlackBearReloaded
// SPDX-License-Identifier: GPL-3.0-or-later
//
// The library's rules, pinned down through the sounds it asks for: the grid
// has edges, filters can be empty, the dialog owns the input while it is up,
// and a title can be finished only once.
#include "concept_fixture.hpp"
#include "concepts/concepts.hpp"
namespace
{
using hui::Action;
using hui::Direction;
using hui::audio::Cue;
class Paper : public hui::testing::ConceptFixture
{
protected:
// The pan of the first cue of the last send(), to tell left from right.
float first_pan() const
{
return last_cues_.empty() ? 0.0f : last_cues_.front().pan;
}
std::unique_ptr<hui::app::Concept> design_ = hui::concepts::make_paper(context_);
};
TEST_F(Paper, TheGridHasFourEdgesThatRefuseSoftly)
{
// The focus starts on the first card: left and up are the edge.
send(*design_, nav(Direction::left));
EXPECT_TRUE(played(Cue::error));
send(*design_, nav(Direction::up));
EXPECT_TRUE(played(Cue::error));
// Six cards across: five steps right, then the edge.
for (int i = 0; i < 5; ++i)
{
send(*design_, nav(Direction::right));
EXPECT_TRUE(played(Cue::focus)) << i;
EXPECT_FALSE(played(Cue::error)) << i;
}
EXPECT_GT(first_pan(), 0.0f); // the tick followed the card to the right
send(*design_, nav(Direction::right));
EXPECT_TRUE(played(Cue::error));
// Twenty-four titles are four rows: three steps down, then the edge.
for (int i = 0; i < 3; ++i)
{
send(*design_, nav(Direction::down));
EXPECT_TRUE(played(Cue::focus)) << i;
}
send(*design_, nav(Direction::down));
EXPECT_TRUE(played(Cue::error));
expect_drawable(*design_);
}
TEST_F(Paper, AHeldDirectionAtAnEdgeStaysQuiet)
{
hui::InputFrame held = nav(Direction::left);
held.nav_repeat = true;
send(*design_, held);
EXPECT_TRUE(last_cues_.empty());
}
TEST_F(Paper, AnEmptyFilterIsAPlaceNotABrokenScreen)
{
// Nothing is starred at the start, so Favorites is empty.
send(*design_, press(Action::jump_next));
EXPECT_TRUE(played(Cue::tab));
expect_drawable(*design_);
// There is nothing to open or star; the screen says no, softly.
send(*design_, press(Action::confirm));
EXPECT_TRUE(played(Cue::invalid));
EXPECT_FALSE(played(Cue::modal_open));
send(*design_, press(Action::north));
EXPECT_TRUE(played(Cue::invalid));
send(*design_, nav(Direction::right));
EXPECT_TRUE(played(Cue::error));
// Back on the full shelf everything works again.
send(*design_, press(Action::jump_prev));
EXPECT_TRUE(played(Cue::tab));
send(*design_, press(Action::confirm));
EXPECT_TRUE(played(Cue::modal_open));
}
TEST_F(Paper, TheFiltersWrapAroundInBothDirections)
{
// L2 from "All" lands on the last chip, and R2 comes back.
send(*design_, press(Action::jump_prev));
EXPECT_TRUE(played(Cue::tab));
const float last_chip = first_pan();
send(*design_, press(Action::jump_next));
EXPECT_TRUE(played(Cue::tab));
EXPECT_LT(first_pan(), last_chip); // "All" is the leftmost chip
expect_drawable(*design_);
}
TEST_F(Paper, UnstarringOnTheFavoritesShelfRemovesTheCard)
{
send(*design_, press(Action::north));
EXPECT_TRUE(played(Cue::favorite_on));
send(*design_, press(Action::jump_next));
// One favourite: it is alone on the shelf, so every direction is an edge.
send(*design_, nav(Direction::right));
EXPECT_TRUE(played(Cue::error));
send(*design_, press(Action::north));
EXPECT_TRUE(played(Cue::favorite_off));
// The shelf is empty again: there is no card left to open.
send(*design_, press(Action::confirm));
EXPECT_TRUE(played(Cue::invalid));
expect_drawable(*design_);
}
TEST_F(Paper, SortingAnswersAndKeepsTheGridUsable)
{
for (int i = 0; i < 3; ++i)
{
send(*design_, press(Action::west), 0.1f); // interrupt the cards mid-flight
EXPECT_TRUE(played(Cue::flip)) << i;
expect_drawable(*design_);
}
send(*design_, nav(Direction::right));
EXPECT_TRUE(played(Cue::focus));
}
TEST_F(Paper, TheDialogTakesTheInputAndATitleFinishesOnce)
{
send(*design_, press(Action::confirm));
EXPECT_TRUE(played(Cue::modal_open));
// The grid does not react while the dialog is up.
send(*design_, press(Action::west));
EXPECT_TRUE(last_cues_.empty());
send(*design_, press(Action::jump_next));
EXPECT_TRUE(last_cues_.empty());
// Play, Favorite, Mark as finished, Close: left of Play is the edge.
send(*design_, nav(Direction::left));
EXPECT_TRUE(played(Cue::error));
send(*design_, nav(Direction::right));
EXPECT_TRUE(played(Cue::focus));
send(*design_, press(Action::confirm));
EXPECT_TRUE(played(Cue::favorite_on));
send(*design_, nav(Direction::right));
send(*design_, press(Action::confirm));
EXPECT_TRUE(played(Cue::complete));
expect_drawable(*design_); // confetti in the air
// Finished is finished: a second press is refused.
send(*design_, press(Action::confirm));
EXPECT_TRUE(played(Cue::invalid));
EXPECT_FALSE(played(Cue::complete));
send(*design_, nav(Direction::right));
send(*design_, nav(Direction::right));
EXPECT_TRUE(played(Cue::error));
send(*design_, press(Action::confirm));
EXPECT_TRUE(played(Cue::modal_close));
// The grid has the input back.
send(*design_, nav(Direction::right));
EXPECT_TRUE(played(Cue::focus));
expect_drawable(*design_);
}
TEST_F(Paper, PlayLaunchesAndBackClosesTheDialog)
{
send(*design_, press(Action::confirm));
send(*design_, press(Action::confirm));
EXPECT_TRUE(played(Cue::launch));
send(*design_, press(Action::west)); // closed: Square sorts again
EXPECT_TRUE(played(Cue::flip));
send(*design_, press(Action::confirm));
EXPECT_TRUE(played(Cue::modal_open));
send(*design_, press(Action::back));
EXPECT_TRUE(played(Cue::back));
// enter() also closes a dialog left open.
send(*design_, press(Action::confirm));
design_->enter();
send(*design_, press(Action::west));
EXPECT_TRUE(played(Cue::flip));
}
} // namespace