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

2099 lines
115 KiB
C++

#include "CppUnitTest.h"
#include "services\AchievementRuntime.hh"
#include "ui\EditorTheme.hh"
#include "ui\viewmodels\TriggerConditionViewModel.hh"
#include "ui\viewmodels\TriggerViewModel.hh"
#include "tests\RA_UnitTestHelpers.h"
#include "tests\devkit\context\mocks\MockConsoleContext.hh"
#include "tests\devkit\context\mocks\MockEmulatorMemoryContext.hh"
#include "tests\devkit\context\mocks\MockUserContext.hh"
#include "tests\devkit\services\mocks\MockConfiguration.hh"
#include "tests\devkit\testutil\MemoryAsserts.hh"
#include "tests\devkit\testutil\RequirementAsserts.hh"
#include "tests\mocks\MockGameContext.hh"
#include <rcheevos\src\rcheevos\rc_internal.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace ra {
namespace ui {
namespace viewmodels {
namespace tests {
TEST_CLASS(TriggerConditionViewModel_Tests)
{
private:
class TriggerConditionViewModelHarness : public TriggerConditionViewModel
{
public:
ra::context::mocks::MockEmulatorMemoryContext mockEmulatorMemoryContext;
ra::context::mocks::MockUserContext mockUserContext;
ra::data::context::mocks::MockGameContext mockGameContext;
ra::services::mocks::MockConfiguration mockConfiguration;
void Parse(const std::string& sInput)
{
const auto nSize = rc_trigger_size(sInput.c_str());
sBuffer.resize(nSize);
const rc_trigger_t* pTrigger = rc_parse_trigger(sBuffer.data(), sInput.c_str(), nullptr, 0);
Assert::IsNotNull(pTrigger);
Ensures(pTrigger != nullptr);
TriggerConditionViewModel::InitializeFrom(*pTrigger->requirement->conditions);
}
bool HasSourceSize() const { return GetValue(HasSourceSizeProperty); }
bool HasSourceValue() const { return GetValue(HasSourceValueProperty); }
bool HasTargetSize() const { return GetValue(HasTargetSizeProperty); }
bool HasTargetValue() const { return GetValue(HasTargetValueProperty); }
bool HasTarget() const { return GetValue(HasTargetProperty); }
bool HasHits() const { return GetValue(HasHitsProperty); }
bool CanEditHits() const { return GetValue(CanEditHitsProperty); }
private:
std::string sBuffer;
};
void ParseAndRegenerate(const std::string& sInput)
{
unsigned char pBuffer[2048] = {0};
const auto nSize = rc_trigger_size(sInput.c_str());
Assert::IsTrue(nSize > 0 && nSize < sizeof(pBuffer));
const rc_trigger_t* pTrigger = rc_parse_trigger(pBuffer, sInput.c_str(), nullptr, 0);
Assert::IsNotNull(pTrigger);
Ensures(pTrigger != nullptr);
TriggerConditionViewModelHarness vmCondition;
vmCondition.InitializeFrom(*pTrigger->requirement->conditions);
const std::string sOutput = vmCondition.Serialize();
Assert::AreEqual(sInput, sOutput);
}
void ParseAndRegenerateValue(const std::string& sInput)
{
unsigned char pBuffer[512] = { 0 };
const auto nSize = rc_value_size(sInput.c_str());
Assert::IsTrue(nSize > 0 && nSize < sizeof(pBuffer));
const rc_value_t* pValue = rc_parse_value(pBuffer, sInput.c_str(), nullptr, 0);
Assert::IsNotNull(pValue);
Ensures(pValue != nullptr);
TriggerViewModel vmTrigger;
vmTrigger.SetIsValue(true);
TriggerConditionViewModelHarness vmCondition;
vmCondition.SetTriggerViewModel(&vmTrigger);
vmCondition.InitializeFrom(*pValue->conditions->conditions);
const std::string sOutput = vmCondition.Serialize();
Assert::AreEqual(sInput, sOutput);
}
public:
TEST_METHOD(TestInitialState)
{
TriggerConditionViewModelHarness vmCondition;
Assert::AreEqual(1, vmCondition.GetIndex());
Assert::AreEqual(ra::data::Requirement::Type::Standard, vmCondition.GetType());
Assert::AreEqual(ra::data::Requirement::OperandType::Address, vmCondition.GetSourceType());
Assert::AreEqual(ra::data::Memory::Size::EightBit, vmCondition.GetSourceSize());
Assert::AreEqual(std::wstring(L"0"), vmCondition.GetSourceValue());
Assert::AreEqual(ra::data::Requirement::OperatorType::Equals, vmCondition.GetOperator());
Assert::AreEqual(ra::data::Requirement::OperandType::Value, vmCondition.GetTargetType());
Assert::AreEqual(ra::data::Memory::Size::ThirtyTwoBit, vmCondition.GetTargetSize());
Assert::AreEqual(std::wstring(L"0"), vmCondition.GetTargetValue());
Assert::AreEqual(0U, vmCondition.GetCurrentHits());
Assert::AreEqual(0U, vmCondition.GetRequiredHits());
Assert::IsTrue(vmCondition.HasSourceSize());
Assert::IsFalse(vmCondition.HasTargetSize());
Assert::IsTrue(vmCondition.HasTarget());
Assert::IsTrue(vmCondition.HasHits());
Assert::IsTrue(vmCondition.CanEditHits());
}
TEST_METHOD(TestHasSourceSize)
{
TriggerConditionViewModelHarness vmCondition;
vmCondition.SetTargetSize(ra::data::Memory::Size::SixteenBit);
Assert::AreEqual(ra::data::Requirement::OperandType::Address, vmCondition.GetSourceType());
Assert::IsTrue(vmCondition.HasSourceSize());
Assert::AreEqual(ra::data::Memory::Size::EightBit, vmCondition.GetSourceSize()); // initial size is 8-bit
vmCondition.SetSourceType(ra::data::Requirement::OperandType::BCD);
Assert::AreEqual(ra::data::Requirement::OperandType::BCD, vmCondition.GetSourceType());
Assert::IsTrue(vmCondition.HasSourceSize());
Assert::AreEqual(ra::data::Memory::Size::EightBit, vmCondition.GetSourceSize());
vmCondition.SetSourceType(ra::data::Requirement::OperandType::Delta);
Assert::AreEqual(ra::data::Requirement::OperandType::Delta, vmCondition.GetSourceType());
Assert::IsTrue(vmCondition.HasSourceSize());
Assert::AreEqual(ra::data::Memory::Size::EightBit, vmCondition.GetSourceSize());
vmCondition.SetSourceType(ra::data::Requirement::OperandType::Prior);
Assert::AreEqual(ra::data::Requirement::OperandType::Prior, vmCondition.GetSourceType());
Assert::IsTrue(vmCondition.HasSourceSize());
Assert::AreEqual(ra::data::Memory::Size::EightBit, vmCondition.GetSourceSize());
vmCondition.SetSourceType(ra::data::Requirement::OperandType::Inverted);
Assert::AreEqual(ra::data::Requirement::OperandType::Inverted, vmCondition.GetSourceType());
Assert::IsTrue(vmCondition.HasSourceSize());
Assert::AreEqual(ra::data::Memory::Size::EightBit, vmCondition.GetSourceSize());
vmCondition.SetSourceType(ra::data::Requirement::OperandType::Recall);
Assert::AreEqual(ra::data::Requirement::OperandType::Recall, vmCondition.GetSourceType());
Assert::IsFalse(vmCondition.HasSourceSize());
Assert::AreEqual(ra::data::Memory::Size::ThirtyTwoBit, vmCondition.GetSourceSize());
vmCondition.SetSourceType(ra::data::Requirement::OperandType::Value);
Assert::AreEqual(ra::data::Requirement::OperandType::Value, vmCondition.GetSourceType());
Assert::IsFalse(vmCondition.HasSourceSize());
Assert::AreEqual(ra::data::Memory::Size::ThirtyTwoBit, vmCondition.GetSourceSize()); // value always 32-bit
vmCondition.SetSourceType(ra::data::Requirement::OperandType::Address);
Assert::AreEqual(ra::data::Requirement::OperandType::Address, vmCondition.GetSourceType());
Assert::IsTrue(vmCondition.HasSourceSize());
Assert::AreEqual(ra::data::Memory::Size::SixteenBit, vmCondition.GetSourceSize()); // copied from target size
}
TEST_METHOD(TestHasSourceValue)
{
TriggerConditionViewModelHarness vmCondition;
vmCondition.SetTargetSize(ra::data::Memory::Size::SixteenBit);
Assert::AreEqual(ra::data::Requirement::OperandType::Address, vmCondition.GetSourceType());
Assert::IsTrue(vmCondition.HasSourceValue());
Assert::AreEqual(ra::data::Memory::Size::EightBit, vmCondition.GetSourceSize()); // initial size is 8-bit
vmCondition.SetSourceType(ra::data::Requirement::OperandType::BCD);
Assert::AreEqual(ra::data::Requirement::OperandType::BCD, vmCondition.GetSourceType());
Assert::IsTrue(vmCondition.HasSourceValue());
Assert::AreEqual(ra::data::Memory::Size::EightBit, vmCondition.GetSourceSize());
vmCondition.SetSourceType(ra::data::Requirement::OperandType::Delta);
Assert::AreEqual(ra::data::Requirement::OperandType::Delta, vmCondition.GetSourceType());
Assert::IsTrue(vmCondition.HasSourceValue());
Assert::AreEqual(ra::data::Memory::Size::EightBit, vmCondition.GetSourceSize());
vmCondition.SetSourceType(ra::data::Requirement::OperandType::Prior);
Assert::AreEqual(ra::data::Requirement::OperandType::Prior, vmCondition.GetSourceType());
Assert::IsTrue(vmCondition.HasSourceValue());
Assert::AreEqual(ra::data::Memory::Size::EightBit, vmCondition.GetSourceSize());
vmCondition.SetSourceType(ra::data::Requirement::OperandType::Inverted);
Assert::AreEqual(ra::data::Requirement::OperandType::Inverted, vmCondition.GetSourceType());
Assert::IsTrue(vmCondition.HasSourceValue());
Assert::AreEqual(ra::data::Memory::Size::EightBit, vmCondition.GetSourceSize());
vmCondition.SetSourceType(ra::data::Requirement::OperandType::Recall);
Assert::AreEqual(ra::data::Requirement::OperandType::Recall, vmCondition.GetSourceType());
Assert::IsFalse(vmCondition.HasSourceValue());
Assert::AreEqual(ra::data::Memory::Size::ThirtyTwoBit, vmCondition.GetSourceSize());
vmCondition.SetSourceType(ra::data::Requirement::OperandType::Value);
Assert::AreEqual(ra::data::Requirement::OperandType::Value, vmCondition.GetSourceType());
Assert::IsTrue(vmCondition.HasSourceValue());
Assert::AreEqual(ra::data::Memory::Size::ThirtyTwoBit, vmCondition.GetSourceSize()); // value always 32-bit
vmCondition.SetSourceType(ra::data::Requirement::OperandType::Address);
Assert::AreEqual(ra::data::Requirement::OperandType::Address, vmCondition.GetSourceType());
Assert::IsTrue(vmCondition.HasSourceValue());
Assert::AreEqual(ra::data::Memory::Size::SixteenBit, vmCondition.GetSourceSize()); // copied from target size
}
TEST_METHOD(TestHasTarget)
{
TriggerConditionViewModelHarness vmCondition;
Assert::AreEqual(ra::data::Requirement::OperatorType::Equals, vmCondition.GetOperator());
Assert::IsTrue(vmCondition.HasTarget());
vmCondition.SetOperator(ra::data::Requirement::OperatorType::NotEquals);
Assert::AreEqual(ra::data::Requirement::OperatorType::NotEquals, vmCondition.GetOperator());
Assert::IsTrue(vmCondition.HasTarget());
vmCondition.SetOperator(ra::data::Requirement::OperatorType::LessThan);
Assert::AreEqual(ra::data::Requirement::OperatorType::LessThan, vmCondition.GetOperator());
Assert::IsTrue(vmCondition.HasTarget());
vmCondition.SetOperator(ra::data::Requirement::OperatorType::LessThanOrEqual);
Assert::AreEqual(ra::data::Requirement::OperatorType::LessThanOrEqual, vmCondition.GetOperator());
Assert::IsTrue(vmCondition.HasTarget());
vmCondition.SetOperator(ra::data::Requirement::OperatorType::GreaterThan);
Assert::AreEqual(ra::data::Requirement::OperatorType::GreaterThan, vmCondition.GetOperator());
Assert::IsTrue(vmCondition.HasTarget());
vmCondition.SetOperator(ra::data::Requirement::OperatorType::GreaterThanOrEqual);
Assert::AreEqual(ra::data::Requirement::OperatorType::GreaterThanOrEqual, vmCondition.GetOperator());
Assert::IsTrue(vmCondition.HasTarget());
vmCondition.SetOperator(ra::data::Requirement::OperatorType::None);
Assert::AreEqual(ra::data::Requirement::OperatorType::None, vmCondition.GetOperator());
Assert::IsFalse(vmCondition.HasTarget());
vmCondition.SetOperator(ra::data::Requirement::OperatorType::Multiply);
Assert::AreEqual(ra::data::Requirement::OperatorType::Multiply, vmCondition.GetOperator());
Assert::IsTrue(vmCondition.HasTarget());
vmCondition.SetOperator(ra::data::Requirement::OperatorType::Divide);
Assert::AreEqual(ra::data::Requirement::OperatorType::Divide, vmCondition.GetOperator());
Assert::IsTrue(vmCondition.HasTarget());
vmCondition.SetOperator(ra::data::Requirement::OperatorType::BitwiseAnd);
Assert::AreEqual(ra::data::Requirement::OperatorType::BitwiseAnd, vmCondition.GetOperator());
Assert::IsTrue(vmCondition.HasTarget());
vmCondition.SetOperator(ra::data::Requirement::OperatorType::Modulus);
Assert::AreEqual(ra::data::Requirement::OperatorType::Modulus, vmCondition.GetOperator());
Assert::IsTrue(vmCondition.HasTarget());
vmCondition.SetOperator(ra::data::Requirement::OperatorType::Add);
Assert::AreEqual(ra::data::Requirement::OperatorType::Add, vmCondition.GetOperator());
Assert::IsTrue(vmCondition.HasTarget());
vmCondition.SetOperator(ra::data::Requirement::OperatorType::Subtract);
Assert::AreEqual(ra::data::Requirement::OperatorType::Subtract, vmCondition.GetOperator());
Assert::IsTrue(vmCondition.HasTarget());
}
TEST_METHOD(TestHasTargetSize)
{
TriggerConditionViewModelHarness vmCondition;
Assert::AreEqual(ra::data::Requirement::OperandType::Value, vmCondition.GetTargetType());
Assert::AreEqual(ra::data::Requirement::OperatorType::Equals, vmCondition.GetOperator());
Assert::IsFalse(vmCondition.HasTargetSize());
Assert::IsTrue(vmCondition.HasTarget());
Assert::AreEqual(ra::data::Memory::Size::ThirtyTwoBit, vmCondition.GetTargetSize()); // value always 32-bit
vmCondition.SetTargetType(ra::data::Requirement::OperandType::Address);
Assert::IsTrue(vmCondition.HasTargetSize());
Assert::IsTrue(vmCondition.HasTarget());
Assert::AreEqual(ra::data::Memory::Size::EightBit, vmCondition.GetTargetSize()); // size copied from source
vmCondition.SetOperator(ra::data::Requirement::OperatorType::None);
Assert::IsFalse(vmCondition.HasTargetSize());
Assert::IsFalse(vmCondition.HasTarget());
vmCondition.SetTargetType(ra::data::Requirement::OperandType::Value);
Assert::IsFalse(vmCondition.HasTargetSize());
Assert::IsFalse(vmCondition.HasTarget());
Assert::AreEqual(ra::data::Memory::Size::ThirtyTwoBit, vmCondition.GetTargetSize()); // value always 32-bit
vmCondition.SetTargetType(ra::data::Requirement::OperandType::Delta);
Assert::IsFalse(vmCondition.HasTargetSize());
Assert::IsFalse(vmCondition.HasTarget());
Assert::AreEqual(ra::data::Memory::Size::EightBit, vmCondition.GetTargetSize()); // size copied from source
vmCondition.SetOperator(ra::data::Requirement::OperatorType::Equals);
Assert::IsTrue(vmCondition.HasTargetSize());
Assert::IsTrue(vmCondition.HasTarget());
Assert::AreEqual(ra::data::Memory::Size::EightBit, vmCondition.GetTargetSize()); // size copied from source
vmCondition.SetTargetType(ra::data::Requirement::OperandType::Value);
Assert::IsFalse(vmCondition.HasTargetSize());
Assert::IsTrue(vmCondition.HasTarget());
Assert::AreEqual(ra::data::Memory::Size::ThirtyTwoBit, vmCondition.GetTargetSize()); // value always 32-bit
vmCondition.SetTargetType(ra::data::Requirement::OperandType::Recall);
Assert::IsFalse(vmCondition.HasTargetSize());
Assert::IsTrue(vmCondition.HasTarget());
Assert::AreEqual(ra::data::Memory::Size::ThirtyTwoBit, vmCondition.GetTargetSize()); // value always 32-bit
vmCondition.SetOperator(ra::data::Requirement::OperatorType::None);
Assert::IsFalse(vmCondition.HasTargetSize());
Assert::IsFalse(vmCondition.HasTarget());
Assert::AreEqual(ra::data::Memory::Size::ThirtyTwoBit, vmCondition.GetTargetSize()); // value still 32-bit
vmCondition.SetTargetType(ra::data::Requirement::OperandType::Prior);
Assert::IsFalse(vmCondition.HasTargetSize());
Assert::IsFalse(vmCondition.HasTarget());
Assert::AreEqual(ra::data::Memory::Size::EightBit, vmCondition.GetTargetSize()); // size copied from source
vmCondition.SetTargetType(ra::data::Requirement::OperandType::Value);
Assert::IsFalse(vmCondition.HasTargetSize());
Assert::IsFalse(vmCondition.HasTarget());
Assert::AreEqual(ra::data::Memory::Size::ThirtyTwoBit, vmCondition.GetTargetSize()); // value always 32-bit
vmCondition.SetTargetType(ra::data::Requirement::OperandType::Inverted);
Assert::IsFalse(vmCondition.HasTargetSize());
Assert::IsFalse(vmCondition.HasTarget());
Assert::AreEqual(ra::data::Memory::Size::EightBit, vmCondition.GetTargetSize()); // size copied from source
vmCondition.SetOperator(ra::data::Requirement::OperatorType::NotEquals);
Assert::IsTrue(vmCondition.HasTargetSize());
Assert::IsTrue(vmCondition.HasTarget());
Assert::AreEqual(ra::data::Memory::Size::EightBit, vmCondition.GetTargetSize());
}
TEST_METHOD(TestHasTargetValue)
{
TriggerConditionViewModelHarness vmCondition;
Assert::AreEqual(ra::data::Requirement::OperandType::Value, vmCondition.GetTargetType());
Assert::AreEqual(ra::data::Requirement::OperatorType::Equals, vmCondition.GetOperator());
Assert::IsTrue(vmCondition.HasTargetValue());
Assert::AreEqual(ra::data::Memory::Size::ThirtyTwoBit, vmCondition.GetTargetSize()); // value always 32-bit
vmCondition.SetTargetType(ra::data::Requirement::OperandType::Address);
Assert::IsTrue(vmCondition.HasTargetValue());
Assert::AreEqual(ra::data::Memory::Size::EightBit, vmCondition.GetTargetSize()); // size copied from source
vmCondition.SetTargetType(ra::data::Requirement::OperandType::Value);
Assert::IsTrue(vmCondition.HasTargetValue());
Assert::AreEqual(ra::data::Memory::Size::ThirtyTwoBit, vmCondition.GetTargetSize()); // value always 32-bit
vmCondition.SetTargetType(ra::data::Requirement::OperandType::Delta);
Assert::IsTrue(vmCondition.HasTargetValue());
Assert::AreEqual(ra::data::Memory::Size::EightBit, vmCondition.GetTargetSize()); // size copied from source
vmCondition.SetTargetType(ra::data::Requirement::OperandType::Inverted);
Assert::IsTrue(vmCondition.HasTargetValue());
Assert::AreEqual(ra::data::Memory::Size::EightBit, vmCondition.GetTargetSize());
vmCondition.SetTargetType(ra::data::Requirement::OperandType::BCD);
Assert::IsTrue(vmCondition.HasTargetValue());
Assert::AreEqual(ra::data::Memory::Size::EightBit, vmCondition.GetTargetSize());
vmCondition.SetTargetType(ra::data::Requirement::OperandType::Recall);
Assert::IsFalse(vmCondition.HasTargetValue());
Assert::AreEqual(ra::data::Memory::Size::ThirtyTwoBit, vmCondition.GetTargetSize()); // value always 32-bit
vmCondition.SetOperator(ra::data::Requirement::OperatorType::None);
Assert::IsFalse(vmCondition.HasTargetValue());
Assert::AreEqual(ra::data::Memory::Size::ThirtyTwoBit, vmCondition.GetTargetSize()); // value still 32-bit
vmCondition.SetOperator(ra::data::Requirement::OperatorType::NotEquals);
Assert::IsFalse(vmCondition.HasTargetValue());
Assert::AreEqual(ra::data::Memory::Size::ThirtyTwoBit, vmCondition.GetTargetSize());
}
TEST_METHOD(TestModifyingConditionUpdatesHasTargetValue)
{
TriggerConditionViewModelHarness vmCondition;
vmCondition.SetType(ra::data::Requirement::Type::AddSource);
Assert::IsFalse(vmCondition.HasTargetValue());
vmCondition.SetOperator(ra::data::Requirement::OperatorType::Divide);
Assert::IsTrue(vmCondition.HasTargetValue());
}
TEST_METHOD(TestHasTargetSizeHasTarget)
{
TriggerConditionViewModelHarness vmCondition;
Assert::AreEqual(ra::data::Requirement::OperandType::Value, vmCondition.GetTargetType());
Assert::IsFalse(vmCondition.HasTargetSize());
vmCondition.SetTargetType(ra::data::Requirement::OperandType::Address);
Assert::AreEqual(ra::data::Requirement::OperandType::Address, vmCondition.GetTargetType());
Assert::IsTrue(vmCondition.HasTargetSize());
vmCondition.SetTargetType(ra::data::Requirement::OperandType::BCD);
Assert::AreEqual(ra::data::Requirement::OperandType::BCD, vmCondition.GetTargetType());
Assert::IsTrue(vmCondition.HasTargetSize());
vmCondition.SetTargetType(ra::data::Requirement::OperandType::Delta);
Assert::AreEqual(ra::data::Requirement::OperandType::Delta, vmCondition.GetTargetType());
Assert::IsTrue(vmCondition.HasTargetSize());
vmCondition.SetTargetType(ra::data::Requirement::OperandType::Prior);
Assert::AreEqual(ra::data::Requirement::OperandType::Prior, vmCondition.GetTargetType());
Assert::IsTrue(vmCondition.HasTargetSize());
vmCondition.SetTargetType(ra::data::Requirement::OperandType::Inverted);
Assert::AreEqual(ra::data::Requirement::OperandType::Inverted, vmCondition.GetTargetType());
Assert::IsTrue(vmCondition.HasTargetSize());
vmCondition.SetTargetType(ra::data::Requirement::OperandType::Recall);
Assert::AreEqual(ra::data::Requirement::OperandType::Recall, vmCondition.GetTargetType());
Assert::IsFalse(vmCondition.HasTargetSize());
vmCondition.SetTargetType(ra::data::Requirement::OperandType::Value);
Assert::AreEqual(ra::data::Requirement::OperandType::Value, vmCondition.GetTargetType());
Assert::IsFalse(vmCondition.HasTargetSize());
}
TEST_METHOD(TestHasHits)
{
TriggerConditionViewModelHarness vmCondition;
Assert::AreEqual(ra::data::Requirement::Type::Standard, vmCondition.GetType());
Assert::IsTrue(vmCondition.HasHits());
Assert::IsTrue(vmCondition.CanEditHits());
vmCondition.SetType(ra::data::Requirement::Type::AddAddress);
Assert::AreEqual(ra::data::Requirement::Type::AddAddress, vmCondition.GetType());
Assert::IsFalse(vmCondition.HasHits());
Assert::IsFalse(vmCondition.CanEditHits());
vmCondition.SetType(ra::data::Requirement::Type::AddHits);
Assert::AreEqual(ra::data::Requirement::Type::AddHits, vmCondition.GetType());
Assert::IsTrue(vmCondition.HasHits());
Assert::IsTrue(vmCondition.CanEditHits());
vmCondition.SetType(ra::data::Requirement::Type::AddSource);
Assert::AreEqual(ra::data::Requirement::Type::AddSource, vmCondition.GetType());
Assert::IsFalse(vmCondition.HasHits());
Assert::IsFalse(vmCondition.CanEditHits());
vmCondition.SetType(ra::data::Requirement::Type::AndNext);
Assert::AreEqual(ra::data::Requirement::Type::AndNext, vmCondition.GetType());
Assert::IsTrue(vmCondition.HasHits());
Assert::IsTrue(vmCondition.CanEditHits());
vmCondition.SetType(ra::data::Requirement::Type::Measured);
Assert::AreEqual(ra::data::Requirement::Type::Measured, vmCondition.GetType());
Assert::IsTrue(vmCondition.HasHits());
Assert::IsTrue(vmCondition.CanEditHits());
vmCondition.SetType(ra::data::Requirement::Type::MeasuredIf);
Assert::AreEqual(ra::data::Requirement::Type::MeasuredIf, vmCondition.GetType());
Assert::IsTrue(vmCondition.HasHits());
Assert::IsTrue(vmCondition.CanEditHits());
vmCondition.SetType(ra::data::Requirement::Type::OrNext);
Assert::AreEqual(ra::data::Requirement::Type::OrNext, vmCondition.GetType());
Assert::IsTrue(vmCondition.HasHits());
Assert::IsTrue(vmCondition.CanEditHits());
vmCondition.SetType(ra::data::Requirement::Type::PauseIf);
Assert::AreEqual(ra::data::Requirement::Type::PauseIf, vmCondition.GetType());
Assert::IsTrue(vmCondition.HasHits());
Assert::IsTrue(vmCondition.CanEditHits());
vmCondition.SetType(ra::data::Requirement::Type::ResetIf);
Assert::AreEqual(ra::data::Requirement::Type::ResetIf, vmCondition.GetType());
Assert::IsTrue(vmCondition.HasHits());
Assert::IsTrue(vmCondition.CanEditHits());
vmCondition.SetType(ra::data::Requirement::Type::SubHits);
Assert::AreEqual(ra::data::Requirement::Type::SubHits, vmCondition.GetType());
Assert::IsTrue(vmCondition.HasHits());
Assert::IsTrue(vmCondition.CanEditHits());
vmCondition.SetType(ra::data::Requirement::Type::SubSource);
Assert::AreEqual(ra::data::Requirement::Type::SubSource, vmCondition.GetType());
Assert::IsFalse(vmCondition.HasHits());
Assert::IsFalse(vmCondition.CanEditHits());
vmCondition.SetType(ra::data::Requirement::Type::Trigger);
Assert::AreEqual(ra::data::Requirement::Type::Trigger, vmCondition.GetType());
Assert::IsTrue(vmCondition.HasHits());
Assert::IsTrue(vmCondition.CanEditHits());
vmCondition.SetType(ra::data::Requirement::Type::Remember);
Assert::AreEqual(ra::data::Requirement::Type::Remember, vmCondition.GetType());
Assert::IsFalse(vmCondition.HasHits());
Assert::IsFalse(vmCondition.CanEditHits());
}
TEST_METHOD(TestHasHitsForValue)
{
TriggerConditionViewModelHarness vmCondition;
TriggerViewModel vmTrigger;
vmTrigger.SetIsValue(true);
vmCondition.SetTriggerViewModel(&vmTrigger);
Assert::AreEqual(ra::data::Requirement::Type::Standard, vmCondition.GetType());
Assert::IsTrue(vmCondition.HasHits());
Assert::IsTrue(vmCondition.CanEditHits());
vmCondition.SetType(ra::data::Requirement::Type::Measured);
Assert::AreEqual(ra::data::Requirement::Type::Measured, vmCondition.GetType());
Assert::AreEqual(ra::data::Requirement::OperatorType::Equals, vmCondition.GetOperator());
Assert::IsTrue(vmCondition.HasHits());
Assert::IsFalse(vmCondition.CanEditHits());
vmCondition.SetOperator(ra::data::Requirement::OperatorType::None);
Assert::AreEqual(ra::data::Requirement::Type::Measured, vmCondition.GetType());
Assert::AreEqual(ra::data::Requirement::OperatorType::None, vmCondition.GetOperator());
Assert::IsFalse(vmCondition.HasHits());
Assert::IsFalse(vmCondition.CanEditHits());
vmCondition.SetType(ra::data::Requirement::Type::Standard);
Assert::AreEqual(ra::data::Requirement::Type::Standard, vmCondition.GetType());
Assert::AreEqual(ra::data::Requirement::OperatorType::Equals, vmCondition.GetOperator());
Assert::IsTrue(vmCondition.HasHits());
Assert::IsTrue(vmCondition.CanEditHits());
vmCondition.SetRequiredHits(5U);
Assert::AreEqual(5U, vmCondition.GetRequiredHits());
vmCondition.SetType(ra::data::Requirement::Type::Measured);
Assert::AreEqual(ra::data::Requirement::Type::Measured, vmCondition.GetType());
Assert::AreEqual(ra::data::Requirement::OperatorType::Equals, vmCondition.GetOperator());
Assert::IsTrue(vmCondition.HasHits());
Assert::IsFalse(vmCondition.CanEditHits());
Assert::AreEqual(0U, vmCondition.GetRequiredHits());
vmCondition.SetOperator(ra::data::Requirement::OperatorType::Multiply);
Assert::AreEqual(ra::data::Requirement::Type::Measured, vmCondition.GetType());
Assert::AreEqual(ra::data::Requirement::OperatorType::Multiply, vmCondition.GetOperator());
Assert::IsFalse(vmCondition.HasHits());
Assert::IsFalse(vmCondition.CanEditHits());
Assert::AreEqual(0U, vmCondition.GetRequiredHits());
}
TEST_METHOD(TestSerializeHasHits)
{
TriggerConditionViewModelHarness vmCondition;
vmCondition.Parse("0xH1234=0xH2345.10.");
std::string sSerialized = vmCondition.Serialize();
Assert::AreEqual(std::string("0xH1234=0xH2345.10."), sSerialized);
// changing to a type that supports hits does not change the hit count
vmCondition.SetType(ra::data::Requirement::Type::AddHits);
sSerialized = vmCondition.Serialize();
Assert::AreEqual(std::string("C:0xH1234=0xH2345.10."), sSerialized);
// changing to a type that does not support hits should remove the hit count (and comparison)
vmCondition.SetType(ra::data::Requirement::Type::AddSource);
sSerialized = vmCondition.Serialize();
Assert::AreEqual(std::string("A:0xH1234"), sSerialized);
// changing back to a type that does support hits should restore the hit count and comparison
// NOTE: editor does not behave this way as the condition is reconstructed from the serialized trigger
vmCondition.SetType(ra::data::Requirement::Type::ResetIf);
sSerialized = vmCondition.Serialize();
Assert::AreEqual(std::string("R:0xH1234=0xH2345.10."), sSerialized);
// changing to a type that does not support hits should remove the hit count (and comparison)
vmCondition.SetType(ra::data::Requirement::Type::AddAddress);
sSerialized = vmCondition.Serialize();
Assert::AreEqual(std::string("I:0xH1234"), sSerialized);
// changing to a type that does not support hits should remove the hit count (and comparison)
vmCondition.SetType(ra::data::Requirement::Type::SubSource);
sSerialized = vmCondition.Serialize();
Assert::AreEqual(std::string("B:0xH1234"), sSerialized);
// changing back to a type that does support hits should restore the hit count and comparison
vmCondition.SetType(ra::data::Requirement::Type::Standard);
sSerialized = vmCondition.Serialize();
Assert::AreEqual(std::string("0xH1234=0xH2345.10."), sSerialized);
// changing to a type that does not support hits should remove the hit count (and comparison)
vmCondition.SetType(ra::data::Requirement::Type::Remember);
sSerialized = vmCondition.Serialize();
Assert::AreEqual(std::string("K:0xH1234"), sSerialized);
}
TEST_METHOD(TestParseAndRegenerateSizes)
{
ParseAndRegenerate("0xH1234=0xH2345"); // 8-bit
ParseAndRegenerate("0x 1234=0x 2345"); // 16-bit
ParseAndRegenerate("0xW1234=0xW2345"); // 24-bit
ParseAndRegenerate("0xX1234=0xX2345"); // 32-bit
ParseAndRegenerate("0xI1234=0xI2345"); // 16-bit BE
ParseAndRegenerate("0xJ1234=0xJ2345"); // 24-bit BE
ParseAndRegenerate("0xG1234=0xG2345"); // 32-bit BE
ParseAndRegenerate("0xU1234=0xU2345"); // upper4
ParseAndRegenerate("0xL1234=0xL2345"); // lower4
ParseAndRegenerate("0xM1234=0xM2345"); // bit0
ParseAndRegenerate("0xN1234=0xN2345"); // bit1
ParseAndRegenerate("0xO1234=0xO2345"); // bit2
ParseAndRegenerate("0xP1234=0xP2345"); // bit3
ParseAndRegenerate("0xQ1234=0xQ2345"); // bit4
ParseAndRegenerate("0xR1234=0xR2345"); // bit5
ParseAndRegenerate("0xS1234=0xS2345"); // bit6
ParseAndRegenerate("0xT1234=0xT2345"); // bit7
ParseAndRegenerate("0xK1234=0xK2345"); // bitcount
ParseAndRegenerate("fF1234=fF2345"); // float
ParseAndRegenerate("fM1234=fM2345"); // mbf32
ParseAndRegenerate("fL1234=fL2345"); // mbf32le
}
TEST_METHOD(TestParseAndRegenerateOperandTypes)
{
ParseAndRegenerate("0xH1234=0xH1234"); // address
ParseAndRegenerate("0xH1234=d0xH1234"); // delta
ParseAndRegenerate("0xH1234=p0xH1234"); // prior
ParseAndRegenerate("0xH1234=b0xH1234"); // bcd
ParseAndRegenerate("0xH1234=~0xH1234"); // inverted
ParseAndRegenerate("{recall}={recall}"); // recall
ParseAndRegenerate("0xH1234=1234"); // value
ParseAndRegenerate("0xH1234=f12.34"); // float
}
TEST_METHOD(TestParseAndRegenerateOperators)
{
ParseAndRegenerate("0xH1234=5"); // equals
ParseAndRegenerate("0xH1234!=5"); // not equals
ParseAndRegenerate("0xH1234<5"); // less than
ParseAndRegenerate("0xH1234<=5"); // less than or equal
ParseAndRegenerate("0xH1234>5"); // greater than
ParseAndRegenerate("0xH1234>=5"); // greater than or equal
ParseAndRegenerate("A:0xH1234"); // none
ParseAndRegenerate("A:0xH1234*5"); // multiply
ParseAndRegenerate("A:0xH1234/5"); // divide
ParseAndRegenerate("A:0xH1234%5"); // modulus
ParseAndRegenerate("A:0xH1234+5"); // addition
ParseAndRegenerate("A:0xH1234-5"); // subtraction
ParseAndRegenerate("A:0xH1234&5"); // bitwise and
}
TEST_METHOD(TestParseAndRegenerateConditionTypes)
{
ParseAndRegenerate("0xH1234=5"); // none
ParseAndRegenerate("R:0xH1234=5"); // reset if
ParseAndRegenerate("Z:0xH1234=5"); // reset next if
ParseAndRegenerate("P:0xH1234=5"); // pause if
ParseAndRegenerate("A:0xH1234"); // add source
ParseAndRegenerate("B:0xH1234"); // sub source
ParseAndRegenerate("C:0xH1234=5"); // add hits
ParseAndRegenerate("D:0xH1234=5"); // sub hits
ParseAndRegenerate("N:0xH1234=5"); // and next
ParseAndRegenerate("K:0xH1234"); // remember
ParseAndRegenerate("O:0xH1234=5"); // or next
ParseAndRegenerate("M:0xH1234=5"); // measured
ParseAndRegenerate("Q:0xH1234=5"); // measured if
ParseAndRegenerate("I:0xH1234"); // add address
ParseAndRegenerate("T:0xH1234=5"); // trigger
}
TEST_METHOD(TestParseAndRegenerateHits)
{
ParseAndRegenerate("R:0xH1234=5.100."); // flag and 100 hits
}
TEST_METHOD(TestParseAndRegenerateValue)
{
ParseAndRegenerateValue("M:0xH1234=5"); // measured without hit target
ParseAndRegenerateValue("M:0xH1234"); // measured without comparison
ParseAndRegenerateValue("M:0xH1234/5"); // measured with modifier
}
TEST_METHOD(TestTooltipAddress)
{
TriggerConditionViewModelHarness condition;
condition.mockGameContext.SetNote({ 8U }, L"This is a note.");
condition.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
condition.Parse("0xH0008=3");
Assert::AreEqual(std::wstring(L"0x0008\r\nThis is a note."), condition.GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L""), condition.GetTooltip(TriggerConditionViewModel::TargetValueProperty));
}
TEST_METHOD(TestTooltipAddressNoMemoryNote)
{
TriggerConditionViewModelHarness condition;
condition.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
condition.Parse("0xH0008=3");
Assert::AreEqual(std::wstring(L"0x0008\r\n[No memory note]"), condition.GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L""), condition.GetTooltip(TriggerConditionViewModel::TargetValueProperty));
}
TEST_METHOD(TestTooltipAddressLongMemoryNote)
{
TriggerConditionViewModelHarness condition;
condition.mockGameContext.SetNote({ 8U }, L"A\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nL\nM\nO\nP\nQ\nR\nS\nT\nU\nV\nW\nX\nY\nZ");
condition.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
condition.Parse("0xH0008=3");
Assert::AreEqual(std::wstring(L"0x0008\r\nA\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nL\nM\nO\nP\nQ\nR\nS\nT\nU\n..."), condition.GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L""), condition.GetTooltip(TriggerConditionViewModel::TargetValueProperty));
}
TEST_METHOD(TestTooltipAddressMultiNote)
{
TriggerConditionViewModelHarness condition;
condition.mockGameContext.SetNote({ 8U }, L"This is a note.");
condition.mockGameContext.SetNote({ 9U }, L"This is another note.");
condition.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
condition.Parse("0xH0008>d0xH0009");
Assert::AreEqual(std::wstring(L"0x0008\r\nThis is a note."), condition.GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0009\r\nThis is another note."), condition.GetTooltip(TriggerConditionViewModel::TargetValueProperty));
}
TEST_METHOD(TestTooltipAddressMultiByteNote)
{
TriggerConditionViewModelHarness condition;
condition.mockGameContext.SetNote({ 8U }, L"[8 bytes] This is a note.");
condition.Parse("0xH0008=3");
Assert::AreEqual(std::wstring(L"0x0008\r\n[8 bytes] This is a note."), condition.GetTooltip(TriggerConditionViewModel::SourceValueProperty));
condition.Parse("0xH000C=3");
Assert::AreEqual(std::wstring(L"0x000c (0x0008+4)\r\n[8 bytes] This is a note."), condition.GetTooltip(TriggerConditionViewModel::SourceValueProperty));
}
TEST_METHOD(TestTooltipAddressLargeArrayNote)
{
TriggerConditionViewModelHarness condition;
condition.mockGameContext.SetNote({ 8U }, L"[100 bytes] This is a note.");
condition.Parse("0xH0008=3");
Assert::AreEqual(std::wstring(L"0x0008\r\n[100 bytes] This is a note."), condition.GetTooltip(TriggerConditionViewModel::SourceValueProperty));
condition.Parse("0xH000C=3");
Assert::AreEqual(std::wstring(L"0x000c (0x0008+4)\r\n[100 bytes] This is a note."), condition.GetTooltip(TriggerConditionViewModel::SourceValueProperty));
condition.Parse("0xH0016=3");
Assert::AreEqual(std::wstring(L"0x0016 (0x0008+0x0e)\r\n[100 bytes] This is a note."), condition.GetTooltip(TriggerConditionViewModel::SourceValueProperty));
condition.Parse("0xH003E=3");
Assert::AreEqual(std::wstring(L"0x003e (0x0008+0x36)\r\n[100 bytes] This is a note."), condition.GetTooltip(TriggerConditionViewModel::SourceValueProperty));
}
TEST_METHOD(TestTooltipAddressBitNote)
{
TriggerConditionViewModelHarness condition;
condition.mockGameContext.SetNote({ 8U }, L"b1=Left\r\nb2=Right\r\nb3=Upper\r\n");
condition.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
condition.Parse("0xO0008=1");
Assert::AreEqual(std::wstring(L"0x0008\r\nb2=Right"), condition.GetTooltip(TriggerConditionViewModel::SourceValueProperty));
}
TEST_METHOD(TestTooltipAddressBitNoteWithHeader)
{
TriggerConditionViewModelHarness condition;
condition.mockGameContext.SetNote({ 8U }, L"Chests in first dungeon\r\nb1=Left\r\nb2=Right\r\nb3=Upper\r\n");
condition.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
condition.Parse("0xO0008=1");
Assert::AreEqual(std::wstring(L"0x0008\r\nChests in first dungeon\r\nb2=Right"), condition.GetTooltip(TriggerConditionViewModel::SourceValueProperty));
}
TEST_METHOD(TestTooltipValueDecimal)
{
TriggerConditionViewModelHarness condition;
condition.mockGameContext.SetNote({ 99U }, L"This is a note.");
condition.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
condition.Parse("255=99");
Assert::AreEqual(std::wstring(L""), condition.GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L""), condition.GetTooltip(TriggerConditionViewModel::TargetValueProperty));
}
TEST_METHOD(TestTooltipValueHex)
{
TriggerConditionViewModelHarness condition;
condition.mockGameContext.SetNote({ 99U }, L"This is a note.");
condition.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, false);
condition.Parse("255=99");
// in hex mode, the fields will show "0xFF" and "0x63", so display the decimal value in the tooltip
Assert::AreEqual(std::wstring(L"255"), condition.GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"99"), condition.GetTooltip(TriggerConditionViewModel::TargetValueProperty));
}
TEST_METHOD(TestTooltipValueEnumPreferDecimal)
{
TriggerConditionViewModelHarness condition;
condition.mockGameContext.SetNote({ 0x0099U }, L"Color {20=Red, 24=Brown}");
condition.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
condition.Parse("0xH0099=20");
Assert::AreEqual(std::wstring(L"20"), condition.GetTargetValue());
Assert::AreEqual(std::wstring(L"0x0099\r\nColor {20=Red, 24=Brown}"), condition.GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"20=Red"), condition.GetTooltip(TriggerConditionViewModel::TargetValueProperty));
}
TEST_METHOD(TestTooltipValueEnumPreferHex)
{
TriggerConditionViewModelHarness condition;
condition.mockGameContext.SetNote({ 0x0099U }, L"Color {20=Red, 24=Brown}");
condition.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, false);
condition.Parse("0xH0099=20");
Assert::AreEqual(std::wstring(L"0x14"), condition.GetTargetValue());
Assert::AreEqual(std::wstring(L"0x0099\r\nColor {20=Red, 24=Brown}"), condition.GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"20=Red"), condition.GetTooltip(TriggerConditionViewModel::TargetValueProperty));
}
TEST_METHOD(TestTooltipValueEnumPreferDecimalHexNote)
{
TriggerConditionViewModelHarness condition;
condition.mockGameContext.SetNote({ 0x0099U }, L"Color {0x14=Red, 0x18=Brown}");
condition.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
condition.Parse("0xH0099=20");
Assert::AreEqual(std::wstring(L"20"), condition.GetTargetValue());
Assert::AreEqual(std::wstring(L"0x0099\r\nColor {0x14=Red, 0x18=Brown}"), condition.GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x14=Red"), condition.GetTooltip(TriggerConditionViewModel::TargetValueProperty));
}
TEST_METHOD(TestTooltipValueEnumPreferHexHexNote)
{
TriggerConditionViewModelHarness condition;
condition.mockGameContext.SetNote({ 0x0099U }, L"Color {0x14=Red, 0x18=Brown}");
condition.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, false);
condition.Parse("0xH0099=20");
Assert::AreEqual(std::wstring(L"0x14"), condition.GetTargetValue());
Assert::AreEqual(std::wstring(L"0x0099\r\nColor {0x14=Red, 0x18=Brown}"), condition.GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x14=Red"), condition.GetTooltip(TriggerConditionViewModel::TargetValueProperty));
}
private:
class IndirectAddressTriggerViewModelHarness : public TriggerViewModel
{
public:
ra::context::mocks::MockEmulatorMemoryContext mockEmulatorMemoryContext;
ra::data::context::mocks::MockGameContext mockGameContext;
ra::context::mocks::MockUserContext mockUserContext;
ra::services::mocks::MockConfiguration mockConfiguration;
void Parse(const std::string& sInput)
{
mockEmulatorMemoryContext.MockMemory(m_pMemory);
InitializeFrom(sInput);
DoFrame();
}
void SetMemory(size_t nAddress, unsigned char nValue)
{
m_pMemory.at(nAddress) = nValue;
}
void MemoryNotesDoFrame()
{
auto* pMemoryNotes = mockGameContext.Assets().FindMemoryNotes();
if (pMemoryNotes != nullptr)
pMemoryNotes->DoFrame();
DoFrame();
}
using TriggerViewModel::EnsureVisible;
private:
std::array<unsigned char, 10> m_pMemory = { 0,1,2,3,4,5,6,7,8,9 };
};
public:
TEST_METHOD(TestTooltipIndirectAddressNoMemoryNote)
{
IndirectAddressTriggerViewModelHarness vmTrigger;
vmTrigger.Parse("I:0xH0001_0xH0002=3");
vmTrigger.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
const auto* pCondition = vmTrigger.Conditions().GetItemAt(1);
Expects(pCondition != nullptr);
Assert::IsTrue(pCondition->IsIndirect());
// $0001 = 1, 1+2 = $0003
Assert::AreEqual(std::wstring(L"0x0003 (indirect $0x0001+0x02)\r\n[No memory note]"), pCondition->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
// $0001 = 3, 3+2 = $0005
vmTrigger.SetMemory({ 1 }, 3);
vmTrigger.DoFrame();
Assert::AreEqual(std::wstring(L"0x0005 (indirect $0x0001+0x02)\r\n[No memory note]"), pCondition->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
}
TEST_METHOD(TestTooltipIndirectAddressMemoryNote)
{
IndirectAddressTriggerViewModelHarness vmTrigger;
vmTrigger.Parse("I:0xH0001_0xH0002=3");
vmTrigger.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
vmTrigger.mockGameContext.SetNote({ 1U }, L"[8-bit pointer]\n+2=This is a note.");
const auto* pCondition = vmTrigger.Conditions().GetItemAt(1);
Expects(pCondition != nullptr);
Assert::IsTrue(pCondition->IsIndirect());
// $0001 = 1, 1+2 = $0003
Assert::AreEqual(std::wstring(L"0x0003 (indirect $0x0001+0x02)\r\nThis is a note."), pCondition->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
// $0001 = 3, 3+2 = $0005
vmTrigger.SetMemory({ 1 }, 3);
vmTrigger.MemoryNotesDoFrame();
Assert::AreEqual(std::wstring(L"0x0005 (indirect $0x0001+0x02)\r\nThis is a note."), pCondition->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
}
TEST_METHOD(TestTooltipIndirectAddressMultiNote)
{
IndirectAddressTriggerViewModelHarness vmTrigger;
vmTrigger.Parse("I:0xH0001_0xH0002=0xH0004");
vmTrigger.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
vmTrigger.mockGameContext.SetNote({ 1U }, L"[8-bit pointer]\n+2=This is a note.\n+4=This is another note.");
const auto* pCondition = vmTrigger.Conditions().GetItemAt(1);
Expects(pCondition != nullptr);
Assert::IsTrue(pCondition->IsIndirect());
// $0001 = 1, 1+2 = $0003, 1+4 = $0005
Assert::AreEqual(std::wstring(L"0x0003 (indirect $0x0001+0x02)\r\nThis is a note."), pCondition->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0005 (indirect $0x0001+0x04)\r\nThis is another note."), pCondition->GetTooltip(TriggerConditionViewModel::TargetValueProperty));
// $0001 = 3, 3+2 = $0005, 3+4 = $0007
vmTrigger.SetMemory({ 1 }, 3);
vmTrigger.MemoryNotesDoFrame();
Assert::AreEqual(std::wstring(L"0x0005 (indirect $0x0001+0x02)\r\nThis is a note."), pCondition->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0007 (indirect $0x0001+0x04)\r\nThis is another note."), pCondition->GetTooltip(TriggerConditionViewModel::TargetValueProperty));
}
TEST_METHOD(TestTooltipIndirectAddressMultiByteNote)
{
IndirectAddressTriggerViewModelHarness vmTrigger;
vmTrigger.Parse("I:0xH0001_0xH0002=0xH0005");
vmTrigger.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
vmTrigger.mockGameContext.SetNote({ 1U }, L"[8-bit pointer]\n+4=[8 bytes] This is a note.");
const auto* pCondition = vmTrigger.Conditions().GetItemAt(1);
Expects(pCondition != nullptr);
Assert::IsTrue(pCondition->IsIndirect());
// $0001 = 1, 1+2 = $0003, 1+5 = $0006
Assert::AreEqual(std::wstring(L"0x0003 (indirect $0x0001+0x02)\r\n[No memory note]"), pCondition->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0006 (indirect $0x0001+0x04+1)\r\n[8 bytes] This is a note."), pCondition->GetTooltip(TriggerConditionViewModel::TargetValueProperty));
}
TEST_METHOD(TestTooltipIndirectAddressMultiplyNoMemoryNote)
{
IndirectAddressTriggerViewModelHarness vmTrigger;
vmTrigger.Parse("I:0xH0001*3_0xH0002=3");
vmTrigger.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
const auto* pCondition = vmTrigger.Conditions().GetItemAt(1);
Expects(pCondition != nullptr);
Assert::IsTrue(pCondition->IsIndirect());
// $0001 = 1, 1*3+2 = $0005
Assert::AreEqual(std::wstring(L"0x0005 (indirect $0x0001*3+0x02)\r\n[No memory note]"), pCondition->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
// $0001 = 3, 3*3+2 = $000B
vmTrigger.SetMemory({ 1 }, 3);
vmTrigger.MemoryNotesDoFrame();
Assert::AreEqual(std::wstring(L"0x000b (indirect $0x0001*3+0x02)\r\n[No memory note]"), pCondition->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
}
TEST_METHOD(TestTooltipIndirectAddressWithAltMemoryNote)
{
IndirectAddressTriggerViewModelHarness vmTrigger;
vmTrigger.Parse("I:0xH0001_0xH0002=3S0=0S1=1");
vmTrigger.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
vmTrigger.mockGameContext.SetNote({ 1U }, L"[8-bit pointer]\n+2=This is a note.");
const auto* pCondition = vmTrigger.Conditions().GetItemAt(1);
Expects(pCondition != nullptr);
Assert::IsTrue(pCondition->IsIndirect());
// $0001 = 1, 1+2 = $0003
Assert::AreEqual(std::wstring(L"0x0003 (indirect $0x0001+0x02)\r\nThis is a note."), pCondition->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
// $0001 = 3, 3+2 = $0005
vmTrigger.SetMemory({ 1 }, 3);
vmTrigger.MemoryNotesDoFrame();
Assert::AreEqual(std::wstring(L"0x0005 (indirect $0x0001+0x02)\r\nThis is a note."),
pCondition->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
}
TEST_METHOD(TestTooltipIndirectAddressInAlt1MemoryNote)
{
IndirectAddressTriggerViewModelHarness vmTrigger;
vmTrigger.Parse("0=0SI:0xH0001_0xH0002=3S1=1");
vmTrigger.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
vmTrigger.mockGameContext.SetNote({ 1U }, L"[8-bit pointer]\n+2=This is a note.");
vmTrigger.SetSelectedGroupIndex(1);
const auto* pCondition = vmTrigger.Conditions().GetItemAt(1);
Expects(pCondition != nullptr);
Assert::IsTrue(pCondition->IsIndirect());
// $0001 = 1, 1+2 = $0003
Assert::AreEqual(std::wstring(L"0x0003 (indirect $0x0001+0x02)\r\nThis is a note."), pCondition->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
// $0001 = 3, 3+2 = $0005
vmTrigger.SetMemory({ 1 }, 3);
vmTrigger.MemoryNotesDoFrame();
Assert::AreEqual(std::wstring(L"0x0005 (indirect $0x0001+0x02)\r\nThis is a note."), pCondition->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
}
TEST_METHOD(TestTooltipIndirectAddressInAlt2MemoryNote)
{
IndirectAddressTriggerViewModelHarness vmTrigger;
vmTrigger.Parse("0=0S1=1SI:0xH0001_0xH0002=3");
vmTrigger.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
vmTrigger.mockGameContext.SetNote({ 1U }, L"[8-bit pointer]\n+2=This is a note.");
vmTrigger.SetSelectedGroupIndex(2);
const auto* pCondition = vmTrigger.Conditions().GetItemAt(1);
Expects(pCondition != nullptr);
Assert::IsTrue(pCondition->IsIndirect());
// $0001 = 1, 1+2 = $0003
Assert::AreEqual(std::wstring(L"0x0003 (indirect $0x0001+0x02)\r\nThis is a note."), pCondition->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
// $0001 = 3, 3+2 = $0005
vmTrigger.SetMemory({ 1 }, 3);
vmTrigger.MemoryNotesDoFrame();
Assert::AreEqual(std::wstring(L"0x0005 (indirect $0x0001+0x02)\r\nThis is a note."),
pCondition->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
}
TEST_METHOD(TestTooltipIndirectAddressMemoryNoteOverflow)
{
IndirectAddressTriggerViewModelHarness vmTrigger;
ra::context::mocks::MockConsoleContext mockConsoleContext;
vmTrigger.Parse("I:0xX0000_0xH80000002=6");
vmTrigger.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
vmTrigger.mockGameContext.SetNote({0U}, L"[32-bit pointer]\n+0x80000002=This is a note.");
const auto* pCondition = vmTrigger.Conditions().GetItemAt(1);
Expects(pCondition != nullptr);
Assert::IsTrue(pCondition->IsIndirect());
// $0001 = 0x80000003, 0x80000003+0x80000002 = 0x100000005 (too big for uint32_t) -> truncated to $0005
vmTrigger.SetMemory({0}, 0x03);
vmTrigger.SetMemory({1}, 0x00);
vmTrigger.SetMemory({2}, 0x00);
vmTrigger.SetMemory({3}, 0x80);
vmTrigger.MemoryNotesDoFrame();
Assert::AreEqual(std::wstring(L"0x0005 (indirect $0x0000+0x80000002)\r\nThis is a note."),
pCondition->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
// $0001 = 0x80000004, 0x8000004+0x80000002 = 0x100000006 (too big for uint32_t) -> truncated to $0006
vmTrigger.SetMemory({0}, 4);
vmTrigger.MemoryNotesDoFrame();
Assert::AreEqual(std::wstring(L"0x0006 (indirect $0x0000+0x80000002)\r\nThis is a note."),
pCondition->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
}
TEST_METHOD(TestTooltipIndirectAddressMemoryNoteMasked)
{
IndirectAddressTriggerViewModelHarness vmTrigger;
ra::context::mocks::MockConsoleContext mockConsoleContext(ConsoleID::PSP, L"PSP");
vmTrigger.Parse("I:0xX0000&33554431_0xH0002=6"); // 33554431 = 0x01FFFFFF
vmTrigger.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
vmTrigger.mockGameContext.SetNote({0U}, L"[32-bit pointer]\n+2=This is a note.");
const auto* pCondition = vmTrigger.Conditions().GetItemAt(1);
Expects(pCondition != nullptr);
Assert::IsTrue(pCondition->IsIndirect());
// $0001 = 0x08000003, 0x80000003&0x01FFFFFF=0x00000003+2 = 0x00000005
vmTrigger.SetMemory({0}, 0x03);
vmTrigger.SetMemory({1}, 0x00);
vmTrigger.SetMemory({2}, 0x00);
vmTrigger.SetMemory({3}, 0x08);
vmTrigger.MemoryNotesDoFrame();
Assert::AreEqual(std::wstring(L"0x0005 (indirect $0x0000+0x02)\r\nThis is a note."),
pCondition->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
// $0001 = 0x80000004, 0x8000004+0x80000002 = 0x100000006 (too big for uint32_t) -> truncated to $0006
vmTrigger.SetMemory({0}, 4);
vmTrigger.MemoryNotesDoFrame();
Assert::AreEqual(std::wstring(L"0x0006 (indirect $0x0000+0x02)\r\nThis is a note."),
pCondition->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
}
TEST_METHOD(TestTooltipDoubleIndirectAddress)
{
IndirectAddressTriggerViewModelHarness vmTrigger;
ra::context::mocks::MockConsoleContext mockConsoleContext;
vmTrigger.mockGameContext.InitializeNotes();
vmTrigger.Parse("I:0xH0001_I:0xH0002_0xH0003=4");
vmTrigger.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
vmTrigger.mockGameContext.SetNote({1U}, L"[8-bit pointer]\n+2=First Level [8-bit pointer]\n +3=Second Level.");
const auto* pCondition1 = vmTrigger.Conditions().GetItemAt(0);
Expects(pCondition1 != nullptr);
const auto* pCondition2 = vmTrigger.Conditions().GetItemAt(1);
Expects(pCondition2 != nullptr);
const auto* pCondition3 = vmTrigger.Conditions().GetItemAt(2);
Expects(pCondition3 != nullptr);
Assert::IsFalse(pCondition1->IsIndirect());
Assert::IsTrue(pCondition2->IsIndirect());
Assert::IsTrue(pCondition3->IsIndirect());
// $0001 = 1, 1+2 = $0003, $0003 = 3, 3+3 = $0006
Assert::AreEqual(std::wstring(L"0x0001\r\n[8-bit pointer]"), pCondition1->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0003 (indirect $0x0001+0x02)\r\nFirst Level [8-bit pointer]"), pCondition2->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0006 (indirect $0x0001+0x02+0x03)\r\nSecond Level."), pCondition3->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
// $0001 = 3, 3+2 = $0005, $0005 = 5, 5+3 = $0008
vmTrigger.SetMemory({ 1 }, 3);
vmTrigger.MemoryNotesDoFrame();
Assert::AreEqual(std::wstring(L"0x0001\r\n[8-bit pointer]"), pCondition1->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0005 (indirect $0x0001+0x02)\r\nFirst Level [8-bit pointer]"), pCondition2->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0008 (indirect $0x0001+0x02+0x03)\r\nSecond Level."), pCondition3->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
}
TEST_METHOD(TestTooltipDoubleIndirectAddressExternal)
{
IndirectAddressTriggerViewModelHarness vmTrigger;
ra::context::mocks::MockConsoleContext mockConsoleContext;
vmTrigger.Parse("I:0xH0001_I:0xH0002_0xH0003=4");
vmTrigger.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
// Neat trick! Get the trigger managed by the viewmodel, and reinitialize the viewmodel using it.
// It'll rebuild itself as if the trigger was external (i.e. from the runtime), but without destroying it.
// This leverages the fact that InitializeFrom only clears out the pointer, not the buffer.
auto* pTrigger = vmTrigger.GetTriggerFromString();
Expects(pTrigger != nullptr);
vmTrigger.InitializeFrom(*pTrigger);
// manually update the memrefs as if rc_runtime_do_frame where called
auto* memrefs = rc_trigger_get_memrefs(pTrigger);
rc_update_memref_values(memrefs, rc_peek_callback, nullptr);
const auto* pCondition1 = vmTrigger.Conditions().GetItemAt(0);
Expects(pCondition1 != nullptr);
const auto* pCondition2 = vmTrigger.Conditions().GetItemAt(1);
Expects(pCondition2 != nullptr);
const auto* pCondition3 = vmTrigger.Conditions().GetItemAt(2);
Expects(pCondition3 != nullptr);
Assert::IsFalse(pCondition1->IsIndirect());
Assert::IsTrue(pCondition2->IsIndirect());
Assert::IsTrue(pCondition3->IsIndirect());
// $0001 = 1, 1+2 = $0003, $0003 = 3, 3+3 = $0006
Assert::AreEqual(std::wstring(L"0x0001\r\n[No memory note]"), pCondition1->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0003 (indirect $0x0001+0x02)\r\n[No memory note]"), pCondition2->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0006 (indirect $0x0001+0x02+0x03)\r\n[No memory note]"), pCondition3->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
// Calling GetTooltip on a viewmodel attached to an external trigger should not evaluate the memrefs.
// External memrefs should only be updated by the runtime to ensure delta values are correct.
vmTrigger.SetMemory({ 1 }, 3);
// Manually update the memrefs as if rc_runtime_do_frame where called
rc_update_memref_values(memrefs, rc_peek_callback, nullptr);
// $0001 = 3, 3+2 = $0005, $0005 = 5, 5+3 = $0008
Assert::AreEqual(std::wstring(L"0x0001\r\n[No memory note]"), pCondition1->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0005 (indirect $0x0001+0x02)\r\n[No memory note]"), pCondition2->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0008 (indirect $0x0001+0x02+0x03)\r\n[No memory note]"), pCondition3->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
}
TEST_METHOD(TestTooltipDoubleIndirectAddressValue)
{
IndirectAddressTriggerViewModelHarness vmTrigger;
ra::context::mocks::MockConsoleContext mockConsoleContext;
vmTrigger.SetIsValue(true);
vmTrigger.Parse("I:0xH0001_I:0xH0002_M:0xH0003=4");
vmTrigger.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
const auto* pCondition1 = vmTrigger.Conditions().GetItemAt(0);
Expects(pCondition1 != nullptr);
const auto* pCondition2 = vmTrigger.Conditions().GetItemAt(1);
Expects(pCondition2 != nullptr);
const auto* pCondition3 = vmTrigger.Conditions().GetItemAt(2);
Expects(pCondition3 != nullptr);
Assert::IsFalse(pCondition1->IsIndirect());
Assert::IsTrue(pCondition2->IsIndirect());
Assert::IsTrue(pCondition3->IsIndirect());
// $0001 = 1, 1+2 = $0003, $0003 = 3, 3+3 = $0006
Assert::AreEqual(std::wstring(L"0x0001\r\n[No memory note]"), pCondition1->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0003 (indirect $0x0001+0x02)\r\n[No memory note]"), pCondition2->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0006 (indirect $0x0001+0x02+0x03)\r\n[No memory note]"), pCondition3->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
// $0001 = 3, 3+2 = $0005, $0005 = 5, 5+3 = $0008
vmTrigger.SetMemory({ 1 }, 3);
vmTrigger.MemoryNotesDoFrame();
Assert::AreEqual(std::wstring(L"0x0001\r\n[No memory note]"),
pCondition1->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0005 (indirect $0x0001+0x02)\r\n[No memory note]"), pCondition2->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0008 (indirect $0x0001+0x02+0x03)\r\n[No memory note]"), pCondition3->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
}
TEST_METHOD(TestTooltipIndirectAddressOffsetMemoryNote)
{
IndirectAddressTriggerViewModelHarness vmTrigger;
vmTrigger.Parse("I:0xH0001_0xH0002=3");
vmTrigger.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
vmTrigger.mockGameContext.SetNote({ 1U }, L"Region differentiator");
vmTrigger.mockGameContext.SetNote({ 2U }, L"[US] Note for NA");
const auto* pCondition = vmTrigger.Conditions().GetItemAt(1);
Expects(pCondition != nullptr);
Assert::IsTrue(pCondition->IsIndirect());
// $0001 = 1, 1+2 = $0003
Assert::AreEqual(std::wstring(L"0x0003 (indirect 0x0002[$0x0001])\r\n[US] Note for NA"), pCondition->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
// $0001 = 3, 3+2 = $0005
vmTrigger.SetMemory({ 1 }, 3);
vmTrigger.MemoryNotesDoFrame();
Assert::AreEqual(std::wstring(L"0x0005 (indirect 0x0002[$0x0001])\r\n[US] Note for NA"), pCondition->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
}
TEST_METHOD(TestTooltipIndirectAddressScaledOffsetMemoryNote)
{
IndirectAddressTriggerViewModelHarness vmTrigger;
vmTrigger.Parse("I:0xH0001*2_0xH0002=3");
vmTrigger.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
vmTrigger.mockGameContext.SetNote({ 1U }, L"Region differentiator");
vmTrigger.mockGameContext.SetNote({ 2U }, L"[US] Note for NA");
const auto* pCondition = vmTrigger.Conditions().GetItemAt(1);
Expects(pCondition != nullptr);
Assert::IsTrue(pCondition->IsIndirect());
// $0001 = 1, 1*2+2 = $0004
Assert::AreEqual(std::wstring(L"0x0004 (indirect 0x0002[$0x0001*2])\r\n[US] Note for NA"), pCondition->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
// $0001 = 3, 3*2+2 = $0008
vmTrigger.SetMemory({ 1 }, 3);
vmTrigger.MemoryNotesDoFrame();
Assert::AreEqual(std::wstring(L"0x0008 (indirect 0x0002[$0x0001*2])\r\n[US] Note for NA"), pCondition->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
}
TEST_METHOD(TestTooltipRecallBasic)
{
IndirectAddressTriggerViewModelHarness vmTrigger;
vmTrigger.mockGameContext.InitializeNotes();
vmTrigger.Parse("K:0xH0001_I:{recall}_I:0xH0002_0xH0003=4");
vmTrigger.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
vmTrigger.mockGameContext.SetNote({1U}, L"[8-bit pointer]\n+2=First Level (8-bit pointer)\n +3=Second Level.");
const auto* pCondition1 = vmTrigger.Conditions().GetItemAt(0);
Expects(pCondition1 != nullptr);
const auto* pCondition2 = vmTrigger.Conditions().GetItemAt(1);
Expects(pCondition2 != nullptr);
const auto* pCondition3 = vmTrigger.Conditions().GetItemAt(2);
Expects(pCondition3 != nullptr);
const auto* pCondition4 = vmTrigger.Conditions().GetItemAt(3);
Expects(pCondition4 != nullptr);
Assert::IsFalse(pCondition1->IsIndirect());
Assert::IsFalse(pCondition2->IsIndirect());
Assert::IsTrue(pCondition3->IsIndirect());
Assert::IsTrue(pCondition4->IsIndirect());
// $0001 = 1, 1+2 = $0003, $0003 = 3, 3+3 = $0006
Assert::AreEqual(std::wstring(L""), pCondition2->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0003 (indirect {recall:0x01}+0x02)\r\nFirst Level (8-bit pointer)"), pCondition3->GetTooltip(TriggerConditionViewModel::SourceValueProperty)); //Kind of a quirk because recall value is a code-noted address.
Assert::AreEqual(std::wstring(L"0x0006 (indirect {recall:0x01}+0x02+0x03)\r\nSecond Level."), pCondition4->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
// $0001 = 3, 3+2 = $0005, $0005 = 5, 5+3 = $0008
vmTrigger.SetMemory({ 1 }, 3);
vmTrigger.MemoryNotesDoFrame();
Assert::AreEqual(std::wstring(L"0x0005 (indirect {recall:0x03}+0x02)\r\nFirst Level (8-bit pointer)"), pCondition3->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0008 (indirect {recall:0x03}+0x02+0x03)\r\nSecond Level."), pCondition4->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
}
TEST_METHOD(TestTooltipRecallAddSource)
{
IndirectAddressTriggerViewModelHarness vmTrigger;
vmTrigger.mockGameContext.InitializeNotes();
vmTrigger.Parse("A:1_K:0xH0001_I:{recall}_I:0xH0002_0xH0003=4");
vmTrigger.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
const auto* pCondition1 = vmTrigger.Conditions().GetItemAt(0);
Expects(pCondition1 != nullptr);
const auto* pCondition2 = vmTrigger.Conditions().GetItemAt(1);
Expects(pCondition2 != nullptr);
const auto* pCondition3 = vmTrigger.Conditions().GetItemAt(2);
Expects(pCondition3 != nullptr);
const auto* pCondition4 = vmTrigger.Conditions().GetItemAt(3);
Expects(pCondition4 != nullptr);
const auto* pCondition5 = vmTrigger.Conditions().GetItemAt(4);
Expects(pCondition5 != nullptr);
Assert::IsFalse(pCondition1->IsIndirect());
Assert::IsFalse(pCondition2->IsIndirect());
Assert::IsFalse(pCondition3->IsIndirect());
Assert::IsTrue(pCondition4->IsIndirect());
Assert::IsTrue(pCondition5->IsIndirect());
// $0001 = 1, 1+1+2 = $0004, $0004 = 4, 4+3 = $0007
Assert::AreEqual(std::wstring(L"0x0004 (indirect {recall:0x02}+0x02)\r\n[No memory note]"),
pCondition4->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0007 (indirect {recall:0x02}+0x02+0x03)\r\n[No memory note]"),
pCondition5->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
// $0001 = 3, 1+3+2 = $0006, $0006 = 6, 6+3 = $0009
vmTrigger.SetMemory({ 1 }, 3);
vmTrigger.MemoryNotesDoFrame();
Assert::AreEqual(std::wstring(L"0x0006 (indirect {recall:0x04}+0x02)\r\n[No memory note]"), pCondition4->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0009 (indirect {recall:0x04}+0x02+0x03)\r\n[No memory note]"), pCondition5->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
}
TEST_METHOD(TestTooltipRecallSubSource)
{
IndirectAddressTriggerViewModelHarness vmTrigger;
vmTrigger.mockGameContext.InitializeNotes();
vmTrigger.Parse("B:2_K:0xH0003_I:{recall}_I:0xH0002_0xH0003=4");
vmTrigger.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
const auto* pCondition1 = vmTrigger.Conditions().GetItemAt(0);
Expects(pCondition1 != nullptr);
const auto* pCondition2 = vmTrigger.Conditions().GetItemAt(1);
Expects(pCondition2 != nullptr);
const auto* pCondition3 = vmTrigger.Conditions().GetItemAt(2);
Expects(pCondition3 != nullptr);
const auto* pCondition4 = vmTrigger.Conditions().GetItemAt(3);
Expects(pCondition4 != nullptr);
const auto* pCondition5 = vmTrigger.Conditions().GetItemAt(4);
Expects(pCondition5 != nullptr);
Assert::IsFalse(pCondition1->IsIndirect());
Assert::IsFalse(pCondition2->IsIndirect());
Assert::IsFalse(pCondition3->IsIndirect());
Assert::IsTrue(pCondition4->IsIndirect());
Assert::IsTrue(pCondition5->IsIndirect());
// $0003 = 3, 3-2+2 = $0003, $0003 = 3, 3+3 = $0006
Assert::AreEqual(std::wstring(L"0x0003 (indirect {recall:0x01}+0x02)\r\n[No memory note]"), pCondition4->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0006 (indirect {recall:0x01}+0x02+0x03)\r\n[No memory note]"), pCondition5->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
// $0003 = 5, 5-2 +2 = $0005, 5+4 = $0008
vmTrigger.SetMemory({ 3 }, 5);
vmTrigger.MemoryNotesDoFrame();
Assert::AreEqual(std::wstring(L"0x0005 (indirect {recall:0x03}+0x02)\r\n[No memory note]"), pCondition4->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0008 (indirect {recall:0x03}+0x02+0x03)\r\n[No memory note]"), pCondition5->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
}
TEST_METHOD(TestTooltipRecallPauseIfEarly)
{
IndirectAddressTriggerViewModelHarness vmTrigger;
vmTrigger.mockGameContext.InitializeNotes();
vmTrigger.Parse("K:0xH0001_P:0=1_I:{recall}_I:0xH0002_0xH0003=4");
vmTrigger.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
vmTrigger.mockGameContext.SetNote({ 1U }, L"[8-bit pointer]\n+2=First Level (8-bit pointer)\n +3=Second Level.");
const auto* pCondition1 = vmTrigger.Conditions().GetItemAt(0);
Expects(pCondition1 != nullptr);
const auto* pCondition2 = vmTrigger.Conditions().GetItemAt(1);
Expects(pCondition2 != nullptr);
const auto* pCondition3 = vmTrigger.Conditions().GetItemAt(2);
Expects(pCondition3 != nullptr);
const auto* pCondition4 = vmTrigger.Conditions().GetItemAt(3);
Expects(pCondition4 != nullptr);
const auto* pCondition5 = vmTrigger.Conditions().GetItemAt(4);
Expects(pCondition5 != nullptr);
Assert::IsFalse(pCondition1->IsIndirect());
Assert::IsFalse(pCondition2->IsIndirect());
Assert::IsFalse(pCondition3->IsIndirect());
Assert::IsTrue(pCondition4->IsIndirect());
Assert::IsTrue(pCondition5->IsIndirect());
// $0001 = 1, 1+2 = $0003, $0003 = 3, 3+3 = $0006
Assert::AreEqual(std::wstring(L""), pCondition3->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0003 (indirect {recall:0x01}+0x02)\r\nFirst Level (8-bit pointer)"), pCondition4->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0006 (indirect {recall:0x01}+0x02+0x03)\r\nSecond Level."), pCondition5->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
// $0001 = 3, 3+2 = $0005, $0005 = 5, 5+3 = $0008
vmTrigger.SetMemory({ 1 }, 3);
vmTrigger.MemoryNotesDoFrame();
Assert::AreEqual(std::wstring(L"0x0005 (indirect {recall:0x03}+0x02)\r\nFirst Level (8-bit pointer)"), pCondition4->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0008 (indirect {recall:0x03}+0x02+0x03)\r\nSecond Level."), pCondition5->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
}
TEST_METHOD(TestTooltipRecallPauseIfLate)
{
IndirectAddressTriggerViewModelHarness vmTrigger;
vmTrigger.mockGameContext.InitializeNotes();
vmTrigger.Parse("I:{recall}_I:0xH0002_0xH0003=4_K:0xH0001_P:0=1");
vmTrigger.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
vmTrigger.mockGameContext.SetNote({ 1U }, L"[8-bit pointer]\n+2=First Level (8-bit pointer)\n +3=Second Level.");
const auto* pCondition1 = vmTrigger.Conditions().GetItemAt(0);
Expects(pCondition1 != nullptr);
const auto* pCondition2 = vmTrigger.Conditions().GetItemAt(1);
Expects(pCondition2 != nullptr);
const auto* pCondition3 = vmTrigger.Conditions().GetItemAt(2);
Expects(pCondition3 != nullptr);
const auto* pCondition4 = vmTrigger.Conditions().GetItemAt(3);
Expects(pCondition4 != nullptr);
const auto* pCondition5 = vmTrigger.Conditions().GetItemAt(4);
Expects(pCondition5 != nullptr);
Assert::IsFalse(pCondition1->IsIndirect());
Assert::IsTrue(pCondition2->IsIndirect());
Assert::IsTrue(pCondition3->IsIndirect());
Assert::IsFalse(pCondition4->IsIndirect());
Assert::IsFalse(pCondition5->IsIndirect());
// $0001 = 1, 1+2 = $0003, $0003 = 3, 3+3 = $0006
Assert::AreEqual(std::wstring(L""), pCondition1->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0003 (indirect {recall:0x01}+0x02)\r\nFirst Level (8-bit pointer)"), pCondition2->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0006 (indirect {recall:0x01}+0x02+0x03)\r\nSecond Level."), pCondition3->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
// $0001 = 3, 3+2 = $0005, $0005 = 5, 5+3 = $0008
vmTrigger.SetMemory({ 1 }, 3);
vmTrigger.MemoryNotesDoFrame();
Assert::AreEqual(std::wstring(L"0x0005 (indirect {recall:0x03}+0x02)\r\nFirst Level (8-bit pointer)"), pCondition2->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x0008 (indirect {recall:0x03}+0x02+0x03)\r\nSecond Level."), pCondition3->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
}
TEST_METHOD(TestTooltipRecallChain)
{
IndirectAddressTriggerViewModelHarness vmTrigger;
vmTrigger.mockGameContext.InitializeNotes();
vmTrigger.Parse("0xH1234=6_K:0x 0002&511_K:{recall}*2_K:{recall}+4_I:0x 0010+{recall}_K:0x 0004_I:{recall}_M:0xH0002=20");
// 1 Byte 0x1234 = 6
// 2 Remember Word 0x0002 & 0x01ff
// 3 Remember Recall * 2
// 4 Remember Recall + 4
// 5 AddAddr Word 0x0010 + Recall
// 6 Remember Word 0x0004
// 7 AddAddr Recall
// 8 Measured Byte 0x0002
vmTrigger.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
const auto* pCondition1 = vmTrigger.Conditions().GetItemAt(0);
Expects(pCondition1 != nullptr);
Assert::IsFalse(pCondition1->IsIndirect());
Assert::AreEqual(std::wstring(L""), pCondition1->GetTooltip(TriggerConditionViewModel::SourceTypeProperty));
Assert::AreEqual(std::wstring(L"0x1234\r\n[No memory note]"),
pCondition1->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L""), pCondition1->GetTooltip(TriggerConditionViewModel::TargetTypeProperty));
Assert::AreEqual(std::wstring(L""), pCondition1->GetTooltip(TriggerConditionViewModel::TargetValueProperty));
const auto* pCondition2 = vmTrigger.Conditions().GetItemAt(1);
Expects(pCondition2 != nullptr);
Assert::IsFalse(pCondition2->IsIndirect());
Assert::AreEqual(std::wstring(L""), pCondition2->GetTooltip(TriggerConditionViewModel::SourceTypeProperty));
Assert::AreEqual(std::wstring(L"0x0002\r\n[No memory note]"),
pCondition2->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L""), pCondition2->GetTooltip(TriggerConditionViewModel::TargetTypeProperty));
Assert::AreEqual(std::wstring(L""), pCondition2->GetTooltip(TriggerConditionViewModel::TargetValueProperty));
// $0002 = 0x0302 & 0x01ff = 0x0102
const auto* pCondition3 = vmTrigger.Conditions().GetItemAt(2);
Expects(pCondition3 != nullptr);
Assert::IsFalse(pCondition3->IsIndirect());
Assert::AreEqual(std::wstring(L"0x00000102 (recall)\r\n2: 0x00000302 & 0x000001ff -> 0x00000102"),
pCondition3->GetTooltip(TriggerConditionViewModel::SourceTypeProperty));
Assert::AreEqual(std::wstring(L""), pCondition3->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L""), pCondition3->GetTooltip(TriggerConditionViewModel::TargetTypeProperty));
Assert::AreEqual(std::wstring(L""), pCondition3->GetTooltip(TriggerConditionViewModel::TargetValueProperty));
// $0002 = 0x0302 & 0x01ff = 0x0102 * 2 = 0x0204
const auto* pCondition4 = vmTrigger.Conditions().GetItemAt(3);
Expects(pCondition4 != nullptr);
Assert::IsFalse(pCondition4->IsIndirect());
Assert::AreEqual(std::wstring(L"0x00000204 (recall)\r\n"
L"2: 0x00000302 & 0x000001ff -> 0x00000102\r\n"
L"3: 0x00000102 * 0x00000002 -> 0x00000204"),
pCondition4->GetTooltip(TriggerConditionViewModel::SourceTypeProperty));
Assert::AreEqual(std::wstring(L""), pCondition4->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L""), pCondition4->GetTooltip(TriggerConditionViewModel::TargetTypeProperty));
Assert::AreEqual(std::wstring(L""), pCondition4->GetTooltip(TriggerConditionViewModel::TargetValueProperty));
// $0002 = 0x0302 & 0x01ff = 0x0102 * 2 = 0x0204 + 0x0004 = 0x0208
const auto* pCondition5 = vmTrigger.Conditions().GetItemAt(4);
Expects(pCondition5 != nullptr);
Assert::IsFalse(pCondition5->IsIndirect());
Assert::AreEqual(std::wstring(L""), pCondition5->GetTooltip(TriggerConditionViewModel::SourceTypeProperty));
Assert::AreEqual(std::wstring(L"0x0010\r\n[No memory note]"),
pCondition5->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L"0x00000208 (recall)\r\n"
L"2: 0x00000302 & 0x000001ff -> 0x00000102\r\n"
L"3: 0x00000102 * 0x00000002 -> 0x00000204\r\n"
L"4: 0x00000204 + 0x00000004 -> 0x00000208"),
pCondition5->GetTooltip(TriggerConditionViewModel::TargetTypeProperty));
Assert::AreEqual(std::wstring(L""), pCondition5->GetTooltip(TriggerConditionViewModel::TargetValueProperty));
// $0010 = 0x0000 + 0x04 + 0x0208 = 0x020c
const auto* pCondition6 = vmTrigger.Conditions().GetItemAt(5);
Expects(pCondition6 != nullptr);
Assert::IsTrue(pCondition6->IsIndirect());
Assert::AreEqual(std::wstring(L""), pCondition6->GetTooltip(TriggerConditionViewModel::SourceTypeProperty));
Assert::AreEqual(std::wstring(L"0x020c (indirect $0x0010+{recall:0x208}+0x04)\r\n[No memory note]"),
pCondition6->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L""), pCondition6->GetTooltip(TriggerConditionViewModel::TargetTypeProperty));
Assert::AreEqual(std::wstring(L""), pCondition6->GetTooltip(TriggerConditionViewModel::TargetValueProperty));
const auto* pCondition7 = vmTrigger.Conditions().GetItemAt(6);
Expects(pCondition7 != nullptr);
Assert::IsFalse(pCondition7->IsIndirect());
Assert::AreEqual(std::wstring(L"0x00000000 (recall)\r\n"
L"6: $0x020c -> 0x00000000"),
pCondition7->GetTooltip(TriggerConditionViewModel::SourceTypeProperty));
Assert::AreEqual(std::wstring(L""), pCondition7->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L""), pCondition7->GetTooltip(TriggerConditionViewModel::TargetTypeProperty));
Assert::AreEqual(std::wstring(L""), pCondition7->GetTooltip(TriggerConditionViewModel::TargetValueProperty));
const auto* pCondition8 = vmTrigger.Conditions().GetItemAt(7);
Expects(pCondition8 != nullptr);
Assert::IsTrue(pCondition8->IsIndirect());
Assert::AreEqual(std::wstring(L""), pCondition8->GetTooltip(TriggerConditionViewModel::SourceTypeProperty));
Assert::AreEqual(std::wstring(L"0x0002 (indirect {recall:0x00}+0x02)\r\n[No memory note]"),
pCondition8->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L""), pCondition8->GetTooltip(TriggerConditionViewModel::TargetTypeProperty));
Assert::AreEqual(std::wstring(L""), pCondition8->GetTooltip(TriggerConditionViewModel::TargetValueProperty));
}
TEST_METHOD(TestTooltipRecallChainWithAddAddress)
{
IndirectAddressTriggerViewModelHarness vmTrigger;
vmTrigger.mockGameContext.InitializeNotes();
// with prefer decimal off, hex constants will have decimal tooltips
vmTrigger.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, false);
vmTrigger.Parse(
"I:0xG1234&33554431_K:0xJ0009_"
"I:{recall}+0xJ2222_N:0xI0010=6_"
"I:{recall}+0xJ2222_C:0xH0018=1_"
"I:{recall}+0xJ2222_K:0xJ0009_"
"I:{recall}+0xJ2222_N:0xI0010=6");
// 1 AddAddress 32-bit BE 0x1234 & 33554431 Global pointer ($001234 => b65ba0)
// 2 Remember 24-bit BE 0x0009 Linked list root ($b65ba8 => 392ec0)
// 3 AddAddress {recall} + 24-bit BE 0x2222 $002222 => acd6c0 + 392ec0 => e60580
// 4 AndNext 16-bit BE 0x0010 = 6 Node value check ($e60590 => 0)
// 5 AddAddress {recall} + 24-bit BE 0x2222
// 6 AddHits 8-bit 0x0018 = 1 Node value check ($e60598 => 0)
// 7 AddAddress {recall} + 24-bit BE 0x2222
// 8 Remember 24-bit BE 0x0009 Linked list next ($e60589 => 000000)
// 9 AddAddress {recall} + 24-bit BE 0x2222
// 10 AndNext 16-bit BE 0x0010 = 6 Node value check
// ... ...
// Parse registers the default memory. Replace it with specific values
vmTrigger.mockEmulatorMemoryContext.MockMemoryValue(0x001234, 0xa05bb680);
vmTrigger.mockEmulatorMemoryContext.MockMemoryValue(0x002222, 0x00c0d6ac);
vmTrigger.mockEmulatorMemoryContext.MockMemoryValue(0xb65ba8, 0xc02e3980);
auto* pTrigger = vmTrigger.GetTriggerFromString();
auto* pMemrefs = rc_trigger_get_memrefs(pTrigger);
rc_update_memref_values(pMemrefs, rc_peek_callback, nullptr);
// start validation
const auto* pCondition1 = vmTrigger.Conditions().GetItemAt(0);
Expects(pCondition1 != nullptr);
Assert::IsFalse(pCondition1->IsIndirect());
Assert::AreEqual(std::wstring(L""), pCondition1->GetTooltip(TriggerConditionViewModel::SourceTypeProperty));
Assert::AreEqual(std::wstring(L"0x001234\r\n[No memory note]"),
pCondition1->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L""), pCondition1->GetTooltip(TriggerConditionViewModel::TargetTypeProperty));
Assert::AreEqual(std::wstring(L"33554431"), pCondition1->GetTooltip(TriggerConditionViewModel::TargetValueProperty));
const auto* pCondition2 = vmTrigger.Conditions().GetItemAt(1);
Expects(pCondition2 != nullptr);
Assert::IsTrue(pCondition2->IsIndirect());
Assert::AreEqual(std::wstring(L""), pCondition2->GetTooltip(TriggerConditionViewModel::SourceTypeProperty));
Assert::AreEqual(std::wstring(L"0xb65ba9 (indirect $0x001234+0x09)\r\n[No memory note]"),
pCondition2->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L""), pCondition2->GetTooltip(TriggerConditionViewModel::TargetTypeProperty));
Assert::AreEqual(std::wstring(L""), pCondition2->GetTooltip(TriggerConditionViewModel::TargetValueProperty));
const auto* pCondition3 = vmTrigger.Conditions().GetItemAt(2);
Expects(pCondition3 != nullptr);
Assert::IsFalse(pCondition3->IsIndirect());
Assert::AreEqual(std::wstring(L"0x00392ec0 (recall)\r\n2: $0xb65ba9 -> 0x00392ec0"),
pCondition3->GetTooltip(TriggerConditionViewModel::SourceTypeProperty));
Assert::AreEqual(std::wstring(L""), pCondition3->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L""), pCondition3->GetTooltip(TriggerConditionViewModel::TargetTypeProperty));
Assert::AreEqual(std::wstring(L"0x002222\r\n[No memory note]"), pCondition3->GetTooltip(TriggerConditionViewModel::TargetValueProperty));
const auto* pCondition4 = vmTrigger.Conditions().GetItemAt(3);
Expects(pCondition4 != nullptr);
Assert::IsTrue(pCondition4->IsIndirect());
Assert::AreEqual(std::wstring(L""), pCondition4->GetTooltip(TriggerConditionViewModel::SourceTypeProperty));
Assert::AreEqual(std::wstring(L"0xe60590 (indirect {recall:0x392ec0}+$0x002222+0x10)\r\n[No memory note]"), pCondition4->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L""), pCondition4->GetTooltip(TriggerConditionViewModel::TargetTypeProperty));
Assert::AreEqual(std::wstring(L"6"), pCondition4->GetTooltip(TriggerConditionViewModel::TargetValueProperty));
const auto* pCondition5 = vmTrigger.Conditions().GetItemAt(4);
Expects(pCondition5 != nullptr);
Assert::IsFalse(pCondition5->IsIndirect());
Assert::AreEqual(std::wstring(L"0x00392ec0 (recall)\r\n2: $0xb65ba9 -> 0x00392ec0"), pCondition5->GetTooltip(TriggerConditionViewModel::SourceTypeProperty));
Assert::AreEqual(std::wstring(L""), pCondition5->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L""), pCondition5->GetTooltip(TriggerConditionViewModel::TargetTypeProperty));
Assert::AreEqual(std::wstring(L"0x002222\r\n[No memory note]"), pCondition5->GetTooltip(TriggerConditionViewModel::TargetValueProperty));
const auto* pCondition6 = vmTrigger.Conditions().GetItemAt(5);
Expects(pCondition6 != nullptr);
Assert::IsTrue(pCondition6->IsIndirect());
Assert::AreEqual(std::wstring(L""), pCondition6->GetTooltip(TriggerConditionViewModel::SourceTypeProperty));
Assert::AreEqual(std::wstring(L"0xe60598 (indirect {recall:0x392ec0}+$0x002222+0x18)\r\n[No memory note]"),
pCondition6->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L""), pCondition6->GetTooltip(TriggerConditionViewModel::TargetTypeProperty));
Assert::AreEqual(std::wstring(L"1"), pCondition6->GetTooltip(TriggerConditionViewModel::TargetValueProperty));
vmTrigger.EnsureVisible(6, 4); // scroll to end
const int nScrollOffset = vmTrigger.GetScrollOffset();
const auto* pCondition7 = vmTrigger.Conditions().GetItemAt(6 - gsl::narrow_cast<gsl::index>(nScrollOffset));
Expects(pCondition7 != nullptr);
Assert::IsFalse(pCondition7->IsIndirect());
Assert::AreEqual(std::wstring(L"0x00392ec0 (recall)\r\n2: $0xb65ba9 -> 0x00392ec0"),
pCondition7->GetTooltip(TriggerConditionViewModel::SourceTypeProperty));
Assert::AreEqual(std::wstring(L""), pCondition7->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L""), pCondition7->GetTooltip(TriggerConditionViewModel::TargetTypeProperty));
Assert::AreEqual(std::wstring(L"0x002222\r\n[No memory note]"), pCondition7->GetTooltip(TriggerConditionViewModel::TargetValueProperty));
const auto* pCondition8 = vmTrigger.Conditions().GetItemAt(7 - gsl::narrow_cast<gsl::index>(nScrollOffset));
Expects(pCondition8 != nullptr);
Assert::IsTrue(pCondition8->IsIndirect());
Assert::AreEqual(std::wstring(L""), pCondition8->GetTooltip(TriggerConditionViewModel::SourceTypeProperty));
Assert::AreEqual(std::wstring(L"0xe60589 (indirect {recall:0x392ec0}+$0x002222+0x09)\r\n[No memory note]"),
pCondition8->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L""), pCondition8->GetTooltip(TriggerConditionViewModel::TargetTypeProperty));
Assert::AreEqual(std::wstring(L""), pCondition8->GetTooltip(TriggerConditionViewModel::TargetValueProperty));
const auto* pCondition9 = vmTrigger.Conditions().GetItemAt(8 - gsl::narrow_cast<gsl::index>(nScrollOffset));
Expects(pCondition9 != nullptr);
Assert::IsFalse(pCondition9->IsIndirect());
Assert::AreEqual(std::wstring(L"0x00000000 (recall)\r\n8: $0xe60589 -> 0x00000000"),
pCondition9->GetTooltip(TriggerConditionViewModel::SourceTypeProperty));
Assert::AreEqual(std::wstring(L""), pCondition9->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L""), pCondition9->GetTooltip(TriggerConditionViewModel::TargetTypeProperty));
Assert::AreEqual(std::wstring(L"0x002222\r\n[No memory note]"),
pCondition9->GetTooltip(TriggerConditionViewModel::TargetValueProperty));
const auto* pCondition10 = vmTrigger.Conditions().GetItemAt(9 - gsl::narrow_cast<gsl::index>(nScrollOffset));
Expects(pCondition8 != nullptr);
Assert::IsTrue(pCondition10->IsIndirect());
Assert::AreEqual(std::wstring(L""), pCondition10->GetTooltip(TriggerConditionViewModel::SourceTypeProperty));
Assert::AreEqual(std::wstring(L"0xacd6d0 (indirect {recall:0x00}+$0x002222+0x10)\r\n[No memory note]"),
pCondition10->GetTooltip(TriggerConditionViewModel::SourceValueProperty));
Assert::AreEqual(std::wstring(L""), pCondition10->GetTooltip(TriggerConditionViewModel::TargetTypeProperty));
Assert::AreEqual(std::wstring(L"6"), pCondition10->GetTooltip(TriggerConditionViewModel::TargetValueProperty));
}
TEST_METHOD(TestTooltipRecallInvalid)
{
IndirectAddressTriggerViewModelHarness vmTrigger;
vmTrigger.mockGameContext.InitializeNotes();
vmTrigger.Parse("A:{recall}_0xH0000=1");
vmTrigger.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
const auto* pCondition1 = vmTrigger.Conditions().GetItemAt(0);
Expects(pCondition1 != nullptr);
// there is no Remember condition to point to
Assert::AreEqual(std::wstring(L"0x00000000 (recall)\r\n[invalid recall]"), pCondition1->GetTooltip(TriggerConditionViewModel::SourceTypeProperty));
}
TEST_METHOD(TestTooltipRecallChainInvalid)
{
IndirectAddressTriggerViewModelHarness vmTrigger;
vmTrigger.mockGameContext.InitializeNotes();
vmTrigger.Parse("K:{recall}+6_A:{recall}_0xH0000=1");
vmTrigger.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
// there is no Remember for the first condition to point to
const auto* pCondition1 = vmTrigger.Conditions().GetItemAt(0);
Expects(pCondition1 != nullptr);
Assert::AreEqual(std::wstring(L"0x00000000 (recall)\r\n[invalid recall]"), pCondition1->GetTooltip(TriggerConditionViewModel::SourceTypeProperty));
// the second condition points to the first one, but it's broken too
const auto* pCondition2 = vmTrigger.Conditions().GetItemAt(1);
Expects(pCondition2 != nullptr);
Assert::AreEqual(std::wstring(L"0x00000006 (recall)\r\n1: 0x00000000 + 0x00000006 -> 0x00000006"),
pCondition2->GetTooltip(TriggerConditionViewModel::SourceTypeProperty));
}
TEST_METHOD(TestTooltipRecallInvalidInPauseIf)
{
IndirectAddressTriggerViewModelHarness vmTrigger;
vmTrigger.mockGameContext.InitializeNotes();
vmTrigger.Parse("K:0xH0001*2_1=1_N:{recall}!=0_P:0xH0000=1");
vmTrigger.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
const auto* pCondition3 = vmTrigger.Conditions().GetItemAt(2);
Expects(pCondition3 != nullptr);
// there is a Remember condition to point to, but it won't be processed until after this condition
Assert::AreEqual(std::wstring(L"0x00000000 (recall)\r\n[invalid recall]"), pCondition3->GetTooltip(TriggerConditionViewModel::SourceTypeProperty));
}
TEST_METHOD(TestIsModifying)
{
TriggerConditionViewModelHarness condition;
condition.SetType(ra::data::Requirement::Type::AddAddress);
Assert::IsTrue(condition.IsModifying());
condition.SetType(ra::data::Requirement::Type::AddHits);
Assert::IsFalse(condition.IsModifying());
condition.SetType(ra::data::Requirement::Type::AddSource);
Assert::IsTrue(condition.IsModifying());
condition.SetType(ra::data::Requirement::Type::AndNext);
Assert::IsFalse(condition.IsModifying());
condition.SetType(ra::data::Requirement::Type::Measured);
Assert::IsFalse(condition.IsModifying());
condition.SetType(ra::data::Requirement::Type::MeasuredIf);
Assert::IsFalse(condition.IsModifying());
condition.SetType(ra::data::Requirement::Type::OrNext);
Assert::IsFalse(condition.IsModifying());
condition.SetType(ra::data::Requirement::Type::PauseIf);
Assert::IsFalse(condition.IsModifying());
condition.SetType(ra::data::Requirement::Type::ResetIf);
Assert::IsFalse(condition.IsModifying());
condition.SetType(ra::data::Requirement::Type::Standard);
Assert::IsFalse(condition.IsModifying());
condition.SetType(ra::data::Requirement::Type::SubHits);
Assert::IsFalse(condition.IsModifying());
condition.SetType(ra::data::Requirement::Type::SubSource);
Assert::IsTrue(condition.IsModifying());
condition.SetType(ra::data::Requirement::Type::Trigger);
Assert::IsFalse(condition.IsModifying());
condition.SetType(ra::data::Requirement::Type::Remember);
Assert::IsTrue(condition.IsModifying());
}
TEST_METHOD(TestSwitchBetweenModifyingAndNonModifying)
{
TriggerConditionViewModelHarness condition;
condition.SetSourceValue(0x1234U);
condition.SetOperator(ra::data::Requirement::OperatorType::NotEquals);
condition.SetTargetValue(8U);
Assert::AreEqual(std::string("0xH1234!=8"), condition.Serialize());
Assert::IsFalse(condition.IsModifying());
// change to modifying switches operator to None. the 8 is unmodified, but ignored
condition.SetType(ra::data::Requirement::Type::AddSource);
Assert::AreEqual(ra::data::Requirement::OperatorType::None, condition.GetOperator());
Assert::AreEqual(std::string("A:0xH1234"), condition.Serialize());
Assert::IsTrue(condition.IsModifying());
// changing operator "restores" the 8
condition.SetOperator(ra::data::Requirement::OperatorType::Multiply);
Assert::AreEqual(std::string("A:0xH1234*8"), condition.Serialize());
// change to non-modifying switches operator to Equals
condition.SetType(ra::data::Requirement::Type::ResetIf);
Assert::AreEqual(ra::data::Requirement::OperatorType::Equals, condition.GetOperator());
Assert::AreEqual(std::string("R:0xH1234=8"), condition.Serialize());
Assert::IsFalse(condition.IsModifying());
// change to modifying switches operator to None. the 8 is unmodified, but ignored
condition.SetType(ra::data::Requirement::Type::AddAddress);
Assert::AreEqual(ra::data::Requirement::OperatorType::None, condition.GetOperator());
Assert::AreEqual(std::string("I:0xH1234"), condition.Serialize());
Assert::IsTrue(condition.IsModifying());
// change to non-modifying switches operator to Equals
condition.SetType(ra::data::Requirement::Type::Standard);
Assert::AreEqual(ra::data::Requirement::OperatorType::Equals, condition.GetOperator());
Assert::AreEqual(std::string("0xH1234=8"), condition.Serialize());
Assert::IsFalse(condition.IsModifying());
// change to modifying switches operator to None. the 8 is unmodified, but ignored
condition.SetType(ra::data::Requirement::Type::Remember);
Assert::AreEqual(ra::data::Requirement::OperatorType::None, condition.GetOperator());
Assert::AreEqual(std::string("K:0xH1234"), condition.Serialize());
Assert::IsTrue(condition.IsModifying());
// changing operator "restores" the 8
condition.SetOperator(ra::data::Requirement::OperatorType::Multiply);
Assert::AreEqual(std::string("K:0xH1234*8"), condition.Serialize());
// change to non-modifying switches operator to Equals
condition.SetType(ra::data::Requirement::Type::ResetIf);
Assert::AreEqual(ra::data::Requirement::OperatorType::Equals, condition.GetOperator());
Assert::AreEqual(std::string("R:0xH1234=8"), condition.Serialize());
Assert::IsFalse(condition.IsModifying());
}
TEST_METHOD(TestSwitchBetweenValueAndAddress)
{
TriggerConditionViewModelHarness condition;
condition.SetSourceValue(0x1234U);
condition.SetOperator(ra::data::Requirement::OperatorType::NotEquals);
condition.SetTargetValue(8U);
Assert::AreEqual(std::string("0xH1234!=8"), condition.Serialize());
Assert::IsFalse(condition.HasTargetSize());
// change to address switches size to match source
condition.SetTargetType(ra::data::Requirement::OperandType::Delta);
Assert::IsTrue(condition.HasTargetSize());
Assert::AreEqual(ra::data::Memory::Size::EightBit, condition.GetTargetSize());
Assert::AreEqual(std::string("0xH1234!=d0xH0008"), condition.Serialize());
// change to value sets size back to 32-bit
condition.SetTargetType(ra::data::Requirement::OperandType::Value);
Assert::IsFalse(condition.HasTargetSize());
Assert::AreEqual(ra::data::Memory::Size::ThirtyTwoBit, condition.GetTargetSize());
Assert::AreEqual(std::string("0xH1234!=8"), condition.Serialize());
}
TEST_METHOD(TestChangeOperand)
{
TriggerConditionViewModelHarness condition;
condition.SetSourceValue(0x1234U);
condition.SetOperator(ra::data::Requirement::OperatorType::NotEquals);
condition.SetTargetValue(8U);
Assert::AreEqual(std::string("0xH1234!=8"), condition.Serialize());
condition.SetTargetType(ra::data::Requirement::OperandType::Float);
Assert::AreEqual(ra::data::Requirement::OperandType::Float, condition.GetTargetType());
Assert::AreEqual(std::wstring(L"8.0"), condition.GetTargetValue());
Assert::AreEqual(std::string("0xH1234!=f8.0"), condition.Serialize());
condition.SetTargetValue(L"3.14");
Assert::AreEqual(std::string("0xH1234!=f3.14"), condition.Serialize());
condition.SetTargetType(ra::data::Requirement::OperandType::Value);
Assert::AreEqual(ra::data::Requirement::OperandType::Value, condition.GetTargetType());
Assert::AreEqual(std::wstring(L"0x03"), condition.GetTargetValue());
Assert::AreEqual(std::string("0xH1234!=3"), condition.Serialize());
condition.SetTargetType(ra::data::Requirement::OperandType::Address);
Assert::AreEqual(ra::data::Requirement::OperandType::Address, condition.GetTargetType());
Assert::AreEqual(std::wstring(L"0x0003"), condition.GetTargetValue());
Assert::AreEqual(std::string("0xH1234!=0xH0003"), condition.Serialize());
condition.SetTargetType(ra::data::Requirement::OperandType::Float);
Assert::AreEqual(ra::data::Requirement::OperandType::Float, condition.GetTargetType());
Assert::AreEqual(std::wstring(L"3.0"), condition.GetTargetValue());
Assert::AreEqual(std::string("0xH1234!=f3.0"), condition.Serialize());
condition.SetTargetType(ra::data::Requirement::OperandType::Address);
Assert::AreEqual(ra::data::Requirement::OperandType::Address, condition.GetTargetType());
Assert::AreEqual(std::wstring(L"0x0003"), condition.GetTargetValue());
Assert::AreEqual(std::string("0xH1234!=0xH0003"), condition.Serialize());
}
TEST_METHOD(TestSetOperandMismatch)
{
TriggerConditionViewModelHarness condition;
condition.SetSourceValue(0x1234U);
condition.SetOperator(ra::data::Requirement::OperatorType::NotEquals);
condition.SetTargetValue(8U);
Assert::AreEqual(std::string("0xH1234!=8"), condition.Serialize());
condition.SetTargetValue(L"3.14");
Assert::AreEqual(ra::data::Requirement::OperandType::Value, condition.GetTargetType());
Assert::AreEqual(std::wstring(L"3.14"), condition.GetTargetValue()); // value is not updated until trigger is reconstructed
Assert::AreEqual(std::string("0xH1234!=0"), condition.Serialize()); // serializer will choke and convert to 0
condition.SetTargetType(ra::data::Requirement::OperandType::Float);
condition.SetTargetValue(L"3.14");
Assert::AreEqual(ra::data::Requirement::OperandType::Float, condition.GetTargetType());
Assert::AreEqual(std::wstring(L"3.14"), condition.GetTargetValue());
Assert::AreEqual(std::string("0xH1234!=f3.14"), condition.Serialize());
condition.SetTargetValue(L"0x0F");
Assert::AreEqual(ra::data::Requirement::OperandType::Float, condition.GetTargetType());
Assert::AreEqual(std::wstring(L"0x0F"), condition.GetTargetValue()); // value is not updated until trigger is reconstructed
Assert::AreEqual(std::string("0xH1234!=f15.0"), condition.Serialize()); // apparently wcstof supports 0x prefix
condition.SetTargetValue(L"6");
Assert::AreEqual(ra::data::Requirement::OperandType::Float, condition.GetTargetType());
Assert::AreEqual(std::wstring(L"6"), condition.GetTargetValue()); // value is not updated until trigger is reconstructed
Assert::AreEqual(std::string("0xH1234!=f6.0"), condition.Serialize()); // serializer can handle this conversion
}
TEST_METHOD(TestIsComparisonVisible)
{
TriggerConditionViewModelHarness condition;
condition.SetType(ra::data::Requirement::Type::Standard);
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::Equals)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::NotEquals)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::LessThan)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::LessThanOrEqual)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::GreaterThan)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::GreaterThanOrEqual)));
Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::None)));
Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::Multiply)));
Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::Divide)));
Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::BitwiseAnd)));
Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::BitwiseXor)));
Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::Modulus)));
condition.SetType(ra::data::Requirement::Type::AddAddress);
Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::Equals)));
Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::NotEquals)));
Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::LessThan)));
Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::LessThanOrEqual)));
Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::GreaterThan)));
Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::GreaterThanOrEqual)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::None)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::Multiply)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::Divide)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::BitwiseAnd)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::BitwiseXor)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::Modulus)));
}
TEST_METHOD(TestIsComparisonVisibleMeasured)
{
TriggerConditionViewModelHarness condition;
condition.SetType(ra::data::Requirement::Type::Measured);
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::Equals)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::NotEquals)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::LessThan)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::LessThanOrEqual)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::GreaterThan)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::GreaterThanOrEqual)));
Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::None)));
Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::Multiply)));
Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::Divide)));
Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::BitwiseAnd)));
Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::BitwiseXor)));
Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::Modulus)));
TriggerViewModel vmTrigger;
vmTrigger.SetIsValue(true);
condition.SetTriggerViewModel(&vmTrigger);
// None operator is visible when associated to a Value trigger
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::Equals)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::NotEquals)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::LessThan)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::LessThanOrEqual)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::GreaterThan)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::GreaterThanOrEqual)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::None)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::Multiply)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::Divide)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::BitwiseAnd)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::BitwiseXor)));
Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(ra::data::Requirement::OperatorType::Modulus)));
}
TEST_METHOD(TestUpdateRowColor)
{
TriggerConditionViewModelHarness condition;
ra::ui::EditorTheme pTheme;
ra::services::ServiceLocator::ServiceOverride<ra::ui::EditorTheme> pThemeOverride(&pTheme);
const auto nDefaultColor = ra::to_unsigned(TriggerConditionViewModel::RowColorProperty.GetDefaultValue());
const std::string sInput = "R:0xH0000=1_P:0xH0001=1_0xH0002=1(2)_0xH0003=1_R:0xH0004=1.2.";
const auto nSize = rc_trigger_size(sInput.c_str());
std::string sBuffer;
sBuffer.resize(nSize);
const rc_trigger_t* pTrigger = rc_parse_trigger(sBuffer.data(), sInput.c_str(), nullptr, 0);
Assert::IsNotNull(pTrigger);
Ensures(pTrigger != nullptr);
// ResetIf
rc_condition_t* pCondition = pTrigger->requirement->conditions;
Expects(pCondition != nullptr);
condition.InitializeFrom(*pCondition);
pCondition->is_true = 0;
condition.UpdateRowColor(pCondition);
Assert::AreEqual(nDefaultColor, condition.GetRowColor().ARGB);
pCondition->is_true = 3;
condition.UpdateRowColor(pCondition);
Assert::AreEqual(pTheme.ColorTriggerResetTrue().ARGB, condition.GetRowColor().ARGB);
pCondition->is_true = 0;
condition.UpdateRowColor(pCondition);
Assert::AreEqual(nDefaultColor, condition.GetRowColor().ARGB);
pCondition->is_true = 1; // condition itself is true, but reset did not occur - assume false exists in AndNext chain
condition.UpdateRowColor(pCondition);
Assert::AreEqual(pTheme.ColorTriggerIsTrue().ARGB, condition.GetRowColor().ARGB);
// PauseIf
pCondition = pCondition->next;
Expects(pCondition != nullptr);
condition.InitializeFrom(*pCondition);
pCondition->is_true = 0;
condition.UpdateRowColor(pCondition);
Assert::AreEqual(nDefaultColor, condition.GetRowColor().ARGB);
pCondition->is_true = 1;
condition.UpdateRowColor(pCondition);
Assert::AreEqual(pTheme.ColorTriggerPauseTrue().ARGB, condition.GetRowColor().ARGB);
pCondition->is_true = 0;
condition.UpdateRowColor(pCondition);
Assert::AreEqual(nDefaultColor, condition.GetRowColor().ARGB);
// Condition with hit target
pCondition = pCondition->next;
Expects(pCondition != nullptr);
condition.InitializeFrom(*pCondition);
pCondition->is_true = 0;
pCondition->current_hits = 0;
condition.UpdateRowColor(pCondition);
Assert::AreEqual(nDefaultColor, condition.GetRowColor().ARGB);
pCondition->is_true = 1;
pCondition->current_hits = 1;
condition.UpdateRowColor(pCondition);
Assert::AreEqual(pTheme.ColorTriggerBecomingTrue().ARGB, condition.GetRowColor().ARGB);
pCondition->is_true = 0;
pCondition->current_hits = 1;
condition.UpdateRowColor(pCondition);
Assert::AreEqual(nDefaultColor, condition.GetRowColor().ARGB);
pCondition->is_true = 1;
pCondition->current_hits = 2;
condition.UpdateRowColor(pCondition);
Assert::AreEqual(pTheme.ColorTriggerIsTrue().ARGB, condition.GetRowColor().ARGB);
pCondition->is_true = 0;
pCondition->current_hits = 2;
condition.UpdateRowColor(pCondition);
Assert::AreEqual(pTheme.ColorTriggerWasTrue().ARGB, condition.GetRowColor().ARGB);
// Simple condition
pCondition = pCondition->next;
Expects(pCondition != nullptr);
condition.InitializeFrom(*pCondition);
pCondition->is_true = 0;
condition.UpdateRowColor(pCondition);
Assert::AreEqual(nDefaultColor, condition.GetRowColor().ARGB);
pCondition->is_true = 1;
condition.UpdateRowColor(pCondition);
Assert::AreEqual(pTheme.ColorTriggerIsTrue().ARGB, condition.GetRowColor().ARGB);
pCondition->is_true = 0;
condition.UpdateRowColor(pCondition);
Assert::AreEqual(nDefaultColor, condition.GetRowColor().ARGB);
// ResetIf with a hit target
pCondition = pCondition->next;
Expects(pCondition != nullptr);
condition.InitializeFrom(*pCondition);
pCondition->is_true = 0;
pCondition->current_hits = 0;
condition.UpdateRowColor(pCondition);
Assert::AreEqual(nDefaultColor, condition.GetRowColor().ARGB);
pCondition->is_true = 1;
pCondition->current_hits = 1;
condition.UpdateRowColor(pCondition);
Assert::AreEqual(pTheme.ColorTriggerBecomingTrue().ARGB, condition.GetRowColor().ARGB);
pCondition->is_true = 0;
pCondition->current_hits = 1;
condition.UpdateRowColor(pCondition);
Assert::AreEqual(nDefaultColor, condition.GetRowColor().ARGB);
pCondition->is_true = 3;
pCondition->current_hits = 0;
condition.UpdateRowColor(pCondition);
Assert::AreEqual(pTheme.ColorTriggerResetTrue().ARGB, condition.GetRowColor().ARGB);
pCondition->is_true = 0;
pCondition->current_hits = 0;
condition.UpdateRowColor(pCondition);
Assert::AreEqual(nDefaultColor, condition.GetRowColor().ARGB);
}
};
} // namespace tests
} // namespace viewmodels
} // namespace ui
} // namespace ra