Files
RAIntegration/tests/ui/viewmodels/OverlayManager_Tests.cpp

722 lines
32 KiB
C++

#include "CppUnitTest.h"
#include "ui\viewmodels\OverlayManager.hh"
#include "ui\OverlayTheme.hh"
#include "tests\devkit\context\mocks\MockRcClient.hh"
#include "tests\devkit\context\mocks\MockUserContext.hh"
#include "tests\devkit\services\mocks\MockClock.hh"
#include "tests\devkit\services\mocks\MockConfiguration.hh"
#include "tests\devkit\services\mocks\MockThreadPool.hh"
#include "tests\devkit\ui\mocks\MockImageRepository.hh"
#include "tests\mocks\MockAchievementRuntime.hh"
#include "tests\mocks\MockDesktop.hh"
#include "tests\mocks\MockEmulatorContext.hh"
#include "tests\mocks\MockGameContext.hh"
#include "tests\mocks\MockOverlayTheme.hh"
#include "tests\mocks\MockSurface.hh"
#include "tests\mocks\MockWindowConfiguration.hh"
#include "tests\mocks\MockWindowManager.hh"
#include "tests\ui\UIAsserts.hh"
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace ra {
namespace ui {
namespace viewmodels {
namespace tests {
TEST_CLASS(OverlayManager_Tests)
{
private:
class OverlayManagerHarness : public OverlayManager
{
public:
ra::context::mocks::MockRcClient mockRcClient;
ra::context::mocks::MockUserContext mockUserContext;
ra::data::context::mocks::MockEmulatorContext mockEmulatorContext;
ra::data::context::mocks::MockGameContext mockGameContext;
ra::services::mocks::MockAchievementRuntime mockAchievementRuntime;
ra::services::mocks::MockClock mockClock;
ra::services::mocks::MockConfiguration mockConfiguration;
ra::services::mocks::MockThreadPool mockThreadPool;
ra::services::mocks::MockWindowConfiguration mockWindowConfiguration;
ra::ui::mocks::MockDesktop mockDesktop;
ra::ui::mocks::MockImageRepository mockImageRepository;
ra::ui::mocks::MockOverlayTheme mockTheme;
ra::ui::drawing::mocks::MockSurfaceFactory mockSurfaceFactory;
ra::ui::viewmodels::mocks::MockWindowManager mockWindowManager;
OverlayManagerHarness() noexcept
: m_Override(this)
{
GSL_SUPPRESS_F6 SetRenderRequestHandler([this]()
{
m_bRenderRequested = true;
});
GSL_SUPPRESS_F6 SetShowRequestHandler([this]()
{
m_bShowRequested = true;
});
GSL_SUPPRESS_F6 SetHideRequestHandler([this]()
{
m_bHideRequested = true;
});
}
bool WasRenderRequested() const noexcept { return m_bRenderRequested; }
bool WasShowRequested() const noexcept { return m_bShowRequested; }
bool WasHideRequested() const noexcept { return m_bHideRequested; }
void ResetRenderRequested() noexcept
{
m_bRenderRequested = false;
m_bShowRequested = false;
m_bHideRequested = false;
}
int GetOverlayRenderX() const { return m_vmOverlay.GetHorizontalOffset(); }
ra::ui::viewmodels::ProgressTrackerViewModel* GetProgressTracker() noexcept
{
return m_vmProgressTracker.get();
}
void AddLeaderboard(unsigned id, const std::string& sDefinition)
{
auto vmLeaderboard = std::make_unique<ra::data::models::LeaderboardModel>();
vmLeaderboard->SetID(id);
vmLeaderboard->SetCategory(ra::data::models::AssetCategory::Promoted);
vmLeaderboard->SetValueDefinition(sDefinition);
vmLeaderboard->CreateServerCheckpoint();
vmLeaderboard->CreateLocalCheckpoint();
mockGameContext.Assets().Append(std::move(vmLeaderboard));
}
private:
bool m_bRenderRequested = false;
bool m_bShowRequested = false;
bool m_bHideRequested = false;
ra::services::ServiceLocator::ServiceOverride<OverlayManager> m_Override;
};
public:
TEST_METHOD(TestQueueMessageTitleDesc)
{
OverlayManagerHarness overlay;
const auto nId = overlay.QueueMessage(L"Title", L"Description");
const auto* pPopup = overlay.GetMessage(nId);
Expects(pPopup != nullptr);
Assert::AreEqual(std::wstring(L"Title"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Description"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L""), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::None, pPopup->GetImage().Type());
Assert::IsTrue(overlay.WasRenderRequested());
}
TEST_METHOD(TestQueueMessageTitleDescDetail)
{
OverlayManagerHarness overlay;
const auto nId = overlay.QueueMessage(L"Title", L"Description", L"Detail");
const auto* pPopup = overlay.GetMessage(nId);
Expects(pPopup != nullptr);
Assert::AreEqual(std::wstring(L"Title"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Description"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Detail"), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::None, pPopup->GetImage().Type());
Assert::IsTrue(overlay.WasRenderRequested());
}
TEST_METHOD(TestQueueMessageTitleDescImage)
{
OverlayManagerHarness overlay;
const auto nId = overlay.QueueMessage(L"Title", L"Description", ra::ui::ImageType::Badge, "BadgeURI");
const auto* pPopup = overlay.GetMessage(nId);
Expects(pPopup != nullptr);
Assert::AreEqual(std::wstring(L"Title"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Description"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L""), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::Badge, pPopup->GetImage().Type());
Assert::AreEqual(std::string("BadgeURI"), pPopup->GetImage().Name());
Assert::IsTrue(overlay.WasRenderRequested());
}
TEST_METHOD(TestQueueMessageTitleDescDetailImage)
{
OverlayManagerHarness overlay;
const auto nId = overlay.QueueMessage(L"Title", L"Description", L"Detail", ra::ui::ImageType::Badge, "BadgeURI");
const auto* pPopup = overlay.GetMessage(nId);
Expects(pPopup != nullptr);
Assert::AreEqual(std::wstring(L"Title"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Description"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Detail"), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::Badge, pPopup->GetImage().Type());
Assert::AreEqual(std::string("BadgeURI"), pPopup->GetImage().Name());
Assert::IsTrue(overlay.WasRenderRequested());
}
TEST_METHOD(TestRenderPopup)
{
OverlayManagerHarness overlay;
const auto nId = overlay.QueueMessage(L"Title", L"Description");
const auto* pPopup = overlay.GetMessage(nId);
Expects(pPopup != nullptr);
// Queuing the message requests the overlay be shown and rendered
Assert::IsTrue(overlay.WasRenderRequested());
Assert::IsTrue(overlay.WasShowRequested());
Assert::IsFalse(overlay.WasHideRequested());
overlay.ResetRenderRequested();
ra::ui::drawing::mocks::MockSurface mockSurface(800, 600);
overlay.Render(mockSurface, false);
Assert::AreEqual(pPopup->GetVerticalOffset(), -74);
// a queued popup still requests a render even if it's not visible. this ensures the Render
// callback will be called to animate the popup
Assert::IsTrue(overlay.WasRenderRequested());
Assert::IsFalse(overlay.WasShowRequested());
Assert::IsFalse(overlay.WasHideRequested());
// 0.8 seconds to fully visualize, expect it to be partially onscreen after 0.5 seconds
overlay.mockClock.AdvanceTime(std::chrono::milliseconds(500));
overlay.ResetRenderRequested();
overlay.Render(mockSurface, false);
Assert::IsTrue(pPopup->GetVerticalOffset() > -74);
Assert::IsTrue(pPopup->GetVerticalOffset() < 10);
Assert::IsTrue(overlay.WasRenderRequested());
Assert::IsFalse(overlay.WasShowRequested());
Assert::IsFalse(overlay.WasHideRequested());
// after 1 second, it should be fully on screen
overlay.mockClock.AdvanceTime(std::chrono::milliseconds(500));
overlay.ResetRenderRequested();
overlay.Render(mockSurface, false);
Assert::AreEqual(pPopup->GetVerticalOffset(), 10);
Assert::IsTrue(overlay.WasRenderRequested());
Assert::IsFalse(overlay.WasShowRequested());
Assert::IsFalse(overlay.WasHideRequested());
// after 2 seconds, it should still be fully on screen
overlay.mockClock.AdvanceTime(std::chrono::seconds(1));
overlay.ResetRenderRequested();
overlay.Render(mockSurface, false);
Assert::AreEqual(pPopup->GetVerticalOffset(), 10);
Assert::IsTrue(overlay.WasRenderRequested());
Assert::IsFalse(overlay.WasShowRequested());
Assert::IsFalse(overlay.WasHideRequested());
// after 3 seconds, it should still be fully on screen
overlay.mockClock.AdvanceTime(std::chrono::seconds(1));
overlay.ResetRenderRequested();
overlay.Render(mockSurface, false);
Assert::AreEqual(pPopup->GetVerticalOffset(), 10);
Assert::IsTrue(overlay.WasRenderRequested());
Assert::IsFalse(overlay.WasShowRequested());
Assert::IsFalse(overlay.WasHideRequested());
// after 4 seconds, it should still be fully on screen
overlay.mockClock.AdvanceTime(std::chrono::seconds(1));
overlay.ResetRenderRequested();
overlay.Render(mockSurface, false);
Assert::AreEqual(pPopup->GetVerticalOffset(), 10);
Assert::IsTrue(overlay.WasRenderRequested());
Assert::IsFalse(overlay.WasShowRequested());
Assert::IsFalse(overlay.WasHideRequested());
// after 4.5 seconds, it should be starting to scroll offscreen
overlay.mockClock.AdvanceTime(std::chrono::milliseconds(500));
overlay.ResetRenderRequested();
overlay.Render(mockSurface, false);
Assert::IsTrue(pPopup->GetVerticalOffset() > -74);
Assert::IsTrue(pPopup->GetVerticalOffset() < 10);
Assert::IsTrue(overlay.WasRenderRequested());
Assert::IsFalse(overlay.WasShowRequested());
Assert::IsFalse(overlay.WasHideRequested());
Assert::IsNotNull(overlay.GetMessage(nId));
// after 5 seconds, it should be fully offscreen and removed from the queue
// since it's removed from the queue, we can't read RenderLocationY.
overlay.mockClock.AdvanceTime(std::chrono::milliseconds(500));
overlay.ResetRenderRequested();
overlay.Render(mockSurface, false);
Assert::IsFalse(overlay.WasRenderRequested());
Assert::IsFalse(overlay.WasShowRequested());
Assert::IsTrue(overlay.WasHideRequested());
Assert::IsNull(overlay.GetMessage(nId));
}
TEST_METHOD(TestAddRemoveScoreTrackerLeaderboardEnabled)
{
OverlayManagerHarness overlay;
overlay.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardTracker, ra::ui::viewmodels::PopupLocation::BottomRight);
const auto& vmScoreTracker = overlay.AddScoreTracker(3);
Assert::AreEqual(3, vmScoreTracker.GetPopupId());
Assert::AreEqual(std::wstring(L"0"), vmScoreTracker.GetDisplayText());
Assert::IsTrue(overlay.WasRenderRequested());
Assert::IsFalse(vmScoreTracker.IsDestroyPending());
Assert::AreEqual(10, vmScoreTracker.GetVerticalOffset());
Assert::IsTrue(&vmScoreTracker == overlay.GetScoreTracker(3));
ra::ui::drawing::mocks::MockSurface mockSurface(800, 600);
overlay.Render(mockSurface, false);
Assert::AreEqual(10, vmScoreTracker.GetVerticalOffset());
overlay.ResetRenderRequested();
overlay.RemoveScoreTracker(3);
Assert::IsTrue(vmScoreTracker.IsDestroyPending()); // has to erase itself in render
Assert::IsFalse(overlay.WasRenderRequested());
Assert::IsNotNull(overlay.GetScoreTracker(3));
overlay.Render(mockSurface, false);
Assert::IsNull(overlay.GetScoreTracker(3)); // render should erase and destroy
}
TEST_METHOD(TestAddRemoveScoreTrackerLeaderboardDisabled)
{
OverlayManagerHarness overlay;
overlay.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardTracker, ra::ui::viewmodels::PopupLocation::None);
const auto& vmScoreTracker = overlay.AddScoreTracker(3);
Assert::AreEqual(3, vmScoreTracker.GetPopupId());
Assert::AreEqual(std::wstring(L"0"), vmScoreTracker.GetDisplayText());
Assert::IsTrue(overlay.WasRenderRequested());
Assert::IsFalse(vmScoreTracker.IsDestroyPending());
Assert::AreEqual(10, vmScoreTracker.GetVerticalOffset());
Assert::IsTrue(&vmScoreTracker == overlay.GetScoreTracker(3));
ra::ui::drawing::mocks::MockSurface mockSurface(800, 600);
overlay.Render(mockSurface, false);
Assert::AreEqual(10, vmScoreTracker.GetVerticalOffset());
overlay.ResetRenderRequested();
overlay.RemoveScoreTracker(3);
Assert::IsTrue(vmScoreTracker.IsDestroyPending()); // has to erase itself in render
Assert::IsTrue(overlay.WasRenderRequested());
Assert::IsNotNull(overlay.GetScoreTracker(3));
overlay.Render(mockSurface, false);
Assert::IsNull(overlay.GetScoreTracker(3)); // render should erase and destroy
}
TEST_METHOD(TestAddMultipleScoreTrackers)
{
OverlayManagerHarness overlay;
overlay.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardTracker, ra::ui::viewmodels::PopupLocation::BottomRight);
overlay.AddLeaderboard(3, "0xH1234");
overlay.AddLeaderboard(4, "0xH2345");
const auto& vmScoreTracker = overlay.AddScoreTracker(3);
Assert::AreEqual(3, vmScoreTracker.GetPopupId());
Assert::IsFalse(vmScoreTracker.IsDestroyPending());
const auto& vmScoreTracker2 = overlay.AddScoreTracker(4);
Assert::AreEqual(4, vmScoreTracker2.GetPopupId());
Assert::IsFalse(vmScoreTracker2.IsDestroyPending());
// no defined font, widget will only be 3 pixels tall
constexpr int nWidgetHeight = 3;
ra::ui::drawing::mocks::MockSurface mockSurface(800, 600);
overlay.Render(mockSurface, false);
Assert::AreEqual(600 - 10 - nWidgetHeight, vmScoreTracker.GetRenderLocationY());
Assert::AreEqual(600 - 10 - nWidgetHeight - 10 - nWidgetHeight, vmScoreTracker2.GetRenderLocationY());
overlay.ResetRenderRequested();
overlay.RemoveScoreTracker(3);
Assert::IsTrue(vmScoreTracker.IsDestroyPending()); // has to erase itself in render
Assert::IsFalse(overlay.WasRenderRequested());
Assert::IsNotNull(overlay.GetScoreTracker(3));
overlay.Render(mockSurface, false);
Assert::IsNull(overlay.GetScoreTracker(3)); // render should erase and destroy
Assert::AreEqual(600 - 10 - nWidgetHeight, vmScoreTracker2.GetRenderLocationY());
}
TEST_METHOD(TestAddMultipleScoreTrackersSharedValue)
{
OverlayManagerHarness overlay;
overlay.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardTracker, ra::ui::viewmodels::PopupLocation::BottomRight);
overlay.AddLeaderboard(3, "0xH1234");
overlay.AddLeaderboard(4, "0xH1234"); // same value definition
const auto& vmScoreTracker = overlay.AddScoreTracker(3);
Assert::AreEqual(3, vmScoreTracker.GetPopupId());
Assert::IsFalse(vmScoreTracker.IsDestroyPending());
const auto& vmScoreTracker2 = overlay.AddScoreTracker(4);
Assert::AreEqual(4, vmScoreTracker2.GetPopupId());
Assert::IsFalse(vmScoreTracker2.IsDestroyPending());
// no defined font, widget will only be 3 pixels tall
constexpr int nWidgetHeight = 3;
ra::ui::drawing::mocks::MockSurface mockSurface(800, 600);
overlay.Render(mockSurface, false);
Assert::AreEqual(600 - 10 - nWidgetHeight, vmScoreTracker.GetRenderLocationY());
Assert::AreEqual(-100, vmScoreTracker2.GetRenderLocationY()); // second widget positioned offscreen
overlay.ResetRenderRequested();
overlay.RemoveScoreTracker(3);
Assert::IsTrue(vmScoreTracker.IsDestroyPending()); // has to erase itself in render
Assert::IsFalse(overlay.WasRenderRequested());
Assert::IsNotNull(overlay.GetScoreTracker(3));
overlay.Render(mockSurface, false);
Assert::IsNull(overlay.GetScoreTracker(3)); // render should erase and destroy
Assert::AreEqual(600 - 10 - nWidgetHeight, vmScoreTracker2.GetRenderLocationY());
}
TEST_METHOD(TestQueueScoreboard)
{
OverlayManagerHarness overlay;
{
auto vmScoreboard = std::make_unique<ScoreboardViewModel>();
vmScoreboard->SetHeaderText(L"HeaderText");
overlay.QueueScoreboard(3, std::move(vmScoreboard));
}
const auto* pScoreboard = overlay.GetScoreboard(3);
Expects(pScoreboard != nullptr);
Assert::AreEqual(std::wstring(L"HeaderText"), pScoreboard->GetHeaderText());
Assert::IsTrue(overlay.WasRenderRequested());
}
TEST_METHOD(TestRenderScoreboard)
{
OverlayManagerHarness overlay;
{
auto vmScoreboard = std::make_unique<ScoreboardViewModel>();
vmScoreboard->SetHeaderText(L"HeaderText");
overlay.QueueScoreboard(3, std::move(vmScoreboard));
}
const auto* pScoreboard = overlay.GetScoreboard(3);
Expects(pScoreboard != nullptr);
constexpr auto nScoreboardWidth = 288;
ra::ui::drawing::mocks::MockSurface mockSurface(800, 600);
overlay.Render(mockSurface, false);
Assert::AreEqual(pScoreboard->GetHorizontalOffset(), 10 - nScoreboardWidth);
// no time has elapsed since the popup was queued, we're still waiting for the first render
Assert::IsTrue(overlay.WasRenderRequested());
// 0.8 seconds to fully visualize, expect it to be partially onscreen after 0.5 seconds
overlay.mockClock.AdvanceTime(std::chrono::milliseconds(500));
overlay.ResetRenderRequested();
overlay.Render(mockSurface, false);
Assert::IsTrue(pScoreboard->GetHorizontalOffset() > 10 - nScoreboardWidth);
Assert::IsTrue(pScoreboard->GetHorizontalOffset() < 10);
Assert::IsTrue(overlay.WasRenderRequested());
// after 1 second, it should be fully on screen
overlay.mockClock.AdvanceTime(std::chrono::milliseconds(500));
overlay.ResetRenderRequested();
overlay.Render(mockSurface, false);
Assert::AreEqual(10, pScoreboard->GetHorizontalOffset());
Assert::IsTrue(overlay.WasRenderRequested());
// after 2 seconds, it should still be fully on screen
overlay.mockClock.AdvanceTime(std::chrono::seconds(1));
overlay.ResetRenderRequested();
overlay.Render(mockSurface, false);
Assert::AreEqual(10, pScoreboard->GetHorizontalOffset());
Assert::IsTrue(overlay.WasRenderRequested());
// after 3 seconds, it should still be fully on screen
overlay.mockClock.AdvanceTime(std::chrono::seconds(1));
overlay.ResetRenderRequested();
overlay.Render(mockSurface, false);
Assert::AreEqual(10, pScoreboard->GetHorizontalOffset());
Assert::IsTrue(overlay.WasRenderRequested());
// after 4 seconds, it should still be fully on screen
overlay.mockClock.AdvanceTime(std::chrono::seconds(1));
overlay.ResetRenderRequested();
overlay.Render(mockSurface, false);
Assert::AreEqual(10, pScoreboard->GetHorizontalOffset());
Assert::IsTrue(overlay.WasRenderRequested());
// after 5 seconds, it should still be fully on screen
overlay.mockClock.AdvanceTime(std::chrono::seconds(1));
overlay.ResetRenderRequested();
overlay.Render(mockSurface, false);
Assert::AreEqual(10, pScoreboard->GetHorizontalOffset());
Assert::IsTrue(overlay.WasRenderRequested());
// after 6 seconds, it should still be fully on screen
overlay.mockClock.AdvanceTime(std::chrono::seconds(1));
overlay.ResetRenderRequested();
overlay.Render(mockSurface, false);
Assert::AreEqual(10, pScoreboard->GetHorizontalOffset());
Assert::IsTrue(overlay.WasRenderRequested());
// after 6.5 seconds, it should be starting to scroll offscreen
overlay.mockClock.AdvanceTime(std::chrono::milliseconds(500));
overlay.ResetRenderRequested();
overlay.Render(mockSurface, false);
Assert::IsTrue(pScoreboard->GetHorizontalOffset() > 10 - nScoreboardWidth);
Assert::IsTrue(pScoreboard->GetHorizontalOffset() < 10);
Assert::IsTrue(overlay.WasRenderRequested());
Assert::IsNotNull(overlay.GetScoreboard(3));
// after 7 seconds, it should be fully offscreen and removed from the queue
// since it's removed from the queue, we can't read RenderLocationY.
overlay.mockClock.AdvanceTime(std::chrono::milliseconds(500));
overlay.ResetRenderRequested();
overlay.Render(mockSurface, false);
Assert::IsFalse(overlay.WasRenderRequested());
Assert::IsNull(overlay.GetScoreboard(3));
}
TEST_METHOD(TestAddRemoveChallengeIndicator)
{
OverlayManagerHarness overlay;
overlay.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::Challenge, ra::ui::viewmodels::PopupLocation::BottomRight);
const auto& vmIndicator = overlay.AddChallengeIndicator(6, ra::ui::ImageType::Badge, "12345");
Assert::AreEqual(6, vmIndicator.GetPopupId());
Assert::AreEqual(ra::ui::ImageType::Badge, vmIndicator.GetImage().Type());
Assert::AreEqual(std::string("12345"), vmIndicator.GetImage().Name());
Assert::IsTrue(overlay.WasRenderRequested());
Assert::IsFalse(vmIndicator.IsDestroyPending());
Assert::AreEqual(10, vmIndicator.GetVerticalOffset());
Assert::IsTrue(&vmIndicator == overlay.GetChallengeIndicator(6));
ra::ui::drawing::mocks::MockSurface mockSurface(800, 600);
overlay.Render(mockSurface, false);
Assert::AreEqual(10, vmIndicator.GetVerticalOffset());
overlay.ResetRenderRequested();
overlay.RemoveChallengeIndicator(6);
Assert::IsTrue(vmIndicator.IsDestroyPending()); // has to erase itself in render
Assert::IsFalse(overlay.WasRenderRequested());
Assert::IsNotNull(overlay.GetChallengeIndicator(6));
overlay.Render(mockSurface, false);
Assert::IsNull(overlay.GetChallengeIndicator(6)); // render should erase and destroy
}
TEST_METHOD(TestAddRemoveChallengeIndicatorDestroyPending)
{
OverlayManagerHarness overlay;
overlay.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::Challenge, ra::ui::viewmodels::PopupLocation::BottomRight);
const auto& vmIndicator = overlay.AddChallengeIndicator(6, ra::ui::ImageType::Badge, "12345");
Assert::IsFalse(vmIndicator.IsDestroyPending());
Assert::IsTrue(&vmIndicator == overlay.GetChallengeIndicator(6));
overlay.RemoveChallengeIndicator(6);
Assert::IsTrue(vmIndicator.IsDestroyPending());
Assert::IsNotNull(overlay.GetChallengeIndicator(6));
ra::ui::drawing::mocks::MockSurface mockSurface(800, 600);
overlay.Render(mockSurface, false);
overlay.ResetRenderRequested();
// request to redisplay indicator (it's currently queued to be destroyed)
const auto& vmNewIndicator = overlay.AddChallengeIndicator(6, ra::ui::ImageType::Badge, "12345");
Assert::AreEqual(6, vmNewIndicator.GetPopupId());
Assert::AreEqual(ra::ui::ImageType::Badge, vmNewIndicator.GetImage().Type());
Assert::AreEqual(std::string("12345"), vmNewIndicator.GetImage().Name());
Assert::IsTrue(overlay.WasRenderRequested());
Assert::IsFalse(vmNewIndicator.IsDestroyPending());
overlay.Render(mockSurface, false);
// indicator should exist
Assert::IsTrue(&vmNewIndicator == overlay.GetChallengeIndicator(6));
}
TEST_METHOD(TestAddMultipleChallengeIndicator)
{
OverlayManagerHarness overlay;
overlay.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::Challenge, ra::ui::viewmodels::PopupLocation::BottomRight);
const auto& vmIndicator = overlay.AddChallengeIndicator(6, ra::ui::ImageType::Badge, "12345");
const auto& vmIndicator2 = overlay.AddChallengeIndicator(7, ra::ui::ImageType::Badge, "22222");
const auto& vmIndicator3 = overlay.AddChallengeIndicator(8, ra::ui::ImageType::Badge, "75319");
Assert::AreEqual(6, vmIndicator.GetPopupId());
Assert::AreEqual(ra::ui::ImageType::Badge, vmIndicator.GetImage().Type());
Assert::AreEqual(std::string("12345"), vmIndicator.GetImage().Name());
Assert::AreEqual(7, vmIndicator2.GetPopupId());
Assert::AreEqual(ra::ui::ImageType::Badge, vmIndicator2.GetImage().Type());
Assert::AreEqual(std::string("22222"), vmIndicator2.GetImage().Name());
Assert::AreEqual(8, vmIndicator3.GetPopupId());
Assert::AreEqual(ra::ui::ImageType::Badge, vmIndicator3.GetImage().Type());
Assert::AreEqual(std::string("75319"), vmIndicator3.GetImage().Name());
Assert::IsTrue(overlay.WasRenderRequested());
ra::ui::drawing::mocks::MockSurface mockSurface(800, 600);
overlay.Render(mockSurface, false);
constexpr int nSpacing = 10 + 32 + 2; // spacing + image + shadow
Assert::AreEqual(10, vmIndicator.GetHorizontalOffset());
Assert::AreEqual(10 + nSpacing, vmIndicator2.GetHorizontalOffset());
Assert::AreEqual(10 + nSpacing * 2, vmIndicator3.GetHorizontalOffset());
Assert::AreEqual(10, vmIndicator.GetVerticalOffset());
Assert::AreEqual(10, vmIndicator2.GetVerticalOffset());
Assert::AreEqual(10, vmIndicator3.GetVerticalOffset());
overlay.RemoveChallengeIndicator(7);
Assert::IsFalse(vmIndicator.IsDestroyPending());
Assert::IsTrue(vmIndicator2.IsDestroyPending());
Assert::IsFalse(vmIndicator3.IsDestroyPending());
overlay.Render(mockSurface, false);
Assert::AreEqual(10, vmIndicator.GetHorizontalOffset());
Assert::AreEqual(10 + nSpacing, vmIndicator3.GetHorizontalOffset());
Assert::AreEqual(10, vmIndicator.GetVerticalOffset());
Assert::AreEqual(10, vmIndicator3.GetVerticalOffset());
}
TEST_METHOD(TestAddExistingChallengeIndicator)
{
OverlayManagerHarness overlay;
overlay.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::Challenge, ra::ui::viewmodels::PopupLocation::BottomRight);
const auto& vmIndicator = overlay.AddChallengeIndicator(6, ra::ui::ImageType::Badge, "12345");
Assert::AreEqual(6, vmIndicator.GetPopupId());
Assert::AreEqual(ra::ui::ImageType::Badge, vmIndicator.GetImage().Type());
Assert::AreEqual(std::string("12345"), vmIndicator.GetImage().Name());
Assert::IsTrue(overlay.WasRenderRequested());
Assert::IsFalse(vmIndicator.IsDestroyPending());
Assert::AreEqual(10, vmIndicator.GetVerticalOffset());
Assert::IsTrue(&vmIndicator == overlay.GetChallengeIndicator(6));
const auto& vmIndicator2 = overlay.AddChallengeIndicator(6, ra::ui::ImageType::Badge, "12345");
Assert::IsTrue(&vmIndicator == &vmIndicator2);
}
TEST_METHOD(TestAddRemoveProgressIndicator)
{
OverlayManagerHarness overlay;
overlay.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::Progress, ra::ui::viewmodels::PopupLocation::BottomRight);
overlay.UpdateProgressTracker(ra::ui::ImageType::Badge, "12345_lock", L"3/7");
auto* pTracker = overlay.GetProgressTracker();
Assert::IsNotNull(pTracker);
Ensures(pTracker != nullptr);
Assert::AreEqual(std::wstring(L"3/7"), pTracker->GetText());
Assert::AreEqual(ra::ui::ImageType::Badge, pTracker->GetImage().Type());
Assert::AreEqual(std::string("12345_lock"), pTracker->GetImage().Name());
Assert::IsTrue(overlay.WasRenderRequested());
Assert::IsFalse(pTracker->IsDestroyPending());
Assert::AreEqual(10, pTracker->GetVerticalOffset());
ra::ui::drawing::mocks::MockSurface mockSurface(800, 600);
overlay.Render(mockSurface, false);
Assert::AreEqual(10, pTracker->GetVerticalOffset());
overlay.UpdateProgressTracker(ra::ui::ImageType::None, "", L"");
overlay.Render(mockSurface, false);
Assert::IsTrue(overlay.WasRenderRequested()); // destroy pending
Assert::IsTrue(pTracker->IsDestroyPending());
overlay.Render(mockSurface, false);
pTracker = overlay.GetProgressTracker(); // destroy occurred
Assert::IsNull(pTracker);
}
TEST_METHOD(TestShowHideOverlay)
{
OverlayManagerHarness overlay;
ra::ui::drawing::mocks::MockSurface mockSurface(800, 600);
Assert::IsFalse(overlay.IsOverlayFullyVisible());
overlay.ShowOverlay();
Assert::IsTrue(overlay.WasRenderRequested());
Assert::IsTrue(overlay.WasShowRequested());
// 0.4 seconds to fully visualize, expect it to be partially onscreen after 0.25 seconds
overlay.mockClock.AdvanceTime(std::chrono::milliseconds(250));
overlay.ResetRenderRequested();
overlay.Render(mockSurface, false);
Assert::IsTrue(overlay.GetOverlayRenderX() < 0);
Assert::IsTrue(overlay.GetOverlayRenderX() > -800);
Assert::IsTrue(overlay.WasRenderRequested());
// after 0.5 seconds, it should be fully on screen
overlay.mockClock.AdvanceTime(std::chrono::milliseconds(250));
overlay.ResetRenderRequested();
overlay.Render(mockSurface, false);
Assert::AreEqual(0, overlay.GetOverlayRenderX());
Assert::IsFalse(overlay.WasRenderRequested());
Assert::IsTrue(overlay.IsOverlayFullyVisible());
overlay.HideOverlay();
Assert::IsTrue(overlay.WasRenderRequested());
overlay.Render(mockSurface, false); // force state change from FadeOutRequested to FadeOut
// 0.4 seconds to fully collapse, expect it to be partially onscreen after 0.25 seconds
overlay.mockClock.AdvanceTime(std::chrono::milliseconds(250));
overlay.ResetRenderRequested();
overlay.Render(mockSurface, false);
Assert::IsTrue(overlay.GetOverlayRenderX() < 0);
Assert::IsTrue(overlay.GetOverlayRenderX() > -800);
Assert::IsTrue(overlay.WasRenderRequested());
// after 0.5 seconds, it should be fully off screen
overlay.mockClock.AdvanceTime(std::chrono::milliseconds(250));
overlay.ResetRenderRequested();
overlay.Render(mockSurface, false);
Assert::AreEqual(-800, overlay.GetOverlayRenderX());
Assert::IsFalse(overlay.WasRenderRequested());
Assert::IsTrue(overlay.WasHideRequested());
Assert::IsFalse(overlay.IsOverlayFullyVisible());
}
TEST_METHOD(TestHideOverlayImmediately)
{
OverlayManagerHarness overlay;
ra::ui::drawing::mocks::MockSurface mockSurface(800, 600);
overlay.ShowOverlay();
// after 0.5 seconds, it should be fully on screen
overlay.mockClock.AdvanceTime(std::chrono::milliseconds(500));
overlay.ResetRenderRequested();
overlay.Render(mockSurface, false);
Assert::AreEqual(0, overlay.GetOverlayRenderX());
Assert::IsFalse(overlay.WasRenderRequested());
Assert::IsTrue(overlay.IsOverlayFullyVisible());
overlay.HideOverlayImmediately();
Assert::IsTrue(overlay.WasRenderRequested());
Assert::IsFalse(overlay.IsOverlayFullyVisible());
overlay.Render(mockSurface, false); // force state change from FadeOut to Hidden
// it should be fully off screen immediately
Assert::AreEqual(-800, overlay.GetOverlayRenderX());
Assert::IsTrue(overlay.WasRenderRequested());
Assert::IsTrue(overlay.WasHideRequested());
}
};
} // namespace tests
} // namespace viewmodels
} // namespace ui
} // namespace ra