Files
RAIntegration/tests/services/AchievementRuntime_Tests.cpp

3749 lines
176 KiB
C++

#include "services\AchievementRuntime.hh"
#include "tests\RA_UnitTestHelpers.h"
#include "tests\data\DataAsserts.hh"
#include "tests\devkit\context\mocks\MockConsoleContext.hh"
#include "tests\devkit\context\mocks\MockEmulatorMemoryContext.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\MockFileSystem.hh"
#include "tests\devkit\services\mocks\MockLocalStorage.hh"
#include "tests\devkit\services\mocks\MockThreadPool.hh"
#include "tests\devkit\testutil\AssetAsserts.hh"
#include "tests\devkit\ui\mocks\MockImageRepository.hh"
#include "tests\mocks\MockAudioSystem.hh"
#include "tests\mocks\MockDesktop.hh"
#include "tests\mocks\MockEmulatorContext.hh"
#include "tests\mocks\MockFrameEventQueue.hh"
#include "tests\mocks\MockGameContext.hh"
#include "tests\mocks\MockGameIdentifier.hh"
#include "tests\mocks\MockLoginService.hh"
#include "tests\mocks\MockOverlayManager.hh"
#include "tests\mocks\MockOverlayTheme.hh"
#include "tests\mocks\MockSessionTracker.hh"
#include "tests\mocks\MockSurface.hh"
#include "tests\mocks\MockWindowConfiguration.hh"
#include "tests\mocks\MockWindowManager.hh"
#include "tests\ui\UIAsserts.hh"
#include "context\IRcClient.hh"
#include "services\impl\OfflineRcClient.hh"
#include <rcheevos\src\rc_client_internal.h>
#include <rcheevos\src\rcheevos\rc_internal.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace ra {
namespace services {
namespace tests {
class AchievementRuntimeHarness : public AchievementRuntime
{
public:
GSL_SUPPRESS_F6 AchievementRuntimeHarness()
: AchievementRuntime(false), m_Override(this)
{
InitializeRcClient();
mockUserContext.Initialize("User", "ApiToken");
GetClient()->user.display_name = "UserDisplay";
GetClient()->state.user = RC_CLIENT_USER_STATE_LOGGED_IN;
MockGame();
m_fRealEventHandler = GetClient()->callbacks.event_handler;
rc_client_set_event_handler(GetClient(), CaptureEventHandler);
rc_client_set_userdata(GetClient(), this);
}
ra::context::mocks::MockEmulatorMemoryContext mockEmulatorMemoryContext;
ra::context::mocks::MockRcClient mockRcClient;
ra::context::mocks::MockUserContext mockUserContext;
ra::data::context::mocks::MockEmulatorContext mockEmulatorContext;
ra::data::context::mocks::MockGameContext mockGameContext;
ra::data::context::mocks::MockSessionTracker mockSessionTracker;
ra::services::mocks::MockAudioSystem mockAudioSystem;
ra::services::mocks::MockClock mockClock;
ra::services::mocks::MockConfiguration mockConfiguration;
ra::services::mocks::MockFileSystem mockFileSystem;
ra::services::mocks::MockFrameEventQueue mockFrameEventQueue;
ra::services::mocks::MockLoginService mockLoginService;
ra::services::mocks::MockThreadPool mockThreadPool;
ra::services::mocks::MockWindowConfiguration mockWindowConfiguration;
ra::ui::drawing::mocks::MockSurfaceFactory mockSurfaceFactory;
ra::ui::mocks::MockDesktop mockDesktop;
ra::ui::mocks::MockImageRepository mockImageRepository;
ra::ui::mocks::MockOverlayTheme mockOverlayTheme;
ra::ui::viewmodels::mocks::MockOverlayManager mockOverlayManager;
ra::ui::viewmodels::mocks::MockWindowManager mockWindowManager;
rc_client_t* GetClient() const
{
return ra::services::ServiceLocator::Get<ra::context::IRcClient>().GetClient();
}
void MockGame()
{
rc_client_game_info_t* game = static_cast<rc_client_game_info_t*>(calloc(1, sizeof(rc_client_game_info_t)));
Expects(game != nullptr);
rc_buffer_init(&game->buffer);
rc_runtime_init(&game->runtime);
game->public_.id = 1;
game->public_.console_id = RC_CONSOLE_NINTENDO;
game->public_.badge_name = "012345";
game->public_.title = "Game Title";
auto* core_subset = static_cast<rc_client_subset_info_t*>(rc_buffer_alloc(&game->buffer, sizeof(rc_client_subset_info_t)));
memset(core_subset, 0, sizeof(*core_subset));
core_subset->public_.id = game->public_.id;
core_subset->public_.title = game->public_.title;
strcpy_s(core_subset->public_.badge_name, game->public_.badge_name);
core_subset->public_.badge_url = game->public_.badge_url;
core_subset->active = 1;
game->subsets = core_subset;
GetClient()->game = game;
mockGameContext.Assets().AddAchievementSet(1, 1, L"Game Title");
}
void ResetEvents() noexcept { m_vEvents.clear(); }
size_t GetEventCount() const noexcept { return m_vEvents.size(); }
void AssertEvent(uint32_t nType, uint32_t nRecordId = 0)
{
for (const auto& pEvent : m_vEvents)
{
if (pEvent.type != nType)
continue;
switch (nType)
{
case RC_CLIENT_EVENT_ACHIEVEMENT_CHALLENGE_INDICATOR_SHOW:
case RC_CLIENT_EVENT_ACHIEVEMENT_CHALLENGE_INDICATOR_HIDE:
case RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_SHOW:
case RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_UPDATE:
case RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED:
if (pEvent.achievement->id == nRecordId)
return;
break;
case RC_CLIENT_EVENT_LEADERBOARD_STARTED:
case RC_CLIENT_EVENT_LEADERBOARD_FAILED:
case RC_CLIENT_EVENT_LEADERBOARD_SUBMITTED:
case RC_CLIENT_EVENT_LEADERBOARD_SCOREBOARD:
if (pEvent.leaderboard->id == nRecordId)
return;
break;
case RC_CLIENT_EVENT_LEADERBOARD_TRACKER_UPDATE:
case RC_CLIENT_EVENT_LEADERBOARD_TRACKER_SHOW:
case RC_CLIENT_EVENT_LEADERBOARD_TRACKER_HIDE:
if (pEvent.leaderboard_tracker->id == nRecordId)
return;
break;
case RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_HIDE:
case RC_CLIENT_EVENT_GAME_COMPLETED:
return;
}
}
if (nRecordId != 0)
Assert::Fail(ra::util::String::Printf(L"Event %u not found for record %u.", nType, nRecordId).c_str());
else
Assert::Fail(ra::util::String::Printf(L"Event %u not found.", nType).c_str());
}
void RaiseEvent(const rc_client_event_t& event)
{
m_fRealEventHandler(&event, GetClient());
}
void ProcessCapturedEvents()
{
for (const auto& pEvent : m_vEvents)
RaiseEvent(pEvent);
ResetEvents();
}
rc_client_subset_info_t* MockSubset(uint32_t nId, const std::string& sName)
{
mockGameContext.Assets().AddAchievementSet(nId, nId, ra::util::String::Widen(sName));
auto* game = GetClient()->game;
return GetSubset(game, nId, rc_buffer_strcpy(&game->buffer, sName.c_str()));
}
rc_client_achievement_info_t* MockAchievement(uint32_t nId, const std::string& sTrigger)
{
return MockSubsetAchievement(nId, sTrigger, GetCoreSubset(GetClient()->game));
}
rc_client_achievement_info_t* MockLocalAchievement(uint32_t nId, const std::string& sTrigger)
{
return MockSubsetAchievement(nId, sTrigger, GetLocalSubset(GetClient()->game));
}
rc_client_achievement_info_t* MockSubsetAchievement(uint32_t nId, const std::string& sTrigger, rc_client_subset_info_t* pSubset)
{
auto* game = GetClient()->game;
auto* ach = AddAchievement(game, pSubset, nId, ra::util::String::Printf("Ach%u", nId).c_str());
rc_runtime_checksum(sTrigger.c_str(), ach->md5);
const auto nSize = rc_trigger_size(sTrigger.c_str());
void* trigger_buffer = rc_buffer_alloc(&game->buffer, nSize);
ach->trigger = rc_parse_trigger(trigger_buffer, sTrigger.c_str(), nullptr, 0);
ActivateAchievement(ach);
return ach;
}
void ActivateAchievement(rc_client_achievement_info_t* pAchievement) noexcept
{
pAchievement->public_.state = RC_CLIENT_ACHIEVEMENT_STATE_ACTIVE;
pAchievement->trigger->state = RC_TRIGGER_STATE_WAITING;
}
void DeactivateAchievement(rc_client_achievement_info_t* pAchievement) noexcept
{
pAchievement->public_.state = RC_CLIENT_ACHIEVEMENT_STATE_INACTIVE;
pAchievement->trigger->state = RC_TRIGGER_STATE_INACTIVE;
}
ra::data::models::AchievementModel* WrapAchievement(rc_client_achievement_info_t* pAchievement)
{
auto vmAchievement = std::make_unique<ra::data::models::AchievementModel>();
auto nCategory = ra::data::models::AssetCategory::Promoted;
if (pAchievement->public_.category == RC_CLIENT_ACHIEVEMENT_CATEGORY_UNPROMOTED)
{
nCategory = ra::data::models::AssetCategory::Unpromoted;
}
else
{
const auto* pSubset = GetClient()->game->subsets;
for (; pSubset; pSubset = pSubset->next)
{
for (uint32_t i = 0; i < pSubset->public_.num_achievements; ++i)
{
if (&pSubset->achievements[i] == pAchievement)
{
if (pSubset->public_.id == ra::data::models::AchievementSetModel::LocalId)
nCategory = ra::data::models::AssetCategory::Local;
vmAchievement->SetSubsetID(pSubset->public_.id);
break;
}
}
}
}
vmAchievement->InitializeFromPublishedAchievement(*pAchievement, "");
vmAchievement->SetLocalAchievementInfo(*pAchievement);
vmAchievement->SetCategory(nCategory);
vmAchievement->UpdateServerCheckpoint();
mockGameContext.Assets().Append(std::move(vmAchievement));
return mockGameContext.Assets().FindAchievement(pAchievement->public_.id);
}
rc_trigger_t* GetAchievementTrigger(uint32_t nId) const
{
const auto* pSubset = GetClient()->game->subsets;
for (; pSubset; pSubset = pSubset->next)
{
for (uint32_t i = 0; i < pSubset->public_.num_achievements; ++i)
{
if (pSubset->achievements[i].public_.id == nId)
return pSubset->achievements[i].trigger;
}
}
return nullptr;
}
rc_client_leaderboard_info_t* MockLeaderboard(uint32_t nId, const std::string& sDefinition)
{
rc_client_game_info_t* game = GetClient()->game;
auto* lboard = AddLeaderboard(game, GetCoreSubset(game), nId, ra::util::String::Printf("Leaderboard%u", nId).c_str());
const auto nSize = rc_lboard_size(sDefinition.c_str());
void* lboard_buffer = rc_buffer_alloc(&game->buffer, nSize);
lboard->lboard = rc_parse_lboard(lboard_buffer, sDefinition.c_str(), nullptr, 0);
ActivateLeaderboard(lboard);
return lboard;
}
rc_client_leaderboard_info_t* MockLocalLeaderboard(uint32_t nId, const std::string& sDefinition)
{
rc_client_game_info_t* game = GetClient()->game;
auto* lboard = AddLeaderboard(game, GetLocalSubset(game), nId, ra::util::String::Printf("Leaderboard%u", nId).c_str());
const auto nSize = rc_lboard_size(sDefinition.c_str());
void* lboard_buffer = rc_buffer_alloc(&game->buffer, nSize);
lboard->lboard = rc_parse_lboard(lboard_buffer, sDefinition.c_str(), nullptr, 0);
ActivateLeaderboard(lboard);
return lboard;
}
void ActivateLeaderboard(rc_client_leaderboard_info_t* pLeaderboard) noexcept
{
pLeaderboard->public_.state = RC_CLIENT_LEADERBOARD_STATE_ACTIVE;
pLeaderboard->lboard->state = RC_LBOARD_STATE_ACTIVE;
}
void DeactivateLeaderboard(rc_client_leaderboard_info_t* pLeaderboard) noexcept
{
pLeaderboard->public_.state = RC_CLIENT_LEADERBOARD_STATE_INACTIVE;
pLeaderboard->lboard->state = RC_LBOARD_STATE_INACTIVE;
}
ra::data::models::LeaderboardModel* WrapLeaderboard(rc_client_leaderboard_info_t* pLeaderboard)
{
auto nCategory = ra::data::models::AssetCategory::Promoted;
const auto* pSubset = GetClient()->game->subsets;
for (; pSubset; pSubset = pSubset->next)
{
for (uint32_t i = 0; i < pSubset->public_.num_leaderboards; ++i)
{
if (&pSubset->leaderboards[i] == pLeaderboard)
{
if (pSubset->public_.id == ra::data::models::AchievementSetModel::LocalId)
nCategory = ra::data::models::AssetCategory::Local;
break;
}
}
}
auto vmLeaderboard = std::make_unique<ra::data::models::LeaderboardModel>();
vmLeaderboard->InitializeFromPublishedLeaderboard(*pLeaderboard, "");
vmLeaderboard->SetLocalLeaderboardInfo(*pLeaderboard);
vmLeaderboard->SetCategory(nCategory);
vmLeaderboard->UpdateServerCheckpoint();
pLeaderboard->lboard->state = RC_LBOARD_STATE_ACTIVE; // syncing State sets this back to waiting
mockGameContext.Assets().Append(std::move(vmLeaderboard));
return mockGameContext.Assets().FindLeaderboard(pLeaderboard->public_.id);
}
void SyncToRuntime()
{
// toggling hardcore will reset the runtime with appropriately active achievements
const bool bHardcoreEnabled = rc_client_get_hardcore_enabled(GetClient());
rc_client_set_hardcore_enabled(GetClient(), !bHardcoreEnabled);
rc_client_set_hardcore_enabled(GetClient(), bHardcoreEnabled);
// one of the toggles will be to hardcore mode, which expects a reset - clear the flag
GetClient()->game->waiting_for_reset = false;
// MockAchievement generates triggers with their own memrefs - promote to common memref pool
auto& runtime = GetClient()->game->runtime;
rc_parse_state_t parse;
rc_init_parse_state(&parse, nullptr);
parse.memrefs = runtime.memrefs;
for (unsigned i = 0; i < runtime.trigger_count; ++i)
{
auto* memrefs = rc_trigger_get_memrefs(runtime.triggers[i].trigger);
if (memrefs)
{
for (unsigned j = 0; j < memrefs->memrefs.count; ++j)
{
const auto* old_memref = &memrefs->memrefs.items[j];
auto* memref = rc_alloc_memref(&parse, old_memref->address, old_memref->value.size);
auto* condition = runtime.triggers[i].trigger->requirement->conditions;
for (; condition; condition = condition->next)
{
if (rc_operand_is_memref(&condition->operand1) && condition->operand1.value.memref == old_memref)
condition->operand1.value.memref = memref;
if (rc_operand_is_memref(&condition->operand2) && condition->operand2.value.memref == old_memref)
condition->operand2.value.memref = memref;
}
}
}
}
}
GSL_SUPPRESS_TYPE1
void SaveProgressToString(std::string& sBuffer)
{
const size_t nSize = SaveProgressToBuffer(reinterpret_cast<uint8_t*>(sBuffer.data()), gsl::narrow_cast<int>(sBuffer.size()));
if (nSize > sBuffer.size())
{
sBuffer.resize(nSize);
SaveProgressToBuffer(reinterpret_cast<uint8_t*>(sBuffer.data()), gsl::narrow_cast<int>(nSize));
}
}
GSL_SUPPRESS_TYPE1
bool LoadProgressFromString(const std::string& sBuffer)
{
return LoadProgressFromBuffer(reinterpret_cast<const uint8_t*>(sBuffer.data()));
}
std::string GetRichPresenceOverride()
{
char buffer[256];
if (!GetClient()->callbacks.rich_presence_override(GetClient(), buffer, sizeof(buffer)))
buffer[0] = '\0';
return std::string(buffer);
}
void MockInspectingMemory(bool bInspectingMemory)
{
mockWindowManager.MemoryBookmarks.SetIsVisible(bInspectingMemory);
}
private:
GSL_SUPPRESS_CON3
static void CaptureEventHandler(const rc_client_event_t* pEvent, rc_client_t* pClient)
{
auto* harness = static_cast<AchievementRuntimeHarness*>(rc_client_get_userdata(pClient));
Expects(harness != nullptr);
harness->m_vEvents.push_back(*pEvent);
}
std::vector<rc_client_event_t> m_vEvents;
static rc_client_subset_info_t* GetSubset(rc_client_game_info_t* game, uint32_t subset_id, const char* name) noexcept
{
rc_client_subset_info_t *subset = game->subsets, **next = &game->subsets;
for (; subset; subset = subset->next)
{
if (subset->public_.id == subset_id)
return subset;
next = &subset->next;
}
subset = static_cast<rc_client_subset_info_t*>(rc_buffer_alloc(&game->buffer, sizeof(rc_client_subset_info_t)));
memset(subset, 0, sizeof(*subset));
subset->public_.id = subset_id;
strcpy_s(subset->public_.badge_name, sizeof(subset->public_.badge_name), game->public_.badge_name);
subset->public_.title = name;
subset->active = 1;
*next = subset;
return subset;
}
static rc_client_subset_info_t* GetCoreSubset(rc_client_game_info_t* game) noexcept
{
return GetSubset(game, game->public_.id, game->public_.title);
}
static rc_client_subset_info_t* GetLocalSubset(rc_client_game_info_t* game) noexcept
{
return GetSubset(game, ra::data::models::AchievementSetModel::LocalId, "Local");
}
static rc_client_achievement_info_t* AddAchievement(rc_client_game_info_t* game, rc_client_subset_info_t* subset,
uint32_t nId, const char* sTitle)
{
if (subset->public_.num_achievements % 8 == 0)
{
const uint32_t new_count = subset->public_.num_achievements + 8;
rc_client_achievement_info_t* new_achievements = static_cast<rc_client_achievement_info_t*>(rc_buffer_alloc(
&game->buffer, sizeof(rc_client_achievement_info_t) * new_count));
if (subset->public_.num_achievements > 0)
{
memcpy(new_achievements, subset->achievements,
sizeof(rc_client_achievement_info_t) * subset->public_.num_achievements);
}
subset->achievements = new_achievements;
}
rc_client_achievement_info_t* achievement = &subset->achievements[subset->public_.num_achievements++];
memset(achievement, 0, sizeof(*achievement));
achievement->public_.id = nId;
if (sTitle)
{
achievement->public_.title = rc_buffer_strcpy(&game->buffer, sTitle);
}
else
{
const std::string sGeneratedTitle = ra::util::String::Printf("Achievement %u", nId);
achievement->public_.title = rc_buffer_strcpy(&game->buffer, sGeneratedTitle.c_str());
}
const std::string sGeneratedDescripton = ra::util::String::Printf("Description %u", nId);
achievement->public_.description = rc_buffer_strcpy(&game->buffer, sGeneratedDescripton.c_str());
achievement->public_.category = RC_CLIENT_ACHIEVEMENT_CATEGORY_PROMOTED;
achievement->public_.state = RC_CLIENT_ACHIEVEMENT_STATE_ACTIVE;
achievement->public_.points = 5;
return achievement;
}
static rc_client_leaderboard_info_t* AddLeaderboard(rc_client_game_info_t* game, rc_client_subset_info_t* subset,
uint32_t nId, const char* sTitle)
{
if (subset->public_.num_leaderboards % 8 == 0)
{
const uint32_t new_count = subset->public_.num_leaderboards + 8;
rc_client_leaderboard_info_t* new_leaderboards = static_cast<rc_client_leaderboard_info_t*>(rc_buffer_alloc(
&game->buffer, sizeof(rc_client_leaderboard_info_t) * new_count));
if (subset->public_.num_leaderboards > 0)
{
memcpy(new_leaderboards, subset->leaderboards,
sizeof(rc_client_leaderboard_info_t) * subset->public_.num_leaderboards);
}
subset->leaderboards = new_leaderboards;
}
rc_client_leaderboard_info_t* leaderboard = &subset->leaderboards[subset->public_.num_leaderboards++];
memset(leaderboard, 0, sizeof(*leaderboard));
leaderboard->public_.id = nId;
if (sTitle)
{
leaderboard->public_.title = rc_buffer_strcpy(&game->buffer, sTitle);
}
else
{
const std::string sGeneratedTitle = ra::util::String::Printf("Leaderboard %u", nId);
leaderboard->public_.title = rc_buffer_strcpy(&game->buffer, sGeneratedTitle.c_str());
}
const std::string sGeneratedDescripton = ra::util::String::Printf("Description %u", nId);
leaderboard->public_.description = rc_buffer_strcpy(&game->buffer, sGeneratedDescripton.c_str());
leaderboard->public_.state = RC_CLIENT_LEADERBOARD_STATE_ACTIVE;
return leaderboard;
}
ra::services::ServiceLocator::ServiceOverride<ra::services::AchievementRuntime> m_Override;
rc_client_event_handler_t m_fRealEventHandler;
};
TEST_CLASS(AchievementRuntime_Tests)
{
public:
TEST_METHOD(TestSyncAssetsModifiedCoreAchievement)
{
AchievementRuntimeHarness runtime;
auto* pAchievement = runtime.MockAchievement(12345U, "0xH0000=1");
pAchievement->public_.category = gsl::narrow_cast<uint8_t>(ra::etoi(ra::data::models::AssetCategory::Promoted));
pAchievement->public_.title = "Achievement Name";
pAchievement->public_.description = "Do something cool";
pAchievement->public_.points = 25;
auto* pSubset = runtime.GetClient()->game->subsets;
Expects(pSubset != nullptr);
pSubset->achievements = pAchievement;
pSubset->public_.num_achievements = 1;
auto vmNewAchievement = std::make_unique<ra::data::models::AchievementModel>();
vmNewAchievement->InitializeFromPublishedAchievement(*pAchievement, "0xH0000=1");
vmNewAchievement->SetLocalAchievementInfo(*pAchievement);
vmNewAchievement->SetSubsetID(1U);
const auto& vmAchievement = reinterpret_cast<ra::data::models::AchievementModel&>(runtime.mockGameContext.Assets().Append(std::move(vmNewAchievement)));
// SyncAssets should generate a new core subset with the merged achievement
runtime.mockGameContext.Assets().SyncAssetsToRuntime();
pSubset = runtime.GetClient()->game->subsets;
Expects(pSubset != nullptr);
Assert::AreEqual("Game Title", pSubset->public_.title);
Assert::AreEqual(1U, pSubset->public_.id);
Assert::AreEqual("012345", pSubset->public_.badge_name);
Assert::AreEqual(1U, pSubset->public_.num_achievements);
Assert::IsTrue(pSubset->active);
Assert::IsNotNull(pSubset->next); // there will be an inactive local subset
const auto* pOriginalTrigger = pSubset->achievements->trigger;
Assert::AreEqual(std::wstring(L"Achievement Name"), vmAchievement.GetName());
Assert::AreEqual(std::wstring(L"Do something cool"), vmAchievement.GetDescription());
Assert::AreEqual(25, vmAchievement.GetPoints());
Assert::AreEqual(12345U, vmAchievement.GetID());
Assert::IsNotNull(pAchievement->trigger);
// directly modifying the achievement trigger should rebuild the underlying trigger
runtime.mockGameContext.Assets().FindAchievement(12345U)->SetTrigger("0xH0000=99"); // force memref allocation
runtime.mockGameContext.Assets().FindAchievement(12345U)->SetTrigger("0xH0000=2"); // no memref allocation, can be freed
const auto* pNewTrigger = pSubset->achievements->trigger;
Assert::AreNotEqual(static_cast<const void*>(pOriginalTrigger), static_cast<const void*>(pNewTrigger));
Assert::AreEqual(2U, pNewTrigger->requirement->conditions->operand2.value.num);
pSubset = runtime.GetClient()->game->subsets;
const auto* pNewerTrigger = pSubset->achievements->trigger;
Assert::AreEqual(static_cast<const void*>(pNewerTrigger), static_cast<const void*>(pNewTrigger));
Assert::IsNull(pNewerTrigger->alternative); // simple check to make sure memory is valid - debug build will fill freed memory with 0xDD
// revert to the original definition - original trigger should be used
runtime.mockGameContext.Assets().FindAchievement(12345U)->RestoreServerCheckpoint();
pSubset = runtime.GetClient()->game->subsets;
const auto* pNewestTrigger = pSubset->achievements->trigger;
Assert::AreNotEqual(static_cast<const void*>(pNewerTrigger), static_cast<const void*>(pNewestTrigger));
Assert::AreEqual(static_cast<const void*>(pOriginalTrigger), static_cast<const void*>(pNewestTrigger));
}
TEST_METHOD(TestDoFrameTriggerAchievement)
{
std::array<unsigned char, 1> memory{ 0x00 };
AchievementRuntimeHarness runtime;
runtime.mockEmulatorMemoryContext.MockMemory(memory);
runtime.MockAchievement(6U, "0xH0000=1");
// expect no events for untriggered achievement
runtime.DoFrame();
Assert::AreEqual({ 0U }, runtime.GetEventCount());
// now trigger it
memory.at(0) = 1;
runtime.DoFrame();
Assert::AreEqual({ 1U }, runtime.GetEventCount());
runtime.AssertEvent(RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED, 6U);
runtime.ResetEvents();
// triggered achievement should not continue to trigger
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
}
TEST_METHOD(TestDoFrameTriggerAchievementWhilePaused)
{
std::array<unsigned char, 1> memory{ 0x00 };
AchievementRuntimeHarness runtime;
runtime.mockEmulatorMemoryContext.MockMemory(memory);
runtime.MockAchievement(6U, "0xH0000=1");
runtime.SetPaused(true);
Assert::IsTrue(runtime.IsPaused());
// runtime is paused and achievement will be ignored as long as its true
for (int i = 0; i < 10; i++)
{
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
}
runtime.SetPaused(false);
Assert::IsFalse(runtime.IsPaused());
// expect no events for untriggered achievement
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
// pause runtime and trigger achievement
runtime.SetPaused(true);
Assert::IsTrue(runtime.IsPaused());
memory.at(0) = 1;
// runtime is paused and achievement will be ignored as long as its paused
for (int i = 0; i < 10; i++)
{
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
}
// unpausing the runtime does not explicitly process a frame
runtime.SetPaused(false);
Assert::IsFalse(runtime.IsPaused());
Assert::AreEqual({0U}, runtime.GetEventCount());
// now achievement should trigger
runtime.DoFrame();
Assert::AreEqual({1U}, runtime.GetEventCount());
runtime.AssertEvent(RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED, 6U);
runtime.ResetEvents();
}
TEST_METHOD(TestDoFrameReactivateAchievementWhilePaused)
{
std::array<unsigned char, 1> memory{0x00};
AchievementRuntimeHarness runtime;
runtime.mockEmulatorMemoryContext.MockMemory(memory);
auto* pAchievement = runtime.MockAchievement(6U, "0xH0000=1");
// expect no events for untriggered achievement
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
// trigger achievement
memory.at(0) = 1;
runtime.DoFrame();
Assert::AreEqual({1U}, runtime.GetEventCount());
runtime.AssertEvent(RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED, 6U);
runtime.ResetEvents();
// pause runtime and reactivate the achievement - expect no events
runtime.SetPaused(true);
runtime.ActivateAchievement(pAchievement);
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
// unpause runtime - expect no events - achievement is waiting
runtime.SetPaused(false);
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual((int)RC_TRIGGER_STATE_WAITING, (int)pAchievement->trigger->state);
// adjust memory so achievement will transition to active
memory.at(0) = 0;
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual((int)RC_TRIGGER_STATE_ACTIVE, (int)pAchievement->trigger->state);
// pause runtime and update memory so achievement would trigger - expect no events
runtime.SetPaused(true);
memory.at(0) = 1;
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
// unpause runtime - expect event
runtime.SetPaused(false);
runtime.DoFrame();
Assert::AreEqual({1U}, runtime.GetEventCount());
runtime.AssertEvent(RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED, 6U);
}
TEST_METHOD(TestDoFrameMeasuredAchievement)
{
std::array<unsigned char, 5> memory{0x00, 0x12, 0x34, 0xAB, 0x56};
AchievementRuntimeHarness runtime;
runtime.mockEmulatorMemoryContext.MockMemory(memory);
runtime.MockAchievement(4U, "M:0xH0002=100");
// value did not change, expect no event from initialization
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
// change value, expect notification
memory.at(2) = 75;
runtime.DoFrame();
Assert::AreEqual({1U}, runtime.GetEventCount());
runtime.AssertEvent(RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_SHOW, 4U);
runtime.ResetEvents();
// watch for notification, no change, no notification
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
// change value, expect notification
memory.at(2) = 78;
runtime.DoFrame();
Assert::AreEqual({1U}, runtime.GetEventCount());
runtime.AssertEvent(RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_UPDATE, 4U);
runtime.ResetEvents();
// trigger achievement, expect trigger notification, but not progress notification
memory.at(2) = 100;
runtime.DoFrame();
Assert::AreEqual({1U}, runtime.GetEventCount());
runtime.AssertEvent(RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED, 4U);
}
/*
TEST_METHOD(TestUpdateAchievementId)
{
std::array<unsigned char, 1> memory{ 0x00 };
AchievementRuntimeHarness runtime;
runtime.mockEmulatorMemoryContext.MockMemory(memory);
auto* sTrigger = "0xH0000=1";
// achievement not active, should not raise events
std::vector<AchievementRuntime::Change> vChanges;
runtime.Process(vChanges);
Assert::AreEqual({ 0U }, vChanges.size());
// make sure the trigger is registered correctly
runtime.ActivateAchievement(110000006U, sTrigger);
auto* pTrigger = runtime.GetAchievementTrigger(110000006U);
Assert::IsNotNull(pTrigger);
// update the ID (this simulates uploading a local achievement)
runtime.UpdateAchievementId(110000006U, 99001U);
auto const* pTrigger2 = runtime.GetAchievementTrigger(99001U);
Assert::IsTrue(pTrigger == pTrigger2);
pTrigger = runtime.GetAchievementTrigger(110000006U);
Assert::IsNull(pTrigger);
// first Process call will switch the state from Waiting to Active
runtime.Process(vChanges);
Assert::AreEqual({ 1U }, vChanges.size());
AssertChange(vChanges, AchievementRuntime::ChangeType::AchievementActivated, 99001U);
vChanges.clear();
// now that it's active, we can trigger it
memory.at(0) = 1;
runtime.Process(vChanges);
Assert::AreEqual({ 1U }, vChanges.size());
AssertChange(vChanges, AchievementRuntime::ChangeType::AchievementTriggered, 99001U);
}
*/
static rc_condition_t* GetCondition(const AchievementRuntimeHarness& harness, ra::AchievementID nId, int nGroup, int nCond)
{
rc_trigger_t* pTrigger = harness.GetAchievementTrigger(nId);
rc_condset_t* pCondset = pTrigger->requirement;
if (nGroup > 1)
{
pCondset = pTrigger->alternative;
while (pCondset && --nGroup > 0)
pCondset = pCondset->next;
}
if (!pCondset)
return nullptr;
rc_condition_t* pCondition = pCondset->conditions;
while (pCondition && nCond-- > 0)
pCondition = pCondition->next;
return pCondition;
}
static void SetConditionHitCount(const AchievementRuntimeHarness& harness, ra::AchievementID nId, int nGroup, int nCond, int nHits)
{
rc_condition_t* pCond = GetCondition(harness, nId, nGroup, nCond);
Assert::IsNotNull(pCond);
pCond->current_hits = ra::to_unsigned(nHits);
}
static void AssertConditionHitCount(const AchievementRuntimeHarness& harness, ra::AchievementID nId, int nGroup, int nCond, int nHits)
{
const rc_condition_t* pCond = GetCondition(harness, nId, nGroup, nCond);
Assert::IsNotNull(pCond);
Assert::AreEqual(pCond->current_hits, ra::to_unsigned(nHits));
}
TEST_METHOD(TestPersistProgressFile)
{
AchievementRuntimeHarness runtime;
auto* pAch3 = runtime.MockAchievement(3U, "1=1.10.");
auto* pAch5 = runtime.MockAchievement(5U, "1=1.2.");
runtime.SyncToRuntime();
SetConditionHitCount(runtime, 3U, 0, 0, 2);
SetConditionHitCount(runtime, 5U, 0, 0, 2);
runtime.SaveProgressToFile("test.sav");
// inactive achievements aren't updated from the persisted data
runtime.DeactivateAchievement(pAch3);
runtime.DeactivateAchievement(pAch5);
SetConditionHitCount(runtime, 3U, 0, 0, 1);
SetConditionHitCount(runtime, 5U, 0, 0, 1);
runtime.LoadProgressFromFile("test.sav");
AssertConditionHitCount(runtime, 3U, 0, 0, 1);
AssertConditionHitCount(runtime, 5U, 0, 0, 1);
// activate one achievement, save, activate other, restore - only first should be restored,
// second should be reset because it wasn't captured.
runtime.ActivateAchievement(pAch3);
SetConditionHitCount(runtime, 3U, 0, 0, 1);
SetConditionHitCount(runtime, 5U, 0, 0, 1);
runtime.SaveProgressToFile("test.sav");
runtime.ActivateAchievement(pAch5);
SetConditionHitCount(runtime, 3U, 0, 0, 2);
SetConditionHitCount(runtime, 5U, 0, 0, 2);
runtime.LoadProgressFromFile("test.sav");
AssertConditionHitCount(runtime, 3U, 0, 0, 1);
AssertConditionHitCount(runtime, 5U, 0, 0, 0);
// both active, save and restore should reset both
SetConditionHitCount(runtime, 3U, 0, 0, 1);
SetConditionHitCount(runtime, 5U, 0, 0, 1);
runtime.SaveProgressToFile("test.sav");
SetConditionHitCount(runtime, 3U, 0, 0, 2);
SetConditionHitCount(runtime, 5U, 0, 0, 2);
runtime.LoadProgressFromFile("test.sav");
AssertConditionHitCount(runtime, 3U, 0, 0, 1);
AssertConditionHitCount(runtime, 5U, 0, 0, 1);
// second no longer active, file contains data that should be ignored
SetConditionHitCount(runtime, 3U, 0, 0, 6);
SetConditionHitCount(runtime, 5U, 0, 0, 6);
runtime.DeactivateAchievement(pAch5);
runtime.LoadProgressFromFile("test.sav");
AssertConditionHitCount(runtime, 3U, 0, 0, 1);
AssertConditionHitCount(runtime, 5U, 0, 0, 6);
// if the definition changes (different md5), the hits should be reset instead of remembered
runtime.GetClient()->game->runtime.triggers[0].md5[0]++;
SetConditionHitCount(runtime, 3U, 0, 0, 6);
runtime.ActivateAchievement(pAch3);
runtime.LoadProgressFromFile("test.sav");
AssertConditionHitCount(runtime, 3U, 0, 0, 0);
}
TEST_METHOD(TestPersistProgressBuffer)
{
AchievementRuntimeHarness runtime;
auto* pAch3 = runtime.MockAchievement(3U, "1=1.10.");
auto* pAch5 = runtime.MockAchievement(5U, "1=1.2.");
runtime.SyncToRuntime();
SetConditionHitCount(runtime, 3U, 0, 0, 2);
SetConditionHitCount(runtime, 5U, 0, 0, 2);
std::string sBuffer;
runtime.SaveProgressToString(sBuffer);
// inactive achievements aren't updated from the persisted data
runtime.DeactivateAchievement(pAch3);
runtime.DeactivateAchievement(pAch5);
SetConditionHitCount(runtime, 3U, 0, 0, 1);
SetConditionHitCount(runtime, 5U, 0, 0, 1);
runtime.LoadProgressFromString(sBuffer);
AssertConditionHitCount(runtime, 3U, 0, 0, 1);
AssertConditionHitCount(runtime, 5U, 0, 0, 1);
// activate one achievement, save, activate other, restore - only first should be restored,
// second should be reset because it wasn't captured.
runtime.ActivateAchievement(pAch3);
SetConditionHitCount(runtime, 3U, 0, 0, 1);
SetConditionHitCount(runtime, 5U, 0, 0, 1);
runtime.SaveProgressToString(sBuffer);
runtime.ActivateAchievement(pAch5);
SetConditionHitCount(runtime, 3U, 0, 0, 2);
SetConditionHitCount(runtime, 5U, 0, 0, 2);
runtime.LoadProgressFromString(sBuffer);
AssertConditionHitCount(runtime, 3U, 0, 0, 1);
AssertConditionHitCount(runtime, 5U, 0, 0, 0);
// both active, save and restore should reset both
SetConditionHitCount(runtime, 3U, 0, 0, 1);
SetConditionHitCount(runtime, 5U, 0, 0, 1);
runtime.SaveProgressToString(sBuffer);
SetConditionHitCount(runtime, 3U, 0, 0, 2);
SetConditionHitCount(runtime, 5U, 0, 0, 2);
runtime.LoadProgressFromString(sBuffer);
AssertConditionHitCount(runtime, 3U, 0, 0, 1);
AssertConditionHitCount(runtime, 5U, 0, 0, 1);
// second no longer active, file contains data that should be ignored
SetConditionHitCount(runtime, 3U, 0, 0, 6);
SetConditionHitCount(runtime, 5U, 0, 0, 6);
runtime.DeactivateAchievement(pAch5);
runtime.LoadProgressFromString(sBuffer);
AssertConditionHitCount(runtime, 3U, 0, 0, 1);
AssertConditionHitCount(runtime, 5U, 0, 0, 6);
// if the definition changes (different md5), the hits should be reset instead of remembered
runtime.GetClient()->game->runtime.triggers[0].md5[0]++;
SetConditionHitCount(runtime, 3U, 0, 0, 6);
runtime.ActivateAchievement(pAch3);
runtime.LoadProgressFromString(sBuffer);
AssertConditionHitCount(runtime, 3U, 0, 0, 0);
}
TEST_METHOD(TestPersistProgressMemory)
{
AchievementRuntimeHarness runtime;
runtime.MockAchievement(9U, "0xH1234=1_0xX1234>d0xX1234");
runtime.SyncToRuntime();
auto pMemRefs = runtime.GetClient()->game->runtime.memrefs;
SetConditionHitCount(runtime, 9U, 0, 0, 2);
SetConditionHitCount(runtime, 9U, 0, 1, 0);
auto pMemRef = &pMemRefs->memrefs.items[0]; // 0xH1234
pMemRef->value.value = 0x02;
pMemRef->value.changed = 0;
pMemRef->value.prior = 0x03;
pMemRef = &pMemRefs->memrefs.items[1]; // 0xX1234
pMemRef->value.value = 0x020000;
pMemRef->value.changed = 1;
pMemRef->value.prior = 0x040000;
runtime.SaveProgressToFile("test.sav");
// modify data so we can see if the persisted data is restored
pMemRef = &pMemRefs->memrefs.items[0];
pMemRef->value.value = 0;
pMemRef->value.changed = 0;
pMemRef->value.prior = 0;
pMemRef = &pMemRefs->memrefs.items[1];
pMemRef->value.value = 0;
pMemRef->value.changed = 0;
pMemRef->value.prior = 0;
SetConditionHitCount(runtime, 9U, 0, 0, 1);
SetConditionHitCount(runtime, 9U, 0, 1, 1);
// restore persisted data
runtime.LoadProgressFromFile("test.sav");
AssertConditionHitCount(runtime, 9U, 0, 0, 2);
AssertConditionHitCount(runtime, 9U, 0, 1, 0);
pMemRef = &pMemRefs->memrefs.items[0];
Assert::AreEqual(0x02U, pMemRef->value.value);
Assert::AreEqual(0, (int)pMemRef->value.changed);
Assert::AreEqual(0x03U, pMemRef->value.prior);
pMemRef = &pMemRefs->memrefs.items[1];
Assert::AreEqual(0x020000U, pMemRef->value.value);
Assert::AreEqual(1, (int)pMemRef->value.changed);
Assert::AreEqual(0x040000U, pMemRef->value.prior);
}
TEST_METHOD(TestLoadProgressV1)
{
AchievementRuntimeHarness runtime;
runtime.WrapAchievement(runtime.MockAchievement(3U, "1=1.10."))->SetTrigger("1=1.10.");
runtime.WrapAchievement(runtime.MockAchievement(5U, "1=1.2."))->SetTrigger("1=1.2.");
runtime.SyncToRuntime();
SetConditionHitCount(runtime, 3U, 0, 0, 2);
SetConditionHitCount(runtime, 5U, 0, 0, 2);
runtime.mockFileSystem.MockFile(L"test.sav.rap", "3:1:1:0:0:0:0:6e2301982f40d1a3f311cdb063f57e2f:4f52856e145d7cb05822e8a9675b086b:5:1:1:0:0:0:0:0e9aec1797ad6ba861a4b1e0c7f6d2ab:dd9e5fc6020e728b8c9231d5a5c904d5:");
runtime.LoadProgressFromFile("test.sav");
AssertConditionHitCount(runtime, 3U, 0, 0, 1);
AssertConditionHitCount(runtime, 5U, 0, 0, 1);
}
TEST_METHOD(TestDoFrameActivateLeaderboard)
{
std::array<unsigned char, 5> memory{ 0x00, 0x12, 0x34, 0xAB, 0x56 };
AchievementRuntimeHarness runtime;
runtime.mockEmulatorMemoryContext.MockMemory(memory);
auto* pLeaderboard = runtime.MockLeaderboard(6U, "STA:0xH00=1::CAN:0xH00=2::SUB:0xH00=3::VAL:0xH02");
// expect no events for inactive leaderboard
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
// leaderboard cancel condition should not be captured until it's been started
memory.at(0) = 2;
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
// leaderboard start condition should notify the start with initial value
memory.at(0) = 1;
runtime.DoFrame();
Assert::AreEqual({2U}, runtime.GetEventCount());
runtime.AssertEvent(RC_CLIENT_EVENT_LEADERBOARD_STARTED, 6U);
runtime.AssertEvent(RC_CLIENT_EVENT_LEADERBOARD_TRACKER_SHOW, 1U);
runtime.ResetEvents();
// still active leaderboard should not notify unless the value changes
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
memory.at(2) = 33;
runtime.DoFrame();
Assert::AreEqual({1U}, runtime.GetEventCount());
runtime.AssertEvent(RC_CLIENT_EVENT_LEADERBOARD_TRACKER_UPDATE, 1U);
runtime.ResetEvents();
// leaderboard cancel condition should notify
memory.at(0) = 2;
runtime.DoFrame();
Assert::AreEqual({2U}, runtime.GetEventCount());
runtime.AssertEvent(RC_CLIENT_EVENT_LEADERBOARD_FAILED, 6U);
runtime.AssertEvent(RC_CLIENT_EVENT_LEADERBOARD_TRACKER_HIDE, 1U);
runtime.ResetEvents();
// leaderboard submit condition should not trigger if leaderboard not started
memory.at(0) = 3;
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
// restart the leaderboard
memory.at(0) = 1;
runtime.DoFrame();
Assert::AreEqual({2U}, runtime.GetEventCount());
runtime.AssertEvent(RC_CLIENT_EVENT_LEADERBOARD_STARTED, 6U);
runtime.AssertEvent(RC_CLIENT_EVENT_LEADERBOARD_TRACKER_SHOW, 1U);
runtime.ResetEvents();
// leaderboard submit should trigger if leaderboard started
memory.at(0) = 3;
runtime.DoFrame();
Assert::AreEqual({2U}, runtime.GetEventCount());
runtime.AssertEvent(RC_CLIENT_EVENT_LEADERBOARD_SUBMITTED, 6U);
runtime.AssertEvent(RC_CLIENT_EVENT_LEADERBOARD_TRACKER_HIDE, 1U);
runtime.ResetEvents();
// leaderboard cancel condition should not trigger after submission
memory.at(0) = 2;
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
// restart the leaderboard
memory.at(0) = 1;
runtime.DoFrame();
Assert::AreEqual({2U}, runtime.GetEventCount());
runtime.AssertEvent(RC_CLIENT_EVENT_LEADERBOARD_STARTED, 6U);
runtime.AssertEvent(RC_CLIENT_EVENT_LEADERBOARD_TRACKER_SHOW, 1U);
runtime.ResetEvents();
// deactivated leaderboard should not trigger
runtime.DeactivateLeaderboard(pLeaderboard);
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
memory.at(0) = 2;
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
}
TEST_METHOD(TestMonitorAchievementPauseOnTrigger)
{
std::array<unsigned char, 5> memory{0x00, 0x12, 0x34, 0xAB, 0x56};
AchievementRuntimeHarness runtime;
runtime.mockEmulatorMemoryContext.MockMemory(memory);
auto* pAchievement = runtime.MockAchievement(6U, "0xH0000=1");
auto* vmAchievement = runtime.WrapAchievement(pAchievement);
// no trigger, no event
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumTriggeredTriggers());
// trigger, expect no notification because not watching for one
memory.at(0) = 1;
runtime.DoFrame();
Assert::AreEqual({1U}, runtime.GetEventCount()); // trigger event
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumTriggeredTriggers());
runtime.ResetEvents();
// reactivate, no event
memory.at(0) = 0;
vmAchievement->SetState(ra::data::models::AssetState::Active);
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumTriggeredTriggers());
// trigger, watch for notification
vmAchievement->SetPauseOnTrigger(true);
memory.at(0) = 1;
runtime.DoFrame();
Assert::AreEqual({1U}, runtime.GetEventCount());
runtime.ProcessCapturedEvents();
Assert::AreEqual({1U}, runtime.mockFrameEventQueue.NumTriggeredTriggers());
runtime.mockFrameEventQueue.Reset();
// already triggered, shouldn't get repeated notification
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumTriggeredTriggers());
}
TEST_METHOD(TestMonitorAchievementPauseOnReset)
{
std::array<unsigned char, 5> memory{0x00, 0x12, 0x34, 0xAB, 0x56};
AchievementRuntimeHarness runtime;
runtime.mockEmulatorMemoryContext.MockMemory(memory);
auto* pAchievement = runtime.MockAchievement(4U, "1=1.3._R:0xH0000=1");
auto* vmAchievement = runtime.WrapAchievement(pAchievement);
// HitCount should increase, but no trigger
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
Assert::AreEqual(true, pAchievement->trigger->has_hits != 0);
// trigger reset, expect no notification because not watching for one
memory.at(0) = 1;
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
// no notification if already reset
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
// disable reset, hitcount should increase, no notification
memory.at(0) = 0;
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
// trigger reset, watch for notification
vmAchievement->SetPauseOnReset(true);
memory.at(0) = 1;
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({1U}, runtime.mockFrameEventQueue.NumResetTriggers());
runtime.mockFrameEventQueue.Reset();
// already reset, shouldn't get repeated notification
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
}
TEST_METHOD(TestMonitorLeaderboardStartPauseOnReset)
{
std::array<unsigned char, 2> memory{ 0x00, 0x00 };
AchievementRuntimeHarness runtime;
runtime.mockEmulatorMemoryContext.MockMemory(memory);
auto* pLeaderboard = runtime.MockLeaderboard(6U,
"STA:0xH0000=0_0xH0000=9_R:0xH0001=1::SUB:0xH0000=1::CAN:0xH0000=2::VAL:0xH0000");
auto* vmLeaderboard = runtime.WrapLeaderboard(pLeaderboard);
// impossible start condition - but it should accumulate hits so we can detect a reset.
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
Assert::IsTrue(pLeaderboard->lboard->start.has_hits);
// reset should clear hits, but we're not watching for event yet
memory.at(1) = 1;
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
Assert::IsFalse(pLeaderboard->lboard->start.has_hits);
// start watching for event and allow hits to accumulate again
vmLeaderboard->SetPauseOnReset(ra::data::models::LeaderboardModel::LeaderboardParts::Start);
memory.at(1) = 0;
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
Assert::IsTrue(pLeaderboard->lboard->start.has_hits);
// reset should clear hits and raise event
memory.at(1) = 1;
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({1U}, runtime.mockFrameEventQueue.NumResetTriggers());
Assert::IsFalse(pLeaderboard->lboard->start.has_hits);
runtime.mockFrameEventQueue.Reset();
// persistent reset should not raise event again
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
Assert::IsFalse(pLeaderboard->lboard->start.has_hits);
}
TEST_METHOD(TestMonitorLeaderboardSubmitPauseOnReset)
{
std::array<unsigned char, 2> memory{0x00, 0x00};
AchievementRuntimeHarness runtime;
runtime.mockEmulatorMemoryContext.MockMemory(memory);
auto* pLeaderboard = runtime.MockLeaderboard(6U,
"STA:0xH0000=1::SUB:0xH0000=0_0xH0000=9_R:0xH0001=1::CAN:0xH0000=2::VAL:0xH0000");
auto* vmLeaderboard = runtime.WrapLeaderboard(pLeaderboard);
// accumulate hits on submit
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
Assert::IsTrue(pLeaderboard->lboard->submit.has_hits);
// reset should clear hits, but we're not watching for event yet
memory.at(1) = 1;
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
Assert::IsFalse(pLeaderboard->lboard->submit.has_hits);
// start watching for event and allow hits to accumulate again
vmLeaderboard->SetPauseOnReset(ra::data::models::LeaderboardModel::LeaderboardParts::Submit);
memory.at(1) = 0;
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
Assert::IsTrue(pLeaderboard->lboard->submit.has_hits);
// reset should clear hits and raise event
memory.at(1) = 1;
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({1U}, runtime.mockFrameEventQueue.NumResetTriggers());
Assert::IsFalse(pLeaderboard->lboard->submit.has_hits);
runtime.mockFrameEventQueue.Reset();
// persistent reset should not raise event again
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
Assert::IsFalse(pLeaderboard->lboard->submit.has_hits);
}
TEST_METHOD(TestMonitorLeaderboardCancelPauseOnReset)
{
std::array<unsigned char, 2> memory{0x00, 0x00};
AchievementRuntimeHarness runtime;
runtime.mockEmulatorMemoryContext.MockMemory(memory);
auto* pLeaderboard = runtime.MockLeaderboard(6U,
"STA:0xH0000=1::SUB:0xH0000=2::CAN:0xH0000=0_0xH0000=9_R:0xH0001=1::VAL:0xH0000");
auto* vmLeaderboard = runtime.WrapLeaderboard(pLeaderboard);
// accumulate hits on cancel
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
Assert::IsTrue(pLeaderboard->lboard->cancel.has_hits);
// reset should clear hits, but we're not watching for event yet
memory.at(1) = 1;
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
Assert::IsFalse(pLeaderboard->lboard->cancel.has_hits);
// start watching for event and allow hits to accumulate again
vmLeaderboard->SetPauseOnReset(ra::data::models::LeaderboardModel::LeaderboardParts::Cancel);
memory.at(1) = 0;
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
Assert::IsTrue(pLeaderboard->lboard->cancel.has_hits);
// reset should clear hits and raise event
memory.at(1) = 1;
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({1U}, runtime.mockFrameEventQueue.NumResetTriggers());
Assert::IsFalse(pLeaderboard->lboard->cancel.has_hits);
runtime.mockFrameEventQueue.Reset();
// persistent reset should not raise event again
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
Assert::IsFalse(pLeaderboard->lboard->cancel.has_hits);
}
TEST_METHOD(TestMonitorLeaderboardValuePauseOnReset)
{
std::array<unsigned char, 3> memory{0x00, 0x00, 0x00};
AchievementRuntimeHarness runtime;
runtime.mockEmulatorMemoryContext.MockMemory(memory);
auto* pLeaderboard = runtime.MockLeaderboard(6U,
"STA:0xH0000=0::SUB:0xH0000=2::CAN:0xH0000=3::VAL:R:0xH0001=1_M:0xH0002=0");
auto* vmLeaderboard = runtime.WrapLeaderboard(pLeaderboard);
// start the leaderboard so we can start counting hits on the value
runtime.DoFrame();
Assert::AreEqual({2U}, runtime.GetEventCount()); // started + tracker
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
Assert::AreEqual(1U, pLeaderboard->lboard->value.value.value);
runtime.ResetEvents();
// reset should clear hits, but we're not watching for event yet
memory.at(1) = 1;
runtime.DoFrame();
Assert::AreEqual({1U}, runtime.GetEventCount()); // tracker updated
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
Assert::AreEqual(0U, pLeaderboard->lboard->value.value.value);
runtime.ResetEvents();
// start watching for event and allow hits to accumulate again
vmLeaderboard->SetPauseOnReset(ra::data::models::LeaderboardModel::LeaderboardParts::Value);
memory.at(1) = 0;
runtime.DoFrame();
Assert::AreEqual({1U}, runtime.GetEventCount()); // tracker updated
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
Assert::AreEqual(1U, pLeaderboard->lboard->value.value.value);
runtime.ResetEvents();
// reset should clear hits and raise event
memory.at(1) = 1;
runtime.DoFrame();
Assert::AreEqual({1U}, runtime.GetEventCount()); // tracker updated
Assert::AreEqual({1U}, runtime.mockFrameEventQueue.NumResetTriggers());
Assert::AreEqual(0U, pLeaderboard->lboard->value.value.value);
runtime.mockFrameEventQueue.Reset();
runtime.ResetEvents();
// persistent reset should not raise event again
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
Assert::AreEqual(0U, pLeaderboard->lboard->value.value.value);
}
TEST_METHOD(TestMonitorLeaderboardStartPauseOnTrigger)
{
std::array<unsigned char, 2> memory{0x00, 0x00};
AchievementRuntimeHarness runtime;
runtime.mockEmulatorMemoryContext.MockMemory(memory);
auto* pLeaderboard = runtime.MockLeaderboard(6U,
"STA:0xH0000=1::SUB:0xH0000=2::CAN:0xH0000=3::VAL:0xH0001");
auto* vmLeaderboard = runtime.WrapLeaderboard(pLeaderboard);
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
// start the leaderboard, but we're not watching for event yet
memory.at(0) = 1;
runtime.DoFrame();
Assert::AreEqual({2U}, runtime.GetEventCount()); // started + tracker show
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
runtime.ResetEvents();
// cancel the leaderboard
memory.at(0) = 3;
runtime.DoFrame();
Assert::AreEqual({2U}, runtime.GetEventCount()); // canceled + tracker hide
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
runtime.ResetEvents();
// let leaderboard transition from waiting to active
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
// start watching for event and restart the leaderboard
vmLeaderboard->SetPauseOnTrigger(ra::data::models::LeaderboardModel::LeaderboardParts::Start);
memory.at(0) = 1;
runtime.DoFrame();
Assert::AreEqual({2U}, runtime.GetEventCount()); // started + tracker show
Assert::AreEqual({1U}, runtime.mockFrameEventQueue.NumTriggeredTriggers());
runtime.mockFrameEventQueue.Reset();
runtime.ResetEvents();
}
TEST_METHOD(TestMonitorLeaderboardSubmitPauseOnTrigger)
{
std::array<unsigned char, 2> memory{0x00, 0x00};
AchievementRuntimeHarness runtime;
runtime.mockEmulatorMemoryContext.MockMemory(memory);
auto* pLeaderboard = runtime.MockLeaderboard(6U,
"STA:0xH0000=1::SUB:0xH0000=2::CAN:0xH0000=3::VAL:0xH0001");
auto* vmLeaderboard = runtime.WrapLeaderboard(pLeaderboard);
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
// start the leaderboard
memory.at(0) = 1;
runtime.DoFrame();
Assert::AreEqual({2U}, runtime.GetEventCount()); // started + tracker show
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
runtime.ResetEvents();
// submit the leaderboard, but we're not watching for the event yet
memory.at(0) = 2;
runtime.DoFrame();
Assert::AreEqual({2U}, runtime.GetEventCount()); // submitted + tracker hide
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
runtime.ResetEvents();
// let leaderboard transition from waiting to active
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
// start watching for event and restart the leaderboard
vmLeaderboard->SetPauseOnTrigger(ra::data::models::LeaderboardModel::LeaderboardParts::Submit);
memory.at(0) = 1;
runtime.DoFrame();
Assert::AreEqual({2U}, runtime.GetEventCount()); // started + tracker show
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumTriggeredTriggers());
runtime.ResetEvents();
// submit the leaderboard, expect the event
memory.at(0) = 2;
runtime.DoFrame();
Assert::AreEqual({2U}, runtime.GetEventCount()); // submitted + tracker hide
Assert::AreEqual({1U}, runtime.mockFrameEventQueue.NumTriggeredTriggers());
}
TEST_METHOD(TestMonitorLeaderboardCancelPauseOnTrigger)
{
std::array<unsigned char, 2> memory{0x00, 0x00};
AchievementRuntimeHarness runtime;
runtime.mockEmulatorMemoryContext.MockMemory(memory);
auto* pLeaderboard = runtime.MockLeaderboard(6U, "STA:0xH0000=1::SUB:0xH0000=2::CAN:0xH0000=3::VAL:0xH0001");
auto* vmLeaderboard = runtime.WrapLeaderboard(pLeaderboard);
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
// start the leaderboard
memory.at(0) = 1;
runtime.DoFrame();
Assert::AreEqual({2U}, runtime.GetEventCount()); // started + tracker show
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
runtime.ResetEvents();
// cancel the leaderboard, but we're not watching for the event yet
memory.at(0) = 3;
runtime.DoFrame();
Assert::AreEqual({2U}, runtime.GetEventCount()); // canceled + tracker hide
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
runtime.ResetEvents();
// let leaderboard transition from waiting to active
runtime.DoFrame();
Assert::AreEqual({0U}, runtime.GetEventCount());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumResetTriggers());
// start watching for event and restart the leaderboard
vmLeaderboard->SetPauseOnTrigger(ra::data::models::LeaderboardModel::LeaderboardParts::Cancel);
memory.at(0) = 1;
runtime.DoFrame();
Assert::AreEqual({2U}, runtime.GetEventCount()); // started + tracker show
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumTriggeredTriggers());
runtime.ResetEvents();
// cancel the leaderboard, expect the event
memory.at(0) = 3;
runtime.DoFrame();
Assert::AreEqual({2U}, runtime.GetEventCount()); // submitted + tracker hide
Assert::AreEqual({1U}, runtime.mockFrameEventQueue.NumTriggeredTriggers());
}
TEST_METHOD(TestDoFrameInvalidAddressDisablesAchievement)
{
std::array<unsigned char, 2> memory{0x00, 0x00};
AchievementRuntimeHarness runtime;
runtime.mockEmulatorMemoryContext.MockMemory(memory);
auto* pAch6 = runtime.MockAchievement(6U, "0xH0004=1");
runtime.SyncToRuntime();
auto* vmAch6 = runtime.WrapAchievement(pAch6);
Assert::AreEqual(static_cast<uint8_t>(RC_CLIENT_ACHIEVEMENT_STATE_ACTIVE), pAch6->public_.state);
Assert::AreEqual(ra::data::models::AssetState::Waiting, vmAch6->GetState());
runtime.DoFrame();
Assert::AreEqual(static_cast<uint8_t>(RC_CLIENT_ACHIEVEMENT_STATE_DISABLED), pAch6->public_.state);
// model state isn't synced until DoFrame is called.
vmAch6->DoFrame();
Assert::AreEqual(ra::data::models::AssetState::Disabled, vmAch6->GetState());
}
TEST_METHOD(TestHandleAchievementTriggeredEvent)
{
AchievementRuntimeHarness runtime;
auto* pAch6 = runtime.MockAchievement(6U, "0xH0000=1");
pAch6->public_.rarity = 23.45f;
pAch6->public_.rarity_hardcore = 12.34f;
memcpy(pAch6->public_.badge_name, "012345", 7);
auto* vmAch6 = runtime.WrapAchievement(pAch6);
runtime.mockGameContext.SetRichPresenceDisplayString(L"Titles");
runtime.mockGameContext.Assets().FindRichPresence()->Activate();
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, false);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::AchievementTriggered,
ra::ui::viewmodels::PopupLocation::BottomLeft);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED;
event.achievement = &pAch6->public_;
runtime.RaiseEvent(event);
Assert::AreEqual(ra::data::models::AssetState::Triggered, vmAch6->GetState());
Assert::AreEqual(std::wstring(L"Titles"), vmAch6->GetUnlockRichPresence());
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::AchievementTriggered, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Achievement Unlocked - 23.45%"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Ach6 (5)"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Description 6"), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::Badge, pPopup->GetImage().Type());
Assert::AreEqual(std::string("012345"), pPopup->GetImage().Name());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumTriggeredTriggers());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\unlock.wav"));
}
TEST_METHOD(TestHandleAchievementTriggeredEventHardcore)
{
AchievementRuntimeHarness runtime;
auto* pAch6 = runtime.MockAchievement(6U, "0xH0000=1");
pAch6->public_.rarity = 23.45f;
pAch6->public_.rarity_hardcore = 12.34f;
memcpy(pAch6->public_.badge_name, "012345", 7);
auto* vmAch6 = runtime.WrapAchievement(pAch6);
runtime.mockGameContext.SetRichPresenceDisplayString(L"Titles");
runtime.mockGameContext.Assets().FindRichPresence()->Activate();
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::AchievementTriggered,
ra::ui::viewmodels::PopupLocation::BottomLeft);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED;
event.achievement = &pAch6->public_;
runtime.RaiseEvent(event);
Assert::AreEqual(ra::data::models::AssetState::Triggered, vmAch6->GetState());
Assert::AreEqual(std::wstring(L"Titles"), vmAch6->GetUnlockRichPresence());
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::AchievementTriggered, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Achievement Unlocked - 12.34%"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Ach6 (5)"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Description 6"), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::Badge, pPopup->GetImage().Type());
Assert::AreEqual(std::string("012345"), pPopup->GetImage().Name());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumTriggeredTriggers());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\unlock.wav"));
}
TEST_METHOD(TestHandleAchievementTriggeredEventHardcoreRare)
{
AchievementRuntimeHarness runtime;
auto* pAch6 = runtime.MockAchievement(6U, "0xH0000=1");
pAch6->public_.rarity = 23.45f;
pAch6->public_.rarity_hardcore = 9.87f;
memcpy(pAch6->public_.badge_name, "012345", 7);
auto* vmAch6 = runtime.WrapAchievement(pAch6);
runtime.mockGameContext.SetRichPresenceDisplayString(L"Titles");
runtime.mockGameContext.Assets().FindRichPresence()->Activate();
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::AchievementTriggered,
ra::ui::viewmodels::PopupLocation::BottomLeft);
runtime.mockFileSystem.MockFileSize(runtime.mockFileSystem.BaseDirectory() + L"Overlay\\rareunlock.wav", 12345);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED;
event.achievement = &pAch6->public_;
runtime.RaiseEvent(event);
Assert::AreEqual(ra::data::models::AssetState::Triggered, vmAch6->GetState());
Assert::AreEqual(std::wstring(L"Titles"), vmAch6->GetUnlockRichPresence());
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::AchievementTriggered, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Rare Achievement Unlocked - 9.87%"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Ach6 (5)"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Description 6"), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::Badge, pPopup->GetImage().Type());
Assert::AreEqual(std::string("012345"), pPopup->GetImage().Name());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumTriggeredTriggers());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\rareunlock.wav"));
}
TEST_METHOD(TestHandleAchievementTriggeredEventNoRichPresence)
{
AchievementRuntimeHarness runtime;
auto* pAch6 = runtime.MockAchievement(6U, "0xH0000=1");
memcpy(pAch6->public_.badge_name, "012345", 7);
pAch6->public_.rarity = 23.45f;
pAch6->public_.rarity_hardcore = 12.34f;
auto* vmAch6 = runtime.WrapAchievement(pAch6);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::AchievementTriggered,
ra::ui::viewmodels::PopupLocation::BottomLeft);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED;
event.achievement = &pAch6->public_;
runtime.RaiseEvent(event);
Assert::AreEqual(ra::data::models::AssetState::Triggered, vmAch6->GetState());
Assert::AreEqual(std::wstring(L""), vmAch6->GetUnlockRichPresence());
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::AchievementTriggered, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Achievement Unlocked - 23.45%"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Ach6 (5)"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Description 6"), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::Badge, pPopup->GetImage().Type());
Assert::AreEqual(std::string("012345"), pPopup->GetImage().Name());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumTriggeredTriggers());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\unlock.wav"));
}
TEST_METHOD(TestHandleAchievementTriggeredEventLocal)
{
AchievementRuntimeHarness runtime;
auto* pAch6 = runtime.MockLocalAchievement(6U, "0xH0000=1");
memcpy(pAch6->public_.badge_name, "012345", 7);
auto* vmAch6 = runtime.WrapAchievement(pAch6);
runtime.mockGameContext.SetRichPresenceDisplayString(L"Titles");
runtime.mockGameContext.Assets().FindRichPresence()->Activate();
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::AchievementTriggered,
ra::ui::viewmodels::PopupLocation::BottomLeft);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED;
event.achievement = &pAch6->public_;
runtime.RaiseEvent(event);
Assert::AreEqual(ra::data::models::AssetState::Triggered, vmAch6->GetState());
Assert::AreEqual(std::wstring(L"Titles"), vmAch6->GetUnlockRichPresence());
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::AchievementTriggered, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Local Achievement Unlocked"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Ach6 (5)"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Description 6"), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::Badge, pPopup->GetImage().Type());
Assert::AreEqual(std::string("012345"), pPopup->GetImage().Name());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumTriggeredTriggers());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\unlock.wav"));
}
TEST_METHOD(TestHandleAchievementTriggeredEventUnpromoted)
{
AchievementRuntimeHarness runtime;
auto* pAch6 = runtime.MockAchievement(6U, "0xH0000=1");
pAch6->public_.category = RC_CLIENT_ACHIEVEMENT_CATEGORY_UNPROMOTED;
memcpy(pAch6->public_.badge_name, "012345", 7);
auto* vmAch6 = runtime.WrapAchievement(pAch6);
runtime.mockGameContext.SetRichPresenceDisplayString(L"Titles");
runtime.mockGameContext.Assets().FindRichPresence()->Activate();
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::AchievementTriggered,
ra::ui::viewmodels::PopupLocation::BottomLeft);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED;
event.achievement = &pAch6->public_;
runtime.RaiseEvent(event);
Assert::AreEqual(ra::data::models::AssetState::Triggered, vmAch6->GetState());
Assert::AreEqual(std::wstring(L"Titles"), vmAch6->GetUnlockRichPresence());
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::AchievementTriggered, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Unpromoted Achievement Unlocked"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Ach6 (5)"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Description 6"), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::Badge, pPopup->GetImage().Type());
Assert::AreEqual(std::string("012345"), pPopup->GetImage().Name());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumTriggeredTriggers());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\unlock.wav"));
}
TEST_METHOD(TestHandleAchievementTriggeredEventCompatibilityMode)
{
AchievementRuntimeHarness runtime;
auto* pAch6 = runtime.MockAchievement(6U, "0xH0000=1");
memcpy(pAch6->public_.badge_name, "012345", 7);
auto* vmAch6 = runtime.WrapAchievement(pAch6);
runtime.mockGameContext.SetMode(ra::data::context::GameContext::Mode::CompatibilityTest);
runtime.mockGameContext.SetRichPresenceDisplayString(L"Titles");
runtime.mockGameContext.Assets().FindRichPresence()->Activate();
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::AchievementTriggered,
ra::ui::viewmodels::PopupLocation::BottomLeft);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED;
event.achievement = &pAch6->public_;
runtime.RaiseEvent(event);
Assert::AreEqual(ra::data::models::AssetState::Triggered, vmAch6->GetState());
Assert::AreEqual(std::wstring(L"Titles"), vmAch6->GetUnlockRichPresence());
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::AchievementTriggered, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Test Achievement Unlocked"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Ach6 (5)"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Description 6"), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::Badge, pPopup->GetImage().Type());
Assert::AreEqual(std::string("012345"), pPopup->GetImage().Name());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumTriggeredTriggers());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\unlock.wav"));
}
TEST_METHOD(TestHandleAchievementTriggeredEventModified)
{
AchievementRuntimeHarness runtime;
auto* pAch6 = runtime.MockAchievement(6U, "0xH0000=1");
memcpy(pAch6->public_.badge_name, "012345", 7);
auto* vmAch6 = runtime.WrapAchievement(pAch6);
vmAch6->SetTrigger("0xH0000=2");
runtime.mockGameContext.SetRichPresenceDisplayString(L"Titles");
runtime.mockGameContext.Assets().FindRichPresence()->Activate();
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::AchievementTriggered,
ra::ui::viewmodels::PopupLocation::BottomLeft);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED;
event.achievement = &pAch6->public_;
runtime.RaiseEvent(event);
Assert::AreEqual(ra::data::models::AssetState::Triggered, vmAch6->GetState());
Assert::AreEqual(std::wstring(L"Titles"), vmAch6->GetUnlockRichPresence());
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::AchievementTriggered, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Modified Achievement Unlocked LOCALLY"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Ach6 (5)"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Description 6"), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::Badge, pPopup->GetImage().Type());
Assert::AreEqual(std::string("012345"), pPopup->GetImage().Name());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumTriggeredTriggers());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\acherror.wav"));
}
TEST_METHOD(TestHandleAchievementTriggeredEventLocalModified)
{
AchievementRuntimeHarness runtime;
auto* pAch6 = runtime.MockLocalAchievement(6U, "0xH0000=1");
memcpy(pAch6->public_.badge_name, "012345", 7);
auto* vmAch6 = runtime.WrapAchievement(pAch6);
vmAch6->SetTrigger("0xH0000=2");
runtime.mockGameContext.SetRichPresenceDisplayString(L"Titles");
runtime.mockGameContext.Assets().FindRichPresence()->Activate();
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::AchievementTriggered,
ra::ui::viewmodels::PopupLocation::BottomLeft);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED;
event.achievement = &pAch6->public_;
runtime.RaiseEvent(event);
Assert::AreEqual(ra::data::models::AssetState::Triggered, vmAch6->GetState());
Assert::AreEqual(std::wstring(L"Titles"), vmAch6->GetUnlockRichPresence());
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::AchievementTriggered, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Modified Local Achievement Unlocked"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Ach6 (5)"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Description 6"), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::Badge, pPopup->GetImage().Type());
Assert::AreEqual(std::string("012345"), pPopup->GetImage().Name());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumTriggeredTriggers());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\acherror.wav"));
}
TEST_METHOD(TestHandleAchievementTriggeredEventMemoryModified)
{
AchievementRuntimeHarness runtime;
auto* pAch6 = runtime.MockAchievement(6U, "0xH0000=1");
memcpy(pAch6->public_.badge_name, "012345", 7);
auto* vmAch6 = runtime.WrapAchievement(pAch6);
runtime.mockGameContext.SetRichPresenceDisplayString(L"Titles");
runtime.mockGameContext.Assets().FindRichPresence()->Activate();
runtime.mockEmulatorMemoryContext.SetMemoryModified();
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::AchievementTriggered,
ra::ui::viewmodels::PopupLocation::BottomLeft);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED;
event.achievement = &pAch6->public_;
runtime.RaiseEvent(event);
Assert::AreEqual(ra::data::models::AssetState::Triggered, vmAch6->GetState());
Assert::AreEqual(std::wstring(L"Titles"), vmAch6->GetUnlockRichPresence());
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::AchievementTriggered, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Achievement Unlocked LOCALLY"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Ach6 (5)"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Error: RAM tampered with"), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::Badge, pPopup->GetImage().Type());
Assert::AreEqual(std::string("012345"), pPopup->GetImage().Name());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumTriggeredTriggers());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\acherror.wav"));
}
TEST_METHOD(TestHandleAchievementTriggeredEventMemoryInsecureHardcore)
{
AchievementRuntimeHarness runtime;
auto* pAch6 = runtime.MockAchievement(6U, "0xH0000=1");
memcpy(pAch6->public_.badge_name, "012345", 7);
auto* vmAch6 = runtime.WrapAchievement(pAch6);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
runtime.GetClient()->state.hardcore = 1;
runtime.mockGameContext.SetRichPresenceDisplayString(L"Titles");
runtime.mockGameContext.Assets().FindRichPresence()->Activate();
runtime.mockEmulatorMemoryContext.MockMemoryInsecure(true);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::AchievementTriggered,
ra::ui::viewmodels::PopupLocation::BottomLeft);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED;
event.achievement = &pAch6->public_;
runtime.RaiseEvent(event);
Assert::AreEqual(ra::data::models::AssetState::Triggered, vmAch6->GetState());
Assert::AreEqual(std::wstring(L"Titles"), vmAch6->GetUnlockRichPresence());
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::AchievementTriggered, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Achievement Unlocked LOCALLY"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Ach6 (5)"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Error: RAM insecure"), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::Badge, pPopup->GetImage().Type());
Assert::AreEqual(std::string("012345"), pPopup->GetImage().Name());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumTriggeredTriggers());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\acherror.wav"));
}
TEST_METHOD(TestHandleAchievementTriggeredEventMemoryInsecureCasual)
{
AchievementRuntimeHarness runtime;
auto* pAch6 = runtime.MockAchievement(6U, "0xH0000=1");
memcpy(pAch6->public_.badge_name, "012345", 7);
pAch6->public_.rarity = 23.45f;
pAch6->public_.rarity_hardcore = 12.34f;
auto* vmAch6 = runtime.WrapAchievement(pAch6);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, false);
runtime.GetClient()->state.hardcore = 0;
runtime.mockGameContext.SetRichPresenceDisplayString(L"Titles");
runtime.mockGameContext.Assets().FindRichPresence()->Activate();
runtime.mockEmulatorMemoryContext.MockMemoryInsecure(true);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::AchievementTriggered,
ra::ui::viewmodels::PopupLocation::BottomLeft);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED;
event.achievement = &pAch6->public_;
runtime.RaiseEvent(event);
Assert::AreEqual(ra::data::models::AssetState::Triggered, vmAch6->GetState());
Assert::AreEqual(std::wstring(L"Titles"), vmAch6->GetUnlockRichPresence());
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::AchievementTriggered, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Achievement Unlocked - 23.45%"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Ach6 (5)"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Description 6"), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::Badge, pPopup->GetImage().Type());
Assert::AreEqual(std::string("012345"), pPopup->GetImage().Name());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumTriggeredTriggers());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\unlock.wav"));
}
TEST_METHOD(TestHandleAchievementTriggeredEventOffline)
{
AchievementRuntimeHarness runtime;
auto* pAch6 = runtime.MockAchievement(6U, "0xH0000=1");
memcpy(pAch6->public_.badge_name, "012345", 7);
auto* vmAch6 = runtime.WrapAchievement(pAch6);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Offline, true);
runtime.mockGameContext.SetRichPresenceDisplayString(L"Titles");
runtime.mockGameContext.Assets().FindRichPresence()->Activate();
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::AchievementTriggered,
ra::ui::viewmodels::PopupLocation::BottomLeft);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED;
event.achievement = &pAch6->public_;
runtime.RaiseEvent(event);
Assert::AreEqual(ra::data::models::AssetState::Triggered, vmAch6->GetState());
Assert::AreEqual(std::wstring(L"Titles"), vmAch6->GetUnlockRichPresence());
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::AchievementTriggered, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Offline Achievement Unlocked"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Ach6 (5)"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Description 6"), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::Badge, pPopup->GetImage().Type());
Assert::AreEqual(std::string("012345"), pPopup->GetImage().Name());
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumTriggeredTriggers());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\unlock.wav"));
}
TEST_METHOD(TestHandleAchievementTriggeredEventOnlyHardcoreWithHardcoreOff)
{
AchievementRuntimeHarness runtime;
auto* pAch6 = runtime.MockAchievement(6U, "0xH0000=1");
memcpy(pAch6->public_.badge_name, "012345", 7);
auto* vmAch6 = runtime.WrapAchievement(pAch6);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, false);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::OnlyHardcoreUnlocks, true);
runtime.mockGameContext.SetRichPresenceDisplayString(L"Titles");
runtime.mockGameContext.Assets().FindRichPresence()->Activate();
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::AchievementTriggered,
ra::ui::viewmodels::PopupLocation::BottomLeft);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED;
event.achievement = &pAch6->public_;
runtime.RaiseEvent(event);
Assert::AreEqual(ra::data::models::AssetState::Triggered, vmAch6->GetState());
Assert::AreEqual(std::wstring(L"Titles"), vmAch6->GetUnlockRichPresence());
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::AchievementTriggered, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Achievement Unlocked LOCALLY"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Ach6 (5)"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Only Hardcore Unlocks feature is enabled"), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::Badge, pPopup->GetImage().Type());
Assert::AreEqual(std::string("012345"), pPopup->GetImage().Name());
Assert::AreEqual({ 0U }, runtime.mockFrameEventQueue.NumTriggeredTriggers());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\unlock.wav"));
}
TEST_METHOD(TestHandleAchievementTriggeredEventOnlyHardcoreWithHardcoreOn)
{
AchievementRuntimeHarness runtime;
auto* pAch6 = runtime.MockAchievement(6U, "0xH0000=1");
memcpy(pAch6->public_.badge_name, "012345", 7);
pAch6->public_.rarity = 23.45f;
pAch6->public_.rarity_hardcore = 12.34f;
auto* vmAch6 = runtime.WrapAchievement(pAch6);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::OnlyHardcoreUnlocks, true);
runtime.mockGameContext.SetRichPresenceDisplayString(L"Titles");
runtime.mockGameContext.Assets().FindRichPresence()->Activate();
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::AchievementTriggered,
ra::ui::viewmodels::PopupLocation::BottomLeft);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED;
event.achievement = &pAch6->public_;
runtime.RaiseEvent(event);
Assert::AreEqual(ra::data::models::AssetState::Triggered, vmAch6->GetState());
Assert::AreEqual(std::wstring(L"Titles"), vmAch6->GetUnlockRichPresence());
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::AchievementTriggered, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Achievement Unlocked - 12.34%"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Ach6 (5)"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Description 6"), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::Badge, pPopup->GetImage().Type());
Assert::AreEqual(std::string("012345"), pPopup->GetImage().Name());
Assert::AreEqual({ 0U }, runtime.mockFrameEventQueue.NumTriggeredTriggers());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\unlock.wav"));
}
TEST_METHOD(TestHandleAchievementTriggeredEventNoPopup)
{
AchievementRuntimeHarness runtime;
auto* pAch6 = runtime.MockAchievement(6U, "0xH0000=1");
memcpy(pAch6->public_.badge_name, "012345", 7);
auto* vmAch6 = runtime.WrapAchievement(pAch6);
runtime.mockGameContext.SetRichPresenceDisplayString(L"Titles");
runtime.mockGameContext.Assets().FindRichPresence()->Activate();
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::AchievementTriggered,
ra::ui::viewmodels::PopupLocation::None);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED;
event.achievement = &pAch6->public_;
runtime.RaiseEvent(event);
Assert::AreEqual(ra::data::models::AssetState::Triggered, vmAch6->GetState());
Assert::AreEqual(std::wstring(L"Titles"), vmAch6->GetUnlockRichPresence());
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Assert::IsNull(pPopup);
Assert::AreEqual({0U}, runtime.mockFrameEventQueue.NumTriggeredTriggers());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\unlock.wav"));
}
TEST_METHOD(TestHandleAchievementTriggeredServerError)
{
AchievementRuntimeHarness runtime;
auto* pAch6 = runtime.MockAchievement(6U, "0xH0000=1");
memcpy(pAch6->public_.badge_name, "012345", 7);
pAch6->public_.points = 3;
runtime.WrapAchievement(pAch6);
rc_client_server_error_t server_error;
memset(&server_error, 0, sizeof(server_error));
server_error.api = "award_achievement";
server_error.error_message = "Cannot find the achievement with ID: 6";
server_error.related_id = 6;
server_error.result = RC_API_FAILURE;
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_SERVER_ERROR;
event.server_error = &server_error;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::AchievementTriggered, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Achievement Unlock FAILED"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Ach6 (3)"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Cannot find the achievement with ID: 6"), pPopup->GetDetail());
Assert::IsTrue(pPopup->IsDetailError());
Assert::AreEqual(ra::ui::ImageType::Badge, pPopup->GetImage().Type());
Assert::AreEqual(std::string("012345"), pPopup->GetImage().Name());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\acherror.wav"));
}
TEST_METHOD(TestHandleChallengeIndicatorShowEvent)
{
AchievementRuntimeHarness runtime;
auto* pAch6 = runtime.MockAchievement(6U, "0xH0000=1");
memcpy(pAch6->public_.badge_name, "012345", 7);
runtime.WrapAchievement(pAch6);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::Challenge,
ra::ui::viewmodels::PopupLocation::BottomRight);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_ACHIEVEMENT_CHALLENGE_INDICATOR_SHOW;
event.achievement = &pAch6->public_;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetChallengeIndicator(6U);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::Challenge, pPopup->GetPopupType());
Assert::AreEqual(ra::ui::ImageType::Badge, pPopup->GetImage().Type());
Assert::AreEqual(std::string("012345"), pPopup->GetImage().Name());
}
TEST_METHOD(TestHandleChallengeIndicatorShowEventNoPopup)
{
AchievementRuntimeHarness runtime;
auto* pAch6 = runtime.MockAchievement(6U, "0xH0000=1");
memcpy(pAch6->public_.badge_name, "012345", 7);
runtime.WrapAchievement(pAch6);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::Challenge,
ra::ui::viewmodels::PopupLocation::None);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_ACHIEVEMENT_CHALLENGE_INDICATOR_SHOW;
event.achievement = &pAch6->public_;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetChallengeIndicator(6U);
Assert::IsNull(pPopup);
}
TEST_METHOD(TestHandleChallengeIndicatorHideEvent)
{
AchievementRuntimeHarness runtime;
auto* pAch6 = runtime.MockAchievement(6U, "0xH0000=1");
runtime.WrapAchievement(pAch6);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::Challenge,
ra::ui::viewmodels::PopupLocation::BottomRight);
runtime.mockOverlayManager.AddChallengeIndicator(6U, ra::ui::ImageType::Badge, "55223");
auto* pPopup = runtime.mockOverlayManager.GetChallengeIndicator(6U);
Assert::IsTrue(pPopup != nullptr && !pPopup->IsDestroyPending());
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_ACHIEVEMENT_CHALLENGE_INDICATOR_HIDE;
event.achievement = &pAch6->public_;
runtime.RaiseEvent(event);
pPopup = runtime.mockOverlayManager.GetChallengeIndicator(6U);
Assert::IsTrue(pPopup == nullptr || pPopup->IsDestroyPending());
}
TEST_METHOD(TestHandleChallengeIndicatorHideEventNoPopup)
{
AchievementRuntimeHarness runtime;
auto* pAch6 = runtime.MockAchievement(6U, "0xH0000=1");
runtime.WrapAchievement(pAch6);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::Challenge,
ra::ui::viewmodels::PopupLocation::BottomRight);
auto* pPopup = runtime.mockOverlayManager.GetChallengeIndicator(6U);
Assert::IsNull(pPopup);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_ACHIEVEMENT_CHALLENGE_INDICATOR_HIDE;
event.achievement = &pAch6->public_;
runtime.RaiseEvent(event);
pPopup = runtime.mockOverlayManager.GetChallengeIndicator(6U);
Assert::IsNull(pPopup);
}
TEST_METHOD(TestHandleProgressIndicatorShowEvent)
{
AchievementRuntimeHarness runtime;
auto* pAch6 = runtime.MockAchievement(6U, "0xH0000=1");
snprintf(pAch6->public_.measured_progress, sizeof(pAch6->public_.measured_progress), "6/10");
memcpy(pAch6->public_.badge_name, "012345", 7);
runtime.WrapAchievement(pAch6);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::Progress,
ra::ui::viewmodels::PopupLocation::BottomRight);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_SHOW;
event.achievement = &pAch6->public_;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetProgressTracker();
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::Progress, pPopup->GetPopupType());
Assert::AreEqual(ra::ui::ImageType::Badge, pPopup->GetImage().Type());
Assert::AreEqual(std::string("012345_lock"), pPopup->GetImage().Name());
Assert::AreEqual(std::wstring(L"6/10"), pPopup->GetText());
}
TEST_METHOD(TestHandleProgressIndicatorShowEventNoPopup)
{
AchievementRuntimeHarness runtime;
auto* pAch6 = runtime.MockAchievement(6U, "0xH0000=1");
snprintf(pAch6->public_.measured_progress, sizeof(pAch6->public_.measured_progress), "6/10");
memcpy(pAch6->public_.badge_name, "012345", 7);
runtime.WrapAchievement(pAch6);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::Progress,
ra::ui::viewmodels::PopupLocation::None);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_SHOW;
event.achievement = &pAch6->public_;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetProgressTracker();
Assert::IsNull(pPopup);
}
TEST_METHOD(TestHandleProgressIndicatorShowEventLocalBadge)
{
AchievementRuntimeHarness runtime;
auto* pAch6 = runtime.MockAchievement(6U, "0xH0000=1");
snprintf(pAch6->public_.measured_progress, sizeof(pAch6->public_.measured_progress), "6/10");
memcpy(pAch6->public_.badge_name, "012345", 7);
auto* vmAch6 = runtime.WrapAchievement(pAch6);
vmAch6->SetBadge(L"local\\abcdefg.png");
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::Progress,
ra::ui::viewmodels::PopupLocation::BottomRight);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_SHOW;
event.achievement = &pAch6->public_;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetProgressTracker();
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::Progress, pPopup->GetPopupType());
Assert::AreEqual(ra::ui::ImageType::Badge, pPopup->GetImage().Type());
Assert::AreEqual(std::string("local\\abcdefg.png"), pPopup->GetImage().Name());
Assert::AreEqual(std::wstring(L"6/10"), pPopup->GetText());
}
TEST_METHOD(TestHandleProgressIndicatorUpdateEvent)
{
AchievementRuntimeHarness runtime;
auto* pAch6 = runtime.MockAchievement(6U, "0xH0000=1");
snprintf(pAch6->public_.measured_progress, sizeof(pAch6->public_.measured_progress), "6/10");
memcpy(pAch6->public_.badge_name, "012345", 7);
runtime.WrapAchievement(pAch6);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::Progress,
ra::ui::viewmodels::PopupLocation::BottomRight);
runtime.mockOverlayManager.UpdateProgressTracker(ra::ui::ImageType::Badge, "000001", L"4/10");
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_UPDATE;
event.achievement = &pAch6->public_;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetProgressTracker();
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::Progress, pPopup->GetPopupType());
Assert::AreEqual(ra::ui::ImageType::Badge, pPopup->GetImage().Type());
Assert::AreEqual(std::string("012345_lock"), pPopup->GetImage().Name());
Assert::AreEqual(std::wstring(L"6/10"), pPopup->GetText());
}
TEST_METHOD(TestHandleProgressIndicatorHideEvent)
{
AchievementRuntimeHarness runtime;
auto* pAch6 = runtime.MockAchievement(6U, "0xH0000=1");
snprintf(pAch6->public_.measured_progress, sizeof(pAch6->public_.measured_progress), "6/10");
memcpy(pAch6->public_.badge_name, "012345", 7);
runtime.WrapAchievement(pAch6);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::Progress,
ra::ui::viewmodels::PopupLocation::BottomRight);
runtime.mockOverlayManager.UpdateProgressTracker(ra::ui::ImageType::Badge, "000001", L"4/10");
auto* pPopup = runtime.mockOverlayManager.GetProgressTracker();
Assert::IsTrue(pPopup != nullptr && !pPopup->IsDestroyPending());
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_HIDE;
runtime.RaiseEvent(event);
pPopup = runtime.mockOverlayManager.GetProgressTracker();
Assert::IsTrue(pPopup == nullptr || pPopup->IsDestroyPending());
}
TEST_METHOD(TestHandleGameCompletedEventHardcore)
{
AchievementRuntimeHarness runtime;
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
runtime.mockGameContext.SetActiveGameId(1U);
runtime.MockAchievement(6U, "0xH0000=1")->public_.points = 5;
runtime.MockAchievement(7U, "0xH0000=1")->public_.points = 1;
runtime.MockAchievement(8U, "0xH0000=1")->public_.points = 1;
runtime.MockAchievement(9U, "0xH0000=1")->public_.points = 3;
runtime.MockAchievement(10U, "0xH0000=1")->public_.points = 10;
runtime.MockAchievement(11U, "0xH0000=1")->public_.points = 5;
runtime.MockAchievement(12U, "0xH0000=1")->public_.points = 10;
runtime.MockAchievement(13U, "0xH0000=1")->public_.points = 5;
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::Mastery,
ra::ui::viewmodels::PopupLocation::TopMiddle);
runtime.mockSessionTracker.MockSession(1U, time(NULL), std::chrono::seconds(20000));
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_GAME_COMPLETED;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::Mastery, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Mastered Game Title"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"8 achievements, 40 points"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"User_ | Play time: 5h33m"), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::Icon, pPopup->GetImage().Type());
Assert::AreEqual(std::string("012345"), pPopup->GetImage().Name());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\unlock.wav"));
}
TEST_METHOD(TestHandleGameCompletedEventCasual)
{
AchievementRuntimeHarness runtime;
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, false);
runtime.mockGameContext.SetActiveGameId(1U);
runtime.MockAchievement(6U, "0xH0000=1")->public_.points = 5;
runtime.MockAchievement(7U, "0xH0000=1")->public_.points = 1;
runtime.MockAchievement(8U, "0xH0000=1")->public_.points = 1;
runtime.MockAchievement(9U, "0xH0000=1")->public_.points = 3;
runtime.MockAchievement(10U, "0xH0000=1")->public_.points = 10;
runtime.MockAchievement(11U, "0xH0000=1")->public_.points = 5;
runtime.MockAchievement(12U, "0xH0000=1")->public_.points = 10;
runtime.MockAchievement(13U, "0xH0000=1")->public_.points = 5;
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::Mastery,
ra::ui::viewmodels::PopupLocation::TopMiddle);
runtime.mockSessionTracker.MockSession(1U, time(NULL), std::chrono::seconds(20000));
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_GAME_COMPLETED;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::Mastery, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Completed Game Title"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"8 achievements, 40 points"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"User_ | Play time: 5h33m"), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::Icon, pPopup->GetImage().Type());
Assert::AreEqual(std::string("012345"), pPopup->GetImage().Name());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\unlock.wav"));
}
TEST_METHOD(TestHandleSubsetCompletedEventHardcore)
{
AchievementRuntimeHarness runtime;
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
runtime.mockGameContext.SetGameTitle(L"Game Name");
runtime.mockGameContext.SetActiveGameId(1U);
auto* pSubset = runtime.MockSubset(2U, "Game Subset");
runtime.WrapAchievement(runtime.MockSubsetAchievement(6U, "0xH0000=1", pSubset))->SetPoints(5);
runtime.WrapAchievement(runtime.MockSubsetAchievement(7U, "0xH0000=1", pSubset))->SetPoints(1);
runtime.WrapAchievement(runtime.MockSubsetAchievement(8U, "0xH0000=1", pSubset))->SetPoints(1);
runtime.WrapAchievement(runtime.MockSubsetAchievement(9U, "0xH0000=1", pSubset))->SetPoints(3);
runtime.WrapAchievement(runtime.MockSubsetAchievement(10U, "0xH0000=1", pSubset))->SetPoints(10);
runtime.WrapAchievement(runtime.MockSubsetAchievement(11U, "0xH0000=1", pSubset))->SetPoints(5);
runtime.WrapAchievement(runtime.MockSubsetAchievement(12U, "0xH0000=1", pSubset))->SetPoints(10);
runtime.WrapAchievement(runtime.MockSubsetAchievement(13U, "0xH0000=1", pSubset))->SetPoints(5);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::Mastery,
ra::ui::viewmodels::PopupLocation::TopMiddle);
runtime.mockSessionTracker.MockSession(1U, time(NULL), std::chrono::seconds(20000));
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_SUBSET_COMPLETED;
event.subset = &pSubset->public_;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::Mastery, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Mastered Game Subset (Game Name)"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"8 achievements, 40 points"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"User_ | Play time: 5h33m"), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::Icon, pPopup->GetImage().Type());
Assert::AreEqual(std::string("012345"), pPopup->GetImage().Name());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\unlock.wav"));
}
TEST_METHOD(TestHandleSubsetCompletedEventCasual)
{
AchievementRuntimeHarness runtime;
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, false);
runtime.mockGameContext.SetGameTitle(L"Game Name");
runtime.mockGameContext.SetActiveGameId(1U);
auto* pSubset = runtime.MockSubset(2U, "Game Subset");
runtime.WrapAchievement(runtime.MockSubsetAchievement(6U, "0xH0000=1", pSubset))->SetPoints(5);
runtime.WrapAchievement(runtime.MockSubsetAchievement(7U, "0xH0000=1", pSubset))->SetPoints(1);
runtime.WrapAchievement(runtime.MockSubsetAchievement(8U, "0xH0000=1", pSubset))->SetPoints(1);
runtime.WrapAchievement(runtime.MockSubsetAchievement(9U, "0xH0000=1", pSubset))->SetPoints(3);
runtime.WrapAchievement(runtime.MockSubsetAchievement(10U, "0xH0000=1", pSubset))->SetPoints(10);
runtime.WrapAchievement(runtime.MockSubsetAchievement(11U, "0xH0000=1", pSubset))->SetPoints(5);
runtime.WrapAchievement(runtime.MockSubsetAchievement(12U, "0xH0000=1", pSubset))->SetPoints(10);
runtime.WrapAchievement(runtime.MockSubsetAchievement(13U, "0xH0000=1", pSubset))->SetPoints(5);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::Mastery,
ra::ui::viewmodels::PopupLocation::TopMiddle);
runtime.mockSessionTracker.MockSession(1U, time(NULL), std::chrono::seconds(20000));
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_SUBSET_COMPLETED;
event.subset = &pSubset->public_;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::Mastery, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Completed Game Subset (Game Name)"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"8 achievements, 40 points"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"User_ | Play time: 5h33m"), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::Icon, pPopup->GetImage().Type());
Assert::AreEqual(std::string("012345"), pPopup->GetImage().Name());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\unlock.wav"));
}
TEST_METHOD(TestHandleLeaderboardStartedEvent)
{
AchievementRuntimeHarness runtime;
auto *pLbd4 = runtime.MockLeaderboard(4U, "STA:0xH0000=1::CAN:0xH0000=1::SUB:0xH0000=2::VAL:0xH0001");
runtime.WrapLeaderboard(pLbd4);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, true);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardStarted,
ra::ui::viewmodels::PopupLocation::BottomLeft);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_LEADERBOARD_STARTED;
event.leaderboard = &pLbd4->public_;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::LeaderboardStarted, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Leaderboard attempt started"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Leaderboard4"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Description 4"), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::None, pPopup->GetImage().Type());
Assert::AreEqual(std::string(), pPopup->GetImage().Name());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\lb.wav"));
}
TEST_METHOD(TestHandleLeaderboardStartedEventNoPopup)
{
AchievementRuntimeHarness runtime;
auto* pLbd4 = runtime.MockLeaderboard(4U, "STA:0xH0000=1::CAN:0xH0000=1::SUB:0xH0000=2::VAL:0xH0001");
runtime.WrapLeaderboard(pLbd4);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, true);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardStarted,
ra::ui::viewmodels::PopupLocation::None);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_LEADERBOARD_STARTED;
event.leaderboard = &pLbd4->public_;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Assert::IsNull(pPopup);
}
TEST_METHOD(TestHandleLeaderboardStartedEventDisabled)
{
AchievementRuntimeHarness runtime;
auto* pLbd4 = runtime.MockLeaderboard(4U, "STA:0xH0000=1::CAN:0xH0000=1::SUB:0xH0000=2::VAL:0xH0001");
runtime.WrapLeaderboard(pLbd4);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, false);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardStarted,
ra::ui::viewmodels::PopupLocation::BottomLeft);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_LEADERBOARD_STARTED;
event.leaderboard = &pLbd4->public_;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Assert::IsNull(pPopup);
}
TEST_METHOD(TestHandleLeaderboardFailedEvent)
{
AchievementRuntimeHarness runtime;
auto* pLbd4 = runtime.MockLeaderboard(4U, "STA:0xH0000=1::CAN:0xH0000=1::SUB:0xH0000=2::VAL:0xH0001");
runtime.WrapLeaderboard(pLbd4);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, true);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardCanceled,
ra::ui::viewmodels::PopupLocation::BottomLeft);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_LEADERBOARD_FAILED;
event.leaderboard = &pLbd4->public_;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::LeaderboardCanceled, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Leaderboard attempt failed"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Leaderboard4"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Description 4"), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::None, pPopup->GetImage().Type());
Assert::AreEqual(std::string(), pPopup->GetImage().Name());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\lbcancel.wav"));
}
TEST_METHOD(TestHandleLeaderboardFailedEventNoPopup)
{
AchievementRuntimeHarness runtime;
auto* pLbd4 = runtime.MockLeaderboard(4U, "STA:0xH0000=1::CAN:0xH0000=1::SUB:0xH0000=2::VAL:0xH0001");
runtime.WrapLeaderboard(pLbd4);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, true);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardCanceled,
ra::ui::viewmodels::PopupLocation::None);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_LEADERBOARD_FAILED;
event.leaderboard = &pLbd4->public_;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Assert::IsNull(pPopup);
}
TEST_METHOD(TestHandleLeaderboardFailedEventDisabled)
{
AchievementRuntimeHarness runtime;
auto* pLbd4 = runtime.MockLeaderboard(4U, "STA:0xH0000=1::CAN:0xH0000=1::SUB:0xH0000=2::VAL:0xH0001");
runtime.WrapLeaderboard(pLbd4);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, false);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardCanceled,
ra::ui::viewmodels::PopupLocation::BottomLeft);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_LEADERBOARD_FAILED;
event.leaderboard = &pLbd4->public_;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Assert::IsNull(pPopup);
}
TEST_METHOD(TestHandleLeaderboardSubmittedEvent)
{
AchievementRuntimeHarness runtime;
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
auto* pLbd4 = runtime.MockLeaderboard(4U, "STA:0xH0000=1::CAN:0xH0000=1::SUB:0xH0000=2::VAL:0xH0001");
pLbd4->public_.tracker_value = "1:23.45";
runtime.WrapLeaderboard(pLbd4);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, true);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardScoreboard,
ra::ui::viewmodels::PopupLocation::BottomRight);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_LEADERBOARD_SUBMITTED;
event.leaderboard = &pLbd4->public_;
runtime.RaiseEvent(event);
// Submitted event doesn't do anything unless a problem occurs - scoreboard event is used instead
Assert::IsNull(runtime.mockOverlayManager.GetMessage(1));
Assert::IsNull(runtime.mockOverlayManager.GetScoreboard(4U));
}
private:
void AssertSimpleScoreboard(AchievementRuntimeHarness& runtime, ra::LeaderboardID nId)
{
auto* pScoreboard = runtime.mockOverlayManager.GetScoreboard(nId);
Assert::IsNotNull(pScoreboard);
Ensures(pScoreboard != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::LeaderboardScoreboard, pScoreboard->GetPopupType());
Assert::AreEqual(std::wstring(L"Leaderboard4"), pScoreboard->GetHeaderText());
Assert::AreEqual({1U}, pScoreboard->Entries().Count());
const auto* pEntry = pScoreboard->Entries().GetItemAt(0);
Expects(pEntry != nullptr);
Assert::AreEqual(0, pEntry->GetRank());
Assert::AreEqual(std::wstring(L"1:23.45"), pEntry->GetScore());
Assert::AreEqual(std::wstring(L"User_"), pEntry->GetUserName());
Assert::IsTrue(pEntry->IsHighlighted());
}
public:
TEST_METHOD(TestHandleLeaderboardSubmittedEventLocal)
{
AchievementRuntimeHarness runtime;
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
auto* pLbd4 = runtime.MockLocalLeaderboard(4U, "STA:0xH0000=1::CAN:0xH0000=1::SUB:0xH0000=2::VAL:0xH0001");
pLbd4->public_.tracker_value = "1:23.45";
runtime.WrapLeaderboard(pLbd4);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, true);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardScoreboard,
ra::ui::viewmodels::PopupLocation::BottomRight);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_LEADERBOARD_SUBMITTED;
event.leaderboard = &pLbd4->public_;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::Message, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Local Leaderboard Submitted"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Leaderboard4"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Local leaderboards are not submitted."), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::None, pPopup->GetImage().Type());
Assert::AreEqual(std::string(), pPopup->GetImage().Name());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\info.wav"));
AssertSimpleScoreboard(runtime, 4U);
}
TEST_METHOD(TestHandleLeaderboardSubmittedEventCompatibilityMode)
{
AchievementRuntimeHarness runtime;
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
runtime.mockGameContext.SetMode(ra::data::context::GameContext::Mode::CompatibilityTest);
auto* pLbd4 = runtime.MockLeaderboard(4U, "STA:0xH0000=1::CAN:0xH0000=1::SUB:0xH0000=2::VAL:0xH0001");
pLbd4->public_.tracker_value = "1:23.45";
runtime.WrapLeaderboard(pLbd4);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, true);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardScoreboard,
ra::ui::viewmodels::PopupLocation::BottomRight);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_LEADERBOARD_SUBMITTED;
event.leaderboard = &pLbd4->public_;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::Message, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Leaderboard Submitted"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Leaderboard4"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Leaderboards are not submitted in compatibility test mode."), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::None, pPopup->GetImage().Type());
Assert::AreEqual(std::string(), pPopup->GetImage().Name());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\info.wav"));
AssertSimpleScoreboard(runtime, 4U);
}
TEST_METHOD(TestHandleLeaderboardSubmittedEventModified)
{
AchievementRuntimeHarness runtime;
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
auto* pLbd4 = runtime.MockLeaderboard(4U, "STA:0xH0000=1::CAN:0xH0000=1::SUB:0xH0000=2::VAL:0xH0001");
pLbd4->public_.tracker_value = "1:23.45";
auto* vmLbd4 = runtime.WrapLeaderboard(pLbd4);
vmLbd4->SetDescription(L"Modified Description");
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, true);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardScoreboard,
ra::ui::viewmodels::PopupLocation::BottomRight);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_LEADERBOARD_SUBMITTED;
event.leaderboard = &pLbd4->public_;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::Message, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Modified Leaderboard Submitted"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Leaderboard4"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Modified leaderboards are not submitted."), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::None, pPopup->GetImage().Type());
Assert::AreEqual(std::string(), pPopup->GetImage().Name());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\info.wav"));
AssertSimpleScoreboard(runtime, 4U);
}
TEST_METHOD(TestHandleLeaderboardSubmittedEventUnpublished)
{
AchievementRuntimeHarness runtime;
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
auto* pLbd4 = runtime.MockLeaderboard(4U, "STA:0xH0000=1::CAN:0xH0000=1::SUB:0xH0000=2::VAL:0xH0001");
pLbd4->public_.tracker_value = "1:23.45";
auto* vmLbd4 = runtime.WrapLeaderboard(pLbd4);
vmLbd4->SetDescription(L"Modified Description");
vmLbd4->UpdateLocalCheckpoint();
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, true);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardScoreboard,
ra::ui::viewmodels::PopupLocation::BottomRight);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_LEADERBOARD_SUBMITTED;
event.leaderboard = &pLbd4->public_;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::Message, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Modified Leaderboard Submitted"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Leaderboard4"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Modified leaderboards are not submitted."), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::None, pPopup->GetImage().Type());
Assert::AreEqual(std::string(), pPopup->GetImage().Name());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\info.wav"));
AssertSimpleScoreboard(runtime, 4U);
}
TEST_METHOD(TestHandleLeaderboardSubmittedEventModifiedMemory)
{
AchievementRuntimeHarness runtime;
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
auto* pLbd4 = runtime.MockLeaderboard(4U, "STA:0xH0000=1::CAN:0xH0000=1::SUB:0xH0000=2::VAL:0xH0001");
pLbd4->public_.tracker_value = "1:23.45";
runtime.WrapLeaderboard(pLbd4);
runtime.mockEmulatorMemoryContext.MockMemoryModified(true);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, true);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardScoreboard,
ra::ui::viewmodels::PopupLocation::BottomRight);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_LEADERBOARD_SUBMITTED;
event.leaderboard = &pLbd4->public_;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::Message, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Leaderboard Submitted"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Leaderboard4"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Error: RAM tampered with"), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::None, pPopup->GetImage().Type());
Assert::AreEqual(std::string(), pPopup->GetImage().Name());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\info.wav"));
AssertSimpleScoreboard(runtime, 4U);
}
TEST_METHOD(TestHandleLeaderboardSubmittedEventCasual)
{
AchievementRuntimeHarness runtime;
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, false);
auto* pLbd4 = runtime.MockLeaderboard(4U, "STA:0xH0000=1::CAN:0xH0000=1::SUB:0xH0000=2::VAL:0xH0001");
pLbd4->public_.tracker_value = "1:23.45";
runtime.WrapLeaderboard(pLbd4);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, true);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardScoreboard,
ra::ui::viewmodels::PopupLocation::BottomRight);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_LEADERBOARD_SUBMITTED;
event.leaderboard = &pLbd4->public_;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::Message, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Leaderboard Submitted"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Leaderboard4"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Submission requires Hardcore mode"), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::None, pPopup->GetImage().Type());
Assert::AreEqual(std::string(), pPopup->GetImage().Name());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\info.wav"));
AssertSimpleScoreboard(runtime, 4U);
}
TEST_METHOD(TestHandleLeaderboardSubmittedEventMemoryInsecure)
{
AchievementRuntimeHarness runtime;
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
auto* pLbd4 = runtime.MockLeaderboard(4U, "STA:0xH0000=1::CAN:0xH0000=1::SUB:0xH0000=2::VAL:0xH0001");
pLbd4->public_.tracker_value = "1:23.45";
runtime.WrapLeaderboard(pLbd4);
runtime.mockEmulatorMemoryContext.MockMemoryInsecure(true);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, true);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardScoreboard,
ra::ui::viewmodels::PopupLocation::BottomRight);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_LEADERBOARD_SUBMITTED;
event.leaderboard = &pLbd4->public_;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::Message, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Leaderboard Submitted"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Leaderboard4"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Error: RAM insecure"), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::None, pPopup->GetImage().Type());
Assert::AreEqual(std::string(), pPopup->GetImage().Name());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\info.wav"));
AssertSimpleScoreboard(runtime, 4U);
}
TEST_METHOD(TestHandleLeaderboardSubmittedEventOffline)
{
AchievementRuntimeHarness runtime;
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Offline, true);
auto* pLbd4 = runtime.MockLeaderboard(4U, "STA:0xH0000=1::CAN:0xH0000=1::SUB:0xH0000=2::VAL:0xH0001");
pLbd4->public_.tracker_value = "1:23.45";
runtime.WrapLeaderboard(pLbd4);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, true);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardScoreboard,
ra::ui::viewmodels::PopupLocation::BottomRight);
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_LEADERBOARD_SUBMITTED;
event.leaderboard = &pLbd4->public_;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::Message, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Leaderboard Submitted"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Leaderboard4"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Leaderboards are not submitted in offline mode."), pPopup->GetDetail());
Assert::AreEqual(ra::ui::ImageType::None, pPopup->GetImage().Type());
Assert::AreEqual(std::string(), pPopup->GetImage().Name());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\info.wav"));
AssertSimpleScoreboard(runtime, 4U);
}
TEST_METHOD(TestHandleLeaderboardTrackerShowEvent)
{
AchievementRuntimeHarness runtime;
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, true);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardTracker,
ra::ui::viewmodels::PopupLocation::BottomRight);
rc_client_leaderboard_tracker_t tracker;
memset(&tracker, 0, sizeof(tracker));
snprintf(tracker.display, sizeof(tracker.display), "1:23.45");
tracker.id = 1;
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_LEADERBOARD_TRACKER_SHOW;
event.leaderboard_tracker = &tracker;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetScoreTracker(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::LeaderboardTracker, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"1:23.45"), pPopup->GetDisplayText());
}
TEST_METHOD(TestHandleLeaderboardTrackerShowEventNoPopup)
{
AchievementRuntimeHarness runtime;
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, true);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardTracker,
ra::ui::viewmodels::PopupLocation::None);
rc_client_leaderboard_tracker_t tracker;
memset(&tracker, 0, sizeof(tracker));
snprintf(tracker.display, sizeof(tracker.display), "1:23.45");
tracker.id = 1;
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_LEADERBOARD_TRACKER_SHOW;
event.leaderboard_tracker = &tracker;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetScoreTracker(1);
Assert::IsNull(pPopup);
}
TEST_METHOD(TestHandleLeaderboardTrackerShowEventDisabled)
{
AchievementRuntimeHarness runtime;
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, false);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardTracker,
ra::ui::viewmodels::PopupLocation::BottomRight);
rc_client_leaderboard_tracker_t tracker;
memset(&tracker, 0, sizeof(tracker));
snprintf(tracker.display, sizeof(tracker.display), "1:23.45");
tracker.id = 1;
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_LEADERBOARD_TRACKER_SHOW;
event.leaderboard_tracker = &tracker;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetScoreTracker(1);
Assert::IsNull(pPopup);
}
TEST_METHOD(TestHandleLeaderboardTrackerUpdateEvent)
{
AchievementRuntimeHarness runtime;
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, true);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardTracker,
ra::ui::viewmodels::PopupLocation::BottomRight);
runtime.mockOverlayManager.AddScoreTracker(1U).SetDisplayText(L"XXX");
rc_client_leaderboard_tracker_t tracker;
memset(&tracker, 0, sizeof(tracker));
snprintf(tracker.display, sizeof(tracker.display), "1:23.45");
tracker.id = 1;
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_LEADERBOARD_TRACKER_UPDATE;
event.leaderboard_tracker = &tracker;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetScoreTracker(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::LeaderboardTracker, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"1:23.45"), pPopup->GetDisplayText());
}
TEST_METHOD(TestHandleLeaderboardTrackerUpdateEventNoPopup)
{
AchievementRuntimeHarness runtime;
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, true);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardTracker,
ra::ui::viewmodels::PopupLocation::None);
rc_client_leaderboard_tracker_t tracker;
memset(&tracker, 0, sizeof(tracker));
snprintf(tracker.display, sizeof(tracker.display), "1:23.45");
tracker.id = 1;
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_LEADERBOARD_TRACKER_UPDATE;
event.leaderboard_tracker = &tracker;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetScoreTracker(1);
Assert::IsNull(pPopup);
}
TEST_METHOD(TestHandleLeaderboardTrackerUpdateEventDisabled)
{
AchievementRuntimeHarness runtime;
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, false);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardTracker,
ra::ui::viewmodels::PopupLocation::BottomRight);
rc_client_leaderboard_tracker_t tracker;
memset(&tracker, 0, sizeof(tracker));
snprintf(tracker.display, sizeof(tracker.display), "1:23.45");
tracker.id = 1;
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_LEADERBOARD_TRACKER_UPDATE;
event.leaderboard_tracker = &tracker;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetScoreTracker(1);
Assert::IsNull(pPopup);
}
TEST_METHOD(TestHandleLeaderboardTrackerHideEvent)
{
AchievementRuntimeHarness runtime;
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, true);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardTracker,
ra::ui::viewmodels::PopupLocation::BottomRight);
runtime.mockOverlayManager.AddScoreTracker(1U).SetDisplayText(L"XXX");
rc_client_leaderboard_tracker_t tracker;
memset(&tracker, 0, sizeof(tracker));
snprintf(tracker.display, sizeof(tracker.display), "1:23.45");
tracker.id = 1;
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_LEADERBOARD_TRACKER_HIDE;
event.leaderboard_tracker = &tracker;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetScoreTracker(1);
Assert::IsTrue(pPopup == nullptr || pPopup->IsDestroyPending());
}
TEST_METHOD(TestHandleLeaderboardScoreboardEvent)
{
AchievementRuntimeHarness runtime;
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, true);
auto* pLbd4 = runtime.MockLeaderboard(4U, "STA:0xH0000=1::CAN:0xH0000=1::SUB:0xH0000=2::VAL:0xH0001");
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardScoreboard,
ra::ui::viewmodels::PopupLocation::BottomRight);
rc_client_leaderboard_scoreboard_entry_t entries[3];
memset(entries, 0, sizeof(entries));
rc_client_leaderboard_scoreboard_t scoreboard;
memset(&scoreboard, 0, sizeof(scoreboard));
scoreboard.leaderboard_id = 4U;
scoreboard.new_rank = 2;
snprintf(scoreboard.best_score, sizeof(scoreboard.best_score), "1:20.00");
snprintf(scoreboard.submitted_score, sizeof(scoreboard.submitted_score), "1:23.45");
scoreboard.num_entries = 3;
scoreboard.num_top_entries = 3;
scoreboard.top_entries = entries;
entries[0].rank = 1;
snprintf(entries[0].score, sizeof(entries[0].score), "1:19.55");
entries[0].username = "Bob";
entries[1].rank = 2;
snprintf(entries[1].score, sizeof(entries[0].score), "1:20.00");
entries[1].username = "User_";
entries[2].rank = 3;
snprintf(entries[2].score, sizeof(entries[0].score), "1:20.12");
entries[2].username = "Tara";
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_LEADERBOARD_SCOREBOARD;
event.leaderboard = &pLbd4->public_;
event.leaderboard_scoreboard = &scoreboard;
runtime.RaiseEvent(event);
auto* pScoreboard = runtime.mockOverlayManager.GetScoreboard(4U);
Assert::IsNotNull(pScoreboard);
Ensures(pScoreboard != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::LeaderboardScoreboard, pScoreboard->GetPopupType());
Assert::AreEqual(std::wstring(L"Leaderboard4"), pScoreboard->GetHeaderText());
Assert::AreEqual({3U}, pScoreboard->Entries().Count());
const auto* pEntry = pScoreboard->Entries().GetItemAt(0);
Expects(pEntry != nullptr);
Assert::AreEqual(1, pEntry->GetRank());
Assert::AreEqual(std::wstring(L"1:19.55"), pEntry->GetScore());
Assert::AreEqual(std::wstring(L"Bob"), pEntry->GetUserName());
Assert::IsFalse(pEntry->IsHighlighted());
pEntry = pScoreboard->Entries().GetItemAt(1);
Expects(pEntry != nullptr);
Assert::AreEqual(2, pEntry->GetRank());
Assert::AreEqual(std::wstring(L"(1:23.45) 1:20.00"), pEntry->GetScore());
Assert::AreEqual(std::wstring(L"User_"), pEntry->GetUserName());
Assert::IsTrue(pEntry->IsHighlighted());
pEntry = pScoreboard->Entries().GetItemAt(2);
Expects(pEntry != nullptr);
Assert::AreEqual(3, pEntry->GetRank());
Assert::AreEqual(std::wstring(L"1:20.12"), pEntry->GetScore());
Assert::AreEqual(std::wstring(L"Tara"), pEntry->GetUserName());
Assert::IsFalse(pEntry->IsHighlighted());
}
TEST_METHOD(TestHandleLeaderboardScoreboardEventNoPopup)
{
AchievementRuntimeHarness runtime;
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, true);
auto* pLbd4 = runtime.MockLeaderboard(4U, "STA:0xH0000=1::CAN:0xH0000=1::SUB:0xH0000=2::VAL:0xH0001");
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardScoreboard,
ra::ui::viewmodels::PopupLocation::None);
rc_client_leaderboard_scoreboard_entry_t entries[3];
memset(entries, 0, sizeof(entries));
rc_client_leaderboard_scoreboard_t scoreboard;
memset(&scoreboard, 0, sizeof(scoreboard));
scoreboard.leaderboard_id = 4U;
scoreboard.new_rank = 2;
snprintf(scoreboard.best_score, sizeof(scoreboard.best_score), "1:20.00");
snprintf(scoreboard.submitted_score, sizeof(scoreboard.submitted_score), "1:23.45");
scoreboard.num_entries = 3;
scoreboard.num_top_entries = 3;
scoreboard.top_entries = entries;
entries[0].rank = 1;
snprintf(entries[0].score, sizeof(entries[0].score), "1:19.55");
entries[0].username = "Bob";
entries[1].rank = 2;
snprintf(entries[1].score, sizeof(entries[1].score), "1:20.00");
entries[1].username = "User_";
entries[2].rank = 3;
snprintf(entries[2].score, sizeof(entries[2].score), "1:20.12");
entries[2].username = "Tara";
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_LEADERBOARD_SCOREBOARD;
event.leaderboard = &pLbd4->public_;
event.leaderboard_scoreboard = &scoreboard;
runtime.RaiseEvent(event);
auto* pScoreboard = runtime.mockOverlayManager.GetScoreboard(4U);
Assert::IsNull(pScoreboard);
}
TEST_METHOD(TestHandleLeaderboardScoreboardEventDisabled)
{
AchievementRuntimeHarness runtime;
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, false);
auto* pLbd4 = runtime.MockLeaderboard(4U, "STA:0xH0000=1::CAN:0xH0000=1::SUB:0xH0000=2::VAL:0xH0001");
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardScoreboard,
ra::ui::viewmodels::PopupLocation::BottomRight);
rc_client_leaderboard_scoreboard_entry_t entries[3];
memset(entries, 0, sizeof(entries));
rc_client_leaderboard_scoreboard_t scoreboard;
memset(&scoreboard, 0, sizeof(scoreboard));
scoreboard.leaderboard_id = 4U;
scoreboard.new_rank = 2;
snprintf(scoreboard.best_score, sizeof(scoreboard.best_score), "1:20.00");
snprintf(scoreboard.submitted_score, sizeof(scoreboard.submitted_score), "1:23.45");
scoreboard.num_entries = 3;
scoreboard.num_top_entries = 3;
scoreboard.top_entries = entries;
entries[0].rank = 1;
snprintf(entries[0].score, sizeof(entries[0].score), "1:19.55");
entries[0].username = "Bob";
entries[1].rank = 2;
snprintf(entries[1].score, sizeof(entries[1].score), "1:20.00");
entries[1].username = "User_";
entries[2].rank = 3;
snprintf(entries[2].score, sizeof(entries[2].score), "1:20.12");
entries[2].username = "Tara";
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_LEADERBOARD_SCOREBOARD;
event.leaderboard = &pLbd4->public_;
event.leaderboard_scoreboard = &scoreboard;
runtime.RaiseEvent(event);
auto* pScoreboard = runtime.mockOverlayManager.GetScoreboard(4U);
Assert::IsNull(pScoreboard);
}
TEST_METHOD(TestHandleLeaderboardScoreboardEventPlayerNotInTopEntries)
{
AchievementRuntimeHarness runtime;
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, true);
auto* pLbd4 = runtime.MockLeaderboard(4U, "STA:0xH0000=1::CAN:0xH0000=1::SUB:0xH0000=2::VAL:0xH0001");
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardScoreboard,
ra::ui::viewmodels::PopupLocation::BottomRight);
rc_client_leaderboard_scoreboard_entry_t entries[7];
memset(entries, 0, sizeof(entries));
rc_client_leaderboard_scoreboard_t scoreboard;
memset(&scoreboard, 0, sizeof(scoreboard));
scoreboard.leaderboard_id = 4U;
scoreboard.new_rank = 27;
snprintf(scoreboard.best_score, sizeof(scoreboard.best_score), "1:20.00");
snprintf(scoreboard.submitted_score, sizeof(scoreboard.submitted_score), "1:23.45");
scoreboard.num_entries = 31;
scoreboard.num_top_entries = 7;
scoreboard.top_entries = entries;
entries[0].rank = 1;
snprintf(entries[0].score, sizeof(entries[0].score), "0:56.55");
entries[0].username = "Bob";
entries[1].rank = 2;
snprintf(entries[1].score, sizeof(entries[1].score), "0:57.00");
entries[1].username = "Calvin";
entries[2].rank = 3;
snprintf(entries[2].score, sizeof(entries[2].score), "0:57.12");
entries[2].username = "Debbie";
entries[3].rank = 4;
snprintf(entries[3].score, sizeof(entries[3].score), "0:58.22");
entries[3].username = "Erin";
entries[4].rank = 4;
snprintf(entries[4].score, sizeof(entries[4].score), "0:58.22");
entries[4].username = "Frank";
entries[5].rank = 6;
snprintf(entries[5].score, sizeof(entries[5].score), "0:59.88");
entries[5].username = "George";
entries[6].rank = 7;
snprintf(entries[6].score, sizeof(entries[6].score), "1:00.41");
entries[6].username = "Harry";
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_LEADERBOARD_SCOREBOARD;
event.leaderboard = &pLbd4->public_;
event.leaderboard_scoreboard = &scoreboard;
runtime.RaiseEvent(event);
auto* pScoreboard = runtime.mockOverlayManager.GetScoreboard(4U);
Assert::IsNotNull(pScoreboard);
Ensures(pScoreboard != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::LeaderboardScoreboard, pScoreboard->GetPopupType());
Assert::AreEqual(std::wstring(L"Leaderboard4"), pScoreboard->GetHeaderText());
Assert::AreEqual({7U}, pScoreboard->Entries().Count());
const auto* pEntry = pScoreboard->Entries().GetItemAt(0);
Expects(pEntry != nullptr);
Assert::AreEqual(1, pEntry->GetRank());
Assert::AreEqual(std::wstring(L"0:56.55"), pEntry->GetScore());
Assert::AreEqual(std::wstring(L"Bob"), pEntry->GetUserName());
Assert::IsFalse(pEntry->IsHighlighted());
pEntry = pScoreboard->Entries().GetItemAt(1);
Expects(pEntry != nullptr);
Assert::AreEqual(2, pEntry->GetRank());
Assert::AreEqual(std::wstring(L"0:57.00"), pEntry->GetScore());
Assert::AreEqual(std::wstring(L"Calvin"), pEntry->GetUserName());
Assert::IsFalse(pEntry->IsHighlighted());
pEntry = pScoreboard->Entries().GetItemAt(2);
Expects(pEntry != nullptr);
Assert::AreEqual(3, pEntry->GetRank());
Assert::AreEqual(std::wstring(L"0:57.12"), pEntry->GetScore());
Assert::AreEqual(std::wstring(L"Debbie"), pEntry->GetUserName());
Assert::IsFalse(pEntry->IsHighlighted());
pEntry = pScoreboard->Entries().GetItemAt(3);
Expects(pEntry != nullptr);
Assert::AreEqual(4, pEntry->GetRank());
Assert::AreEqual(std::wstring(L"0:58.22"), pEntry->GetScore());
Assert::AreEqual(std::wstring(L"Erin"), pEntry->GetUserName());
Assert::IsFalse(pEntry->IsHighlighted());
pEntry = pScoreboard->Entries().GetItemAt(4);
Expects(pEntry != nullptr);
Assert::AreEqual(4, pEntry->GetRank());
Assert::AreEqual(std::wstring(L"0:58.22"), pEntry->GetScore());
Assert::AreEqual(std::wstring(L"Frank"), pEntry->GetUserName());
Assert::IsFalse(pEntry->IsHighlighted());
pEntry = pScoreboard->Entries().GetItemAt(5);
Expects(pEntry != nullptr);
Assert::AreEqual(6, pEntry->GetRank());
Assert::AreEqual(std::wstring(L"0:59.88"), pEntry->GetScore());
Assert::AreEqual(std::wstring(L"George"), pEntry->GetUserName());
Assert::IsFalse(pEntry->IsHighlighted());
pEntry = pScoreboard->Entries().GetItemAt(6);
Expects(pEntry != nullptr);
Assert::AreEqual(27, pEntry->GetRank());
Assert::AreEqual(std::wstring(L"(1:23.45) 1:20.00"), pEntry->GetScore());
Assert::AreEqual(std::wstring(L"User_"), pEntry->GetUserName());
Assert::IsTrue(pEntry->IsHighlighted());
}
TEST_METHOD(TestHandleLeaderboardServerError)
{
AchievementRuntimeHarness runtime;
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Leaderboards, true);
auto* pLeaderboard = runtime.MockLeaderboard(4U, "STA:0xH0000=1::CAN:0xH0000=1::SUB:0xH0000=2::VAL:0xH0001");
runtime.WrapLeaderboard(pLeaderboard);
rc_client_server_error_t server_error;
memset(&server_error, 0, sizeof(server_error));
server_error.api = "submit_lboard_entry";
server_error.error_message = "Cannot find the leaderboard with ID: 4";
server_error.related_id = 4;
server_error.result = RC_API_FAILURE;
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_SERVER_ERROR;
event.server_error = &server_error;
runtime.RaiseEvent(event);
auto* pPopup = runtime.mockOverlayManager.GetMessage(1);
Expects(pPopup != nullptr);
Assert::AreEqual(ra::ui::viewmodels::Popup::Message, pPopup->GetPopupType());
Assert::AreEqual(std::wstring(L"Leaderboard Submit FAILED"), pPopup->GetTitle());
Assert::AreEqual(std::wstring(L"Leaderboard4"), pPopup->GetDescription());
Assert::AreEqual(std::wstring(L"Cannot find the leaderboard with ID: 4"), pPopup->GetDetail());
Assert::IsTrue(pPopup->IsDetailError());
Assert::AreEqual(ra::ui::ImageType::None, pPopup->GetImage().Type());
Assert::AreEqual(std::string(), pPopup->GetImage().Name());
Assert::IsTrue(runtime.mockAudioSystem.WasAudioFilePlayed(L"Overlay\\acherror.wav"));
}
TEST_METHOD(TestHandleResetEvent)
{
AchievementRuntimeHarness runtime;
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
runtime.mockWindowConfiguration.SetPopupLocation(ra::ui::viewmodels::Popup::LeaderboardTracker,
ra::ui::viewmodels::PopupLocation::BottomRight);
bool bWasReset = false;
runtime.mockEmulatorContext.SetResetFunction([&bWasReset]() { bWasReset = true; });
rc_client_event_t event;
memset(&event, 0, sizeof(event));
event.type = RC_CLIENT_EVENT_RESET;
runtime.RaiseEvent(event);
Assert::IsTrue(bWasReset);
}
TEST_METHOD(TestRichPresenceOverrideNoRichPresence)
{
AchievementRuntimeHarness runtime;
runtime.mockGameContext.SetGameTitle(L"GameTitle");
Assert::AreEqual(std::string(""), runtime.GetRichPresenceOverride()); // no override
}
TEST_METHOD(TestRichPresenceOverrideRichPresence)
{
AchievementRuntimeHarness runtime;
runtime.mockGameContext.SetRichPresenceDisplayString(L"Level 10");
runtime.mockGameContext.Assets().FindRichPresence()->Activate();
Assert::AreEqual(std::string(""), runtime.GetRichPresenceOverride()); // no override
}
TEST_METHOD(TestRichPresenceOverrideRichPresenceFromFile)
{
AchievementRuntimeHarness runtime;
runtime.mockGameContext.SetGameTitle(L"GameTitle");
runtime.mockGameContext.SetRichPresenceDisplayString(L"Level 10");
runtime.mockGameContext.SetRichPresenceFromFile(true);
runtime.mockGameContext.Assets().FindRichPresence()->Activate();
Assert::AreEqual(std::string("Playing GameTitle"), runtime.GetRichPresenceOverride());
}
TEST_METHOD(TestRichPresenceOverrideRichPresenceCompatibilityMode)
{
AchievementRuntimeHarness runtime;
runtime.mockGameContext.SetMode(ra::data::context::GameContext::Mode::CompatibilityTest);
runtime.mockGameContext.SetRichPresenceDisplayString(L"Level 10");
runtime.mockGameContext.Assets().FindRichPresence()->Activate();
Assert::AreEqual(std::string(""), runtime.GetRichPresenceOverride()); // no override
}
TEST_METHOD(TestRichPresenceOverrideInspectingMemoryCompatibilityMode)
{
AchievementRuntimeHarness runtime;
runtime.mockGameContext.MockAchievement();
runtime.mockGameContext.SetMode(ra::data::context::GameContext::Mode::CompatibilityTest);
runtime.MockInspectingMemory(true);
Assert::AreEqual(std::string("Testing Compatibility"), runtime.GetRichPresenceOverride());
}
TEST_METHOD(TestRichPresenceOverrideInspectingMemoryNoAchievements)
{
AchievementRuntimeHarness runtime;
runtime.MockInspectingMemory(true);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, false);
Assert::AreEqual(std::string("Inspecting Memory"), runtime.GetRichPresenceOverride());
}
TEST_METHOD(TestRichPresenceOverrideInspectingMemoryNoAchievementsHardcore)
{
AchievementRuntimeHarness runtime;
runtime.MockInspectingMemory(true);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
Assert::AreEqual(std::string("Inspecting Memory in Hardcore mode"), runtime.GetRichPresenceOverride());
}
TEST_METHOD(TestRichPresenceOverrideInspectingMemoryCoreAchievementsHardcore)
{
AchievementRuntimeHarness runtime;
runtime.MockInspectingMemory(true);
runtime.mockGameContext.MockAchievement();
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
Assert::AreEqual(std::string("Inspecting Memory in Hardcore mode"), runtime.GetRichPresenceOverride());
}
TEST_METHOD(TestRichPresenceOverrideInspectingMemoryModifiedCoreAchievementsHardcore)
{
AchievementRuntimeHarness runtime;
runtime.MockInspectingMemory(true);
auto& pAch = runtime.mockGameContext.MockAchievement();
pAch.SetName(L"Modified");
Assert::AreEqual(pAch.GetChanges(), ra::data::models::AssetChanges::Modified);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true);
Assert::AreEqual(std::string("Inspecting Memory in Hardcore mode"), runtime.GetRichPresenceOverride());
}
TEST_METHOD(TestRichPresenceOverrideInspectingMemoryCoreAchievements)
{
AchievementRuntimeHarness runtime;
runtime.MockInspectingMemory(true);
auto& pAch = runtime.mockGameContext.MockAchievement();
Assert::AreEqual(pAch.GetChanges(), ra::data::models::AssetChanges::None);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, false);
Assert::AreEqual(std::string("Inspecting Memory"), runtime.GetRichPresenceOverride());
}
TEST_METHOD(TestRichPresenceOverrideInspectingMemoryModifiedCoreAchievements)
{
AchievementRuntimeHarness runtime;
runtime.MockInspectingMemory(true);
auto& pAch = runtime.mockGameContext.MockAchievement();
pAch.SetName(L"Modified");
Assert::AreEqual(pAch.GetChanges(), ra::data::models::AssetChanges::Modified);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, false);
Assert::AreEqual(std::string("Fixing Achievements"), runtime.GetRichPresenceOverride());
}
TEST_METHOD(TestRichPresenceOverrideInspectingMemoryUnpublishedCoreAchievements)
{
AchievementRuntimeHarness runtime;
runtime.MockInspectingMemory(true);
auto& pAch = runtime.mockGameContext.MockAchievement();
pAch.SetName(L"Modified");
pAch.UpdateLocalCheckpoint();
Assert::AreEqual(pAch.GetChanges(), ra::data::models::AssetChanges::Unpublished);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, false);
Assert::AreEqual(std::string("Fixing Achievements"), runtime.GetRichPresenceOverride());
}
TEST_METHOD(TestRichPresenceOverrideInspectingMemoryUnpromotedAchievements)
{
AchievementRuntimeHarness runtime;
runtime.MockInspectingMemory(true);
const auto& pAch = runtime.mockGameContext.MockUnpromotedAchievement();
Assert::AreEqual(pAch.GetChanges(), ra::data::models::AssetChanges::None);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, false);
Assert::AreEqual(std::string("Inspecting Memory"), runtime.GetRichPresenceOverride());
}
TEST_METHOD(TestRichPresenceOverrideInspectingMemoryModifiedUnpromotedAchievements)
{
AchievementRuntimeHarness runtime;
runtime.MockInspectingMemory(true);
auto& pAch = runtime.mockGameContext.MockUnpromotedAchievement();
pAch.SetName(L"Modified");
Assert::AreEqual(pAch.GetChanges(), ra::data::models::AssetChanges::Modified);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, false);
Assert::AreEqual(std::string("Fixing Achievements"), runtime.GetRichPresenceOverride());
}
TEST_METHOD(TestRichPresenceOverrideInspectingMemoryLocalAchievements)
{
AchievementRuntimeHarness runtime;
runtime.MockInspectingMemory(true);
runtime.mockGameContext.MockLocalAchievement();
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, false);
Assert::AreEqual(std::string("Developing Achievements"), runtime.GetRichPresenceOverride());
}
TEST_METHOD(TestRichPresenceOverrideInspectingMemoryLocalAndPromotedAchievements)
{
AchievementRuntimeHarness runtime;
runtime.MockInspectingMemory(true);
runtime.mockGameContext.MockLocalAchievement();
const auto& pAch = runtime.mockGameContext.MockAchievement();
Assert::AreEqual(pAch.GetChanges(), ra::data::models::AssetChanges::None);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, false);
Assert::AreEqual(std::string("Developing Achievements"), runtime.GetRichPresenceOverride());
}
TEST_METHOD(TestRichPresenceOverrideInspectingMemoryLocalAndModifiedPromotedAchievements)
{
AchievementRuntimeHarness runtime;
runtime.MockInspectingMemory(true);
runtime.mockGameContext.MockLocalAchievement();;
auto& pAch = runtime.mockGameContext.MockAchievement();
pAch.SetName(L"Modified");
Assert::AreEqual(pAch.GetChanges(), ra::data::models::AssetChanges::Modified);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, false);
Assert::AreEqual(std::string("Developing Achievements"), runtime.GetRichPresenceOverride());
}
TEST_METHOD(TestServerCallOfflinePatchExists)
{
AchievementRuntimeHarness runtime;
ra::services::mocks::MockLocalStorage mockLocalStorage;
mockLocalStorage.MockStoredData(ra::services::StorageItemType::GameData, L"1234", "{\"a\":1}");
ra::services::impl::OfflineRcClient pOfflineRcClient;
ra::services::ServiceLocator::ServiceOverride<ra::context::IRcClient> pRcClientOverride(&pOfflineRcClient, false);
rc_api_request_t pRequest;
memset(&pRequest, 0, sizeof(pRequest));
pRequest.url = "https://retroachievements.org/dorequest.php";
pRequest.post_data = "r=patch&u=User&t=APITOKEN&g=1234";
pRequest.content_type = "application/x-www-form-urlencoded";
bool bCallbackCalled = false;
auto fCallback = [](const rc_api_server_response_t* server_response, void* callback_data) {
Assert::AreEqual("{\"Success\":true,\"PatchData\":{\"a\":1}}", server_response->body);
Assert::AreEqual({36}, server_response->body_length);
Assert::AreEqual(200, server_response->http_status_code);
*(static_cast<bool*>(callback_data)) = true;
};
runtime.GetClient()->callbacks.server_call(&pRequest, fCallback, &bCallbackCalled, runtime.GetClient());
Assert::IsFalse(bCallbackCalled);
runtime.mockThreadPool.ExecuteNextTask();
Assert::IsTrue(bCallbackCalled);
}
TEST_METHOD(TestServerCallOfflinePatchDoesntExist)
{
AchievementRuntimeHarness runtime;
ra::services::mocks::MockLocalStorage mockLocalStorage;
ra::services::impl::OfflineRcClient pOfflineRcClient;
ra::services::ServiceLocator::ServiceOverride<ra::context::IRcClient> pRcClientOverride(&pOfflineRcClient, false);
rc_api_request_t pRequest;
memset(&pRequest, 0, sizeof(pRequest));
pRequest.url = "https://retroachievements.org/dorequest.php";
pRequest.post_data = "g=1234&r=patch&u=User&t=APITOKEN";
pRequest.content_type = "application/x-www-form-urlencoded";
bool bCallbackCalled = false;
auto fCallback = [](const rc_api_server_response_t* server_response, void* callback_data) {
Assert::AreEqual("{\"Success\":false,\"Error\":\"Achievement data for game 1234 not found in cache\"}", server_response->body);
Assert::AreEqual({77}, server_response->body_length);
Assert::AreEqual(404, server_response->http_status_code);
*(static_cast<bool*>(callback_data)) = true;
};
runtime.GetClient()->callbacks.server_call(&pRequest, fCallback, &bCallbackCalled, runtime.GetClient());
Assert::IsFalse(bCallbackCalled);
runtime.mockThreadPool.ExecuteNextTask();
Assert::IsTrue(bCallbackCalled);
}
TEST_METHOD(TestServerCallOfflineAchievementSetsExists)
{
AchievementRuntimeHarness runtime;
runtime.mockLoginService.Logout();
ra::services::mocks::MockLocalStorage mockLocalStorage;
ra::services::mocks::MockGameIdentifier mockGameIdentifier;
mockLocalStorage.MockStoredData(ra::services::StorageItemType::GameData, L"1234", "{\"Title\":\"GameName\",\"Sets\":[{\"a\":1}]}");
ra::services::impl::OfflineRcClient pOfflineRcClient;
ra::services::ServiceLocator::ServiceOverride<ra::context::IRcClient> pRcClientOverride(&pOfflineRcClient, false);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Offline, true);
mockGameIdentifier.AddHash("ABCDEF0123456789", 1234);
rc_api_request_t pRequest;
memset(&pRequest, 0, sizeof(pRequest));
pRequest.url = "https://retroachievements.org/dorequest.php";
pRequest.post_data = "r=achievementsets&u=User&t=APITOKEN&m=ABCDEF0123456789";
pRequest.content_type = "application/x-www-form-urlencoded";
bool bCallbackCalled = false;
auto fCallback = [](const rc_api_server_response_t* server_response, void* callback_data) {
Assert::AreEqual("{\"Title\":\"GameName\",\"Sets\":[{\"a\":1}]}", server_response->body);
Assert::AreEqual({38}, server_response->body_length);
Assert::AreEqual(200, server_response->http_status_code);
*(static_cast<bool*>(callback_data)) = true;
};
runtime.GetClient()->callbacks.server_call(&pRequest, fCallback, &bCallbackCalled, runtime.GetClient());
Assert::IsFalse(bCallbackCalled);
runtime.mockThreadPool.ExecuteNextTask();
Assert::IsTrue(bCallbackCalled);
}
TEST_METHOD(TestServerCallOfflineAchievementSetsPatchExists)
{
AchievementRuntimeHarness runtime;
runtime.mockLoginService.Logout();
ra::services::mocks::MockLocalStorage mockLocalStorage;
ra::services::mocks::MockGameIdentifier mockGameIdentifier;
// cached file exists, but is old format and should be ignored
mockLocalStorage.MockStoredData(ra::services::StorageItemType::GameData, L"1234", "{\"a\":1}");
ra::services::impl::OfflineRcClient pOfflineRcClient;
ra::services::ServiceLocator::ServiceOverride<ra::context::IRcClient> pRcClientOverride(&pOfflineRcClient, false);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Offline, true);
mockGameIdentifier.AddHash("ABCDEF0123456789", 1234);
rc_api_request_t pRequest;
memset(&pRequest, 0, sizeof(pRequest));
pRequest.url = "https://retroachievements.org/dorequest.php";
pRequest.post_data = "r=achievementsets&u=User&t=APITOKEN&m=ABCDEF0123456789";
pRequest.content_type = "application/x-www-form-urlencoded";
bool bCallbackCalled = false;
auto fCallback = [](const rc_api_server_response_t* server_response, void* callback_data) {
Assert::AreEqual("{\"Success\":false,\"Error\":\"Achievement data for game 1234 not found in cache\"}", server_response->body);
Assert::AreEqual({77}, server_response->body_length);
Assert::AreEqual(404, server_response->http_status_code);
*(static_cast<bool*>(callback_data)) = true;
};
runtime.GetClient()->callbacks.server_call(&pRequest, fCallback, &bCallbackCalled, runtime.GetClient());
Assert::IsFalse(bCallbackCalled);
runtime.mockThreadPool.ExecuteNextTask();
Assert::IsTrue(bCallbackCalled);
}
TEST_METHOD(TestServerCallOfflineAchievementSetsDoesntExist)
{
AchievementRuntimeHarness runtime;
ra::services::mocks::MockLocalStorage mockLocalStorage;
ra::services::mocks::MockGameIdentifier mockGameIdentifier;
mockGameIdentifier.AddHash("ABCDEF0123456789", 1234);
ra::services::impl::OfflineRcClient pOfflineRcClient;
ra::services::ServiceLocator::ServiceOverride<ra::context::IRcClient> pRcClientOverride(&pOfflineRcClient, false);
runtime.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Offline, true);
rc_api_request_t pRequest;
memset(&pRequest, 0, sizeof(pRequest));
pRequest.url = "https://retroachievements.org/dorequest.php";
pRequest.post_data = "r=achievementsets&u=User&t=APITOKEN&m=ABCDEF0123456789";
pRequest.content_type = "application/x-www-form-urlencoded";
bool bCallbackCalled = false;
auto fCallback = [](const rc_api_server_response_t* server_response, void* callback_data) {
Assert::AreEqual("{\"Success\":false,\"Error\":\"Achievement data for game 1234 not found in cache\"}", server_response->body);
Assert::AreEqual({77}, server_response->body_length);
Assert::AreEqual(404, server_response->http_status_code);
*(static_cast<bool*>(callback_data)) = true;
};
runtime.GetClient()->callbacks.server_call(&pRequest, fCallback, &bCallbackCalled, runtime.GetClient());
Assert::IsFalse(bCallbackCalled);
runtime.mockThreadPool.ExecuteNextTask();
Assert::IsTrue(bCallbackCalled);
}
TEST_METHOD(TestServerCallOfflineStartSession)
{
AchievementRuntimeHarness runtime;
ra::services::impl::OfflineRcClient pOfflineRcClient;
ra::services::ServiceLocator::ServiceOverride<ra::context::IRcClient> pRcClientOverride(&pOfflineRcClient, false);
rc_api_request_t pRequest;
memset(&pRequest, 0, sizeof(pRequest));
pRequest.url = "https://retroachievements.org/dorequest.php";
pRequest.post_data = "r=startsession&u=User&t=APITOKEN&g=1234";
pRequest.content_type = "application/x-www-form-urlencoded";
bool bCallbackCalled = false;
auto fCallback = [](const rc_api_server_response_t* server_response, void* callback_data) {
Assert::AreEqual("{\"Success\":true}", server_response->body);
Assert::AreEqual({16}, server_response->body_length);
Assert::AreEqual(200, server_response->http_status_code);
*(static_cast<bool*>(callback_data)) = true;
};
runtime.GetClient()->callbacks.server_call(&pRequest, fCallback, &bCallbackCalled, runtime.GetClient());
Assert::IsFalse(bCallbackCalled);
runtime.mockThreadPool.ExecuteNextTask();
Assert::IsTrue(bCallbackCalled);
}
TEST_METHOD(TestServerCallOfflinePing)
{
AchievementRuntimeHarness runtime;
ra::services::impl::OfflineRcClient pOfflineRcClient;
ra::services::ServiceLocator::ServiceOverride<ra::context::IRcClient> pRcClientOverride(&pOfflineRcClient, false);
rc_api_request_t pRequest;
memset(&pRequest, 0, sizeof(pRequest));
pRequest.url = "https://retroachievements.org/dorequest.php";
pRequest.post_data = "r=ping&u=User&t=APITOKEN&g=1234&m=Hi";
pRequest.content_type = "application/x-www-form-urlencoded";
bool bCallbackCalled = false;
auto fCallback = [](const rc_api_server_response_t* server_response, void* callback_data) {
Assert::AreEqual("{\"Success\":true}", server_response->body);
Assert::AreEqual({16}, server_response->body_length);
Assert::AreEqual(200, server_response->http_status_code);
*(static_cast<bool*>(callback_data)) = true;
};
runtime.GetClient()->callbacks.server_call(&pRequest, fCallback, &bCallbackCalled, runtime.GetClient());
Assert::IsFalse(bCallbackCalled);
runtime.mockThreadPool.ExecuteNextTask();
Assert::IsTrue(bCallbackCalled);
}
};
} // namespace tests
} // namespace services
} // namespace ra