Files
AnyPS5/core/libs/tests/GuestImeModalDialog.cpp
T
MarcZX10 f49c380cca test(libSceIme): cover the bound on a dialog that is never drawn
guest_ime_modal_dialog opens a dialog with a 40 ms bound and never reads its
view: it captures input until the bound, then becomes Unshown, stops capturing,
ignores answers and lets the next dialog open. A dialog whose view was read
keeps capturing and can be answered long after the bound.
2026-10-29 14:06:17 +00:00

162 lines
8.2 KiB
C++

#include "prx/libSceIme/include/ModalDialog.hpp"
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <thread>
namespace {
constexpr std::uint32_t Left = 0x0080;
constexpr std::uint32_t Right = 0x0020;
constexpr std::uint32_t Circle = 0x2000;
constexpr std::uint32_t Cross = 0x4000;
void Require(bool value, const char* what) {
if (value) return;
std::fprintf(stderr, "FAIL %s\n", what);
std::exit(1);
}
void IgnoreText(ImeHostInputKind, const char16_t*, std::size_t) {}
ModalDialogRequest YesNo(std::size_t focus, std::size_t cancel) {
ModalDialogRequest request;
request.message = u"Overwrite?";
request.buttons = {u"Yes", u"No"};
request.focus = focus;
request.cancel = cancel;
return request;
}
ImeHostPad Pad(std::uint32_t buttons, std::uint8_t leftX = 128) {
ImeHostPad pad;
pad.buttons = buttons;
pad.leftX = leftX;
return pad;
}
std::size_t Chosen(ModalDialogState expected) {
std::size_t chosen = 7;
Require(ModalDialogPoll_nid_postfix(chosen) == expected, "unexpected state");
return chosen;
}
}
int main() {
Require(!ModalDialogHasPresenter_nid_postfix(), "a presenter is registered at start");
Require(!ModalDialogOpen_nid_postfix(YesNo(0, 1)), "opened without a presenter");
Chosen(ModalDialogState::Idle);
Require(!ModalDialogCapturesInput_nid_postfix(), "captures input while idle");
ModalDialogSetPresenter_nid_postfix(true);
Require(ModalDialogHasPresenter_nid_postfix(), "presenter not registered");
ModalDialogRequest none;
Require(!ModalDialogOpen_nid_postfix(none), "opened without buttons");
ModalDialogRequest four = YesNo(0, 1);
four.buttons = {u"a", u"b", u"c", u"d"};
Require(!ModalDialogOpen_nid_postfix(four), "opened with four buttons");
Require(!ModalDialogOpen_nid_postfix(YesNo(2, 1)), "opened with a focus past the buttons");
Require(!ModalDialogOpen_nid_postfix(YesNo(0, 2)), "opened with a cancel button past the buttons");
Chosen(ModalDialogState::Idle);
Require(ModalDialogOpen_nid_postfix(YesNo(0, 1)), "valid request refused");
Require(!ModalDialogOpen_nid_postfix(YesNo(0, 1)), "second open accepted while showing");
Chosen(ModalDialogState::Showing);
Require(ModalDialogCapturesInput_nid_postfix(), "does not capture input while showing");
ModalDialogView view;
Require(ModalDialogReadView_nid_postfix(view), "no view while showing");
Require(view.message == u"Overwrite?" && view.buttons.size() == 2, "view content");
Require(view.buttons[0].selected && !view.buttons[1].selected && view.buttons[1].label == u"No", "view focus");
ModalDialogPublishInput_nid_postfix(ModalDialogInputKind::Previous);
Require(ModalDialogReadView_nid_postfix(view) && view.buttons[0].selected, "focus moved before the first button");
ModalDialogPublishInput_nid_postfix(ModalDialogInputKind::Next);
ModalDialogPublishInput_nid_postfix(ModalDialogInputKind::Next);
Require(ModalDialogReadView_nid_postfix(view) && view.buttons[1].selected, "focus moved past the last button");
ModalDialogPublishInput_nid_postfix(ModalDialogInputKind::Confirm);
Require(Chosen(ModalDialogState::Answered) == 1, "confirm did not choose the focused button");
Require(!ModalDialogCapturesInput_nid_postfix(), "captures input once answered");
Require(!ModalDialogReadView_nid_postfix(view), "view after the answer");
ModalDialogPublishInput_nid_postfix(ModalDialogInputKind::Previous);
Require(Chosen(ModalDialogState::Answered) == 1, "an answered dialog changed");
Require(ModalDialogOpen_nid_postfix(YesNo(1, 1)), "reopen refused after an answer");
Chosen(ModalDialogState::Showing);
ModalDialogPublishInput_nid_postfix(ModalDialogInputKind::Cancel);
Require(Chosen(ModalDialogState::Answered) == 1, "cancel did not choose the cancel button");
ModalDialogRequest noCancel = YesNo(0, ModalDialogNoButton);
Require(ModalDialogOpen_nid_postfix(noCancel), "request without cancel refused");
ModalDialogPublishInput_nid_postfix(ModalDialogInputKind::Cancel);
Chosen(ModalDialogState::Showing);
ModalDialogClose_nid_postfix();
Chosen(ModalDialogState::Closed);
Require(!ModalDialogCapturesInput_nid_postfix(), "captures input once closed");
ModalDialogReset_nid_postfix();
Chosen(ModalDialogState::Idle);
Require(ModalDialogOpen_nid_postfix(YesNo(0, 1)), "reopen refused after a reset");
ModalDialogPublishPad_nid_postfix(Pad(Cross));
Chosen(ModalDialogState::Showing);
ModalDialogPublishPad_nid_postfix(Pad(0));
ModalDialogPublishPad_nid_postfix(Pad(Right));
Require(ModalDialogReadView_nid_postfix(view) && view.buttons[1].selected, "D-pad right did not move the focus");
ModalDialogPublishPad_nid_postfix(Pad(Right));
ModalDialogPublishPad_nid_postfix(Pad(0));
ModalDialogPublishPad_nid_postfix(Pad(0, 20));
Require(ModalDialogReadView_nid_postfix(view) && view.buttons[0].selected, "stick left did not move the focus");
ModalDialogPublishPad_nid_postfix(Pad(0, 128));
ModalDialogPublishPad_nid_postfix(Pad(Left));
Require(ModalDialogReadView_nid_postfix(view) && view.buttons[0].selected, "focus moved before the first button");
ModalDialogPublishPad_nid_postfix(Pad(0));
ModalDialogPublishPad_nid_postfix(Pad(Circle));
Require(Chosen(ModalDialogState::Answered) == 1, "Circle did not cancel");
Require(ModalDialogOpen_nid_postfix(YesNo(0, 1)), "reopen refused");
ModalDialogPublishPad_nid_postfix(Pad(Cross | Circle));
ModalDialogPublishPad_nid_postfix(Pad(Cross | Circle));
Chosen(ModalDialogState::Showing);
ModalDialogPublishPad_nid_postfix(Pad(0));
ModalDialogPublishPad_nid_postfix(Pad(Cross));
Require(Chosen(ModalDialogState::Answered) == 0, "Cross did not confirm the focused button");
Require(ModalDialogOpen_nid_postfix(YesNo(0, 1)), "reopen refused before the IME check");
ImeSetHostTextSink_nid_no_patch(&IgnoreText);
Require(!ModalDialogCapturesInput_nid_postfix(), "captures input while the IME owns the keyboard");
Require(!ModalDialogReadView_nid_postfix(view), "view while the IME owns the keyboard");
ModalDialogPublishInput_nid_postfix(ModalDialogInputKind::Confirm);
Chosen(ModalDialogState::Showing);
ImeSetHostTextSink_nid_no_patch(nullptr);
Require(ModalDialogCapturesInput_nid_postfix(), "does not resume after the IME releases the keyboard");
Require(ModalDialogReadView_nid_postfix(view), "no view after the IME releases the keyboard");
ModalDialogPublishInput_nid_postfix(ModalDialogInputKind::Confirm);
Require(Chosen(ModalDialogState::Answered) == 0, "confirm after the IME released the keyboard");
ModalDialogSetPresenter_nid_postfix(false);
Require(!ModalDialogOpen_nid_postfix(YesNo(0, 1)), "opened after the presenter went away");
ModalDialogSetPresenter_nid_postfix(true);
ModalDialogReset_nid_postfix();
ModalDialogSetShowTimeout_nid_postfix(40);
Require(ModalDialogOpen_nid_postfix(YesNo(0, 1)), "reopen refused before the timeout checks");
Chosen(ModalDialogState::Showing);
Require(ModalDialogCapturesInput_nid_postfix(), "a new dialog does not capture input before the timeout");
std::this_thread::sleep_for(std::chrono::milliseconds(120));
Require(!ModalDialogCapturesInput_nid_postfix(), "an undrawn dialog still captures input after the timeout");
Chosen(ModalDialogState::Unshown);
Require(!ModalDialogReadView_nid_postfix(view), "an undrawn dialog was drawn after the timeout");
ModalDialogPublishInput_nid_postfix(ModalDialogInputKind::Confirm);
Chosen(ModalDialogState::Unshown);
Require(ModalDialogOpen_nid_postfix(YesNo(0, 1)), "an unshown dialog blocks the next one");
Require(ModalDialogReadView_nid_postfix(view), "a new dialog is not drawn");
std::this_thread::sleep_for(std::chrono::milliseconds(120));
Require(ModalDialogCapturesInput_nid_postfix(), "a dialog that was drawn stopped capturing input");
Chosen(ModalDialogState::Showing);
ModalDialogPublishInput_nid_postfix(ModalDialogInputKind::Confirm);
Require(Chosen(ModalDialogState::Answered) == 0, "a drawn dialog could not be answered after the timeout");
ModalDialogReset_nid_postfix();
ModalDialogSetShowTimeout_nid_postfix(ModalDialogShowTimeoutMilliseconds);
ModalDialogSetPresenter_nid_postfix(false);
}