diff --git a/.gitattributes b/.gitattributes index 51617f5f..e0d31f91 100644 --- a/.gitattributes +++ b/.gitattributes @@ -12,29 +12,6 @@ ############################################################################### *.cs diff=csharp -############################################################################### -# Set the merge driver for project and solution files -# -# Merging from the command prompt will add diff markers to the files if there -# are conflicts (Merging from VS is not affected by the settings below, in VS -# the diff markers are never inserted). Diff markers may cause the following -# file extensions to fail to load in VS. An alternative would be to treat -# these files as binary and thus will always conflict and require user -# intervention with every merge. To do so, just uncomment the entries below -############################################################################### -*.sln merge=binary -*.csproj merge=binary -*.vbproj merge=binary -*.vcxproj merge=binary -*.vcproj merge=binary -*.dbproj merge=binary -*.fsproj merge=binary -*.lsproj merge=binary -*.wixproj merge=binary -*.modelproj merge=binary -*.sqlproj merge=binary -*.wwaproj merge=binary - ############################################################################### # behavior for image files # diff --git a/.gitignore b/.gitignore index 2100dfc1..1088272d 100644 --- a/.gitignore +++ b/.gitignore @@ -289,6 +289,7 @@ __pycache__/ # Calculator specific Generated Files/ +src/GraphControl/GraphingImplOverrides.props !/build/config/TRexDefs/** !src/Calculator/TemporaryKey.pfx !src/CalculatorUnitTests/CalculatorUnitTests_TemporaryKey.pfx \ No newline at end of file diff --git a/README.md b/README.md index d967b175..00243142 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,9 @@ Calculator ships regularly with new features and bug fixes. You can get the late - Calculation history and memory capabilities. - Conversion between many units of measurement. - Currency conversion based on data retrieved from [Bing](https://www.bing.com). +- [Infinite precision](https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic) for basic + arithmetic operations (addition, subtraction, multiplication, division) so that calculations + never lose precision. ## Getting started Prerequisites: diff --git a/build/config/SignConfig.xml b/build/config/SignConfig.xml index 7209725e..eb298e87 100644 --- a/build/config/SignConfig.xml +++ b/build/config/SignConfig.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/build/pipelines/azure-pipelines.ci.yaml b/build/pipelines/azure-pipelines.ci.yaml index 31ce98fb..23eb46ca 100644 --- a/build/pipelines/azure-pipelines.ci.yaml +++ b/build/pipelines/azure-pipelines.ci.yaml @@ -33,6 +33,10 @@ jobs: platform: ARM64 condition: not(eq(variables['Build.Reason'], 'PullRequest')) +- template: ./templates/run-ui-tests.yaml + parameters: + platform: x64 + - template: ./templates/run-unit-tests.yaml parameters: platform: x64 diff --git a/build/pipelines/templates/build-single-architecture.yaml b/build/pipelines/templates/build-single-architecture.yaml index 92e06f7b..50859db3 100644 --- a/build/pipelines/templates/build-single-architecture.yaml +++ b/build/pipelines/templates/build-single-architecture.yaml @@ -30,7 +30,7 @@ steps: inputs: solution: src/Calculator.sln vsVersion: 15.0 - msbuildArgs: /bl:$(Build.BinariesDirectory)\$(BuildConfiguration)\$(BuildPlatform)\Calculator.binlog /p:OutDir=$(Build.BinariesDirectory)\$(BuildConfiguration)\$(BuildPlatform)\ /p:GenerateProjectSpecificOutputFolder=true /p:AppVersion=$(Build.BuildNumber) ${{ parameters.extraMsBuildArgs }} + msbuildArgs: /bl:$(Build.BinariesDirectory)\$(BuildConfiguration)\$(BuildPlatform)\Calculator.binlog /p:OutDir=$(Build.BinariesDirectory)\$(BuildConfiguration)\$(BuildPlatform)\ /p:GenerateProjectSpecificOutputFolder=true /p:AppVersion=$(Build.BuildNumber) /t:Publish /p:PublishDir=$(Build.BinariesDirectory)\$(BuildConfiguration)\$(BuildPlatform)\publish\ ${{ parameters.extraMsBuildArgs }} platform: $(BuildPlatform) configuration: $(BuildConfiguration) clean: true diff --git a/build/pipelines/templates/prepare-release-internalonly.yaml b/build/pipelines/templates/prepare-release-internalonly.yaml index a6a4973b..178fd268 100644 --- a/build/pipelines/templates/prepare-release-internalonly.yaml +++ b/build/pipelines/templates/prepare-release-internalonly.yaml @@ -1,5 +1,5 @@ # This template contains a job which builds artifacts needed to release the app to the store and to -# Windows using Microsoft-internal systems. It relies Microsoft-internal resources and will not +# Windows using Microsoft-internal systems. It relies on Microsoft-internal resources and will not # work outside of Microsoft. # Specifically, this job: # - Signs the bundle using a secure system. If you want to build your own, use SignTool following diff --git a/build/pipelines/templates/run-ui-tests.yaml b/build/pipelines/templates/run-ui-tests.yaml new file mode 100644 index 00000000..41d441ad --- /dev/null +++ b/build/pipelines/templates/run-ui-tests.yaml @@ -0,0 +1,52 @@ +# This template contains jobs to run UI tests using WinAppDriver. + +parameters: + platform: '' + +jobs: +- job: UITests${{ parameters.platform }} + displayName: UITests ${{ parameters.platform }} + dependsOn: Build${{ parameters.platform }} + condition: succeeded() + pool: + vmImage: windows-2019 + variables: + skipComponentGovernanceDetection: true + steps: + - checkout: none + + - powershell: Set-DisplayResolution -Width 1920 -Height 1080 -Force + displayName: Set resolution to 1920x1080 + continueOnError: true + + - task: DownloadBuildArtifacts@0 + displayName: Download AppxBundle and CalculatorUITests + inputs: + artifactName: drop + itemPattern: | + drop/Release/${{ parameters.platform }}/Calculator/AppPackages/** + drop/Release/${{ parameters.platform }}/publish/** + + - task: PowerShell@2 + displayName: Install certificate + inputs: + filePath: $(Build.ArtifactStagingDirectory)/drop/Release/${{ parameters.platform }}/Calculator/AppPackages/Calculator_$(Build.BuildNumber)_Test/Add-AppDevPackage.ps1 + arguments: -CertificatePath $(Build.ArtifactStagingDirectory)/drop/Release/${{ parameters.platform }}/Calculator/AppPackages/Calculator_$(Build.BuildNumber)_Test/Calculator_$(Build.BuildNumber)_${{ parameters.platform }}.cer -Force + + - task: PowerShell@2 + displayName: Install app + inputs: + filePath: $(Build.ArtifactStagingDirectory)/drop/Release/${{ parameters.platform }}/Calculator/AppPackages/Calculator_$(Build.BuildNumber)_Test/Add-AppDevPackage.ps1 + arguments: -Force + + - powershell: Start-Process -FilePath "C:\Program Files (x86)\Windows Application Driver\WinAppDriver.exe" -Verb RunAs + displayName: Start WinAppDriver + + - task: VSTest@2 + displayName: Run CalculatorUITests + inputs: + testAssemblyVer2: $(Build.ArtifactStagingDirectory)/drop/Release/${{ parameters.platform }}/publish/CalculatorUITests.dll + vsTestVersion: 16.0 + runSettingsFile: $(Build.ArtifactStagingDirectory)/drop/Release/${{ parameters.platform }}/publish/CalculatorUITests.runsettings + platform: ${{ parameters.platform }} + configuration: Release \ No newline at end of file diff --git a/docs/ApplicationArchitecture.md b/docs/ApplicationArchitecture.md index 0d7c2ff3..dff02965 100644 --- a/docs/ApplicationArchitecture.md +++ b/docs/ApplicationArchitecture.md @@ -153,7 +153,9 @@ The CalcEngine contains the logic for interpreting and performing operations acc ### RatPack -The RatPack (short for Rational Pack) is the core of the Calculator model and contains the logic for performing its mathematical operations. The interface to this layer is defined in [ratpak.h][ratpak.h]. +The RatPack (short for Rational Pack) is the core of the Calculator model and contains the logic for +performing its mathematical operations (using [infinite precision][Infinite Precision] arithmetic +instead of regular floating point arithmetic). The interface to this layer is defined in [ratpak.h][ratpak.h]. [References]:#################################################################################################### diff --git a/src/CalcViewModel/Common/CopyPasteManager.cpp b/src/CalcViewModel/Common/CopyPasteManager.cpp index c8adad34..6d6807ba 100644 --- a/src/CalcViewModel/Common/CopyPasteManager.cpp +++ b/src/CalcViewModel/Common/CopyPasteManager.cpp @@ -15,11 +15,15 @@ using namespace Windows::Foundation; using namespace Windows::System; using namespace Windows::ApplicationModel::DataTransfer; -unsigned long long maxOperandNumber; +String^ CopyPasteManager::supportedFormats[] = +{ + StandardDataFormats::Text +}; -String ^ CopyPasteManager::supportedFormats[] = { StandardDataFormats::Text }; +static constexpr wstring_view c_validCharacterSet{ L"0123456789()+-*/.abcdefABCDEF" }; -constexpr wstring_view c_validCharacterSet{ L"0123456789()+-*/.abcdefABCDEF" }; +// The below values can not be "constexpr"-ed, +// as both wstring_view and wchar[] can not be concatenated // [\s\x85] means white-space characters static const wstring c_wspc = L"[\\s\\x85]*"; static const wstring c_wspcLParens = c_wspc + L"[(]*" + c_wspc; @@ -36,26 +40,43 @@ static const wstring c_binProgrammerChars = L"[0-1]+((_|'|`)[0-1]+)*"; static const wstring c_uIntSuffixes = L"[uU]?[lL]{0,2}"; // RegEx Patterns used by various modes -static const array standardModePatterns = { wregex(c_wspc + c_signedDecFloat + c_wspc) }; -static const array scientificModePatterns = { wregex(L"(" + c_wspc + L"[-+]?)|(" + c_wspcLParenSigned + L")" + c_signedDecFloat + c_wspcRParens), - wregex(L"(" + c_wspc + L"[-+]?)|(" + c_wspcLParenSigned + L")" + c_signedDecFloat - + L"[e]([+]|[-])+\\d+" + c_wspcRParens) }; -static const array, 4> programmerModePatterns = { - { // Hex numbers like 5F, 4A0C, 0xa9, 0xFFull, 47CDh - { wregex(c_wspcLParens + L"(0[xX])?" + c_hexProgrammerChars + c_uIntSuffixes + c_wspcRParens), - wregex(c_wspcLParens + c_hexProgrammerChars + L"[hH]?" + c_wspcRParens) }, - // Decimal numbers like -145, 145, 0n145, 123ull etc - { wregex(c_wspcLParens + L"[-+]?" + c_decProgrammerChars + L"[lL]{0,2}" + c_wspcRParens), - wregex(c_wspcLParens + L"(0[nN])?" + c_decProgrammerChars + c_uIntSuffixes + c_wspcRParens) }, - // Octal numbers like 06, 010, 0t77, 0o77, 077ull etc - { wregex(c_wspcLParens + L"(0[otOT])?" + c_octProgrammerChars + c_uIntSuffixes + c_wspcRParens) }, - // Binary numbers like 011010110, 0010110, 10101001, 1001b, 0b1001, 0y1001, 0b1001ull - { wregex(c_wspcLParens + L"(0[byBY])?" + c_binProgrammerChars + c_uIntSuffixes + c_wspcRParens), - wregex(c_wspcLParens + c_binProgrammerChars + L"[bB]?" + c_wspcRParens) } } +static const array standardModePatterns = +{ + wregex(c_wspc + c_signedDecFloat + c_wspc) +}; +static const array scientificModePatterns = +{ + wregex(L"(" + c_wspc + L"[-+]?)|(" + c_wspcLParenSigned + L")" + c_signedDecFloat + c_wspcRParens), + wregex(L"(" + c_wspc + L"[-+]?)|(" + c_wspcLParenSigned + L")" + c_signedDecFloat + L"[e]([+]|[-])+\\d+" + c_wspcRParens) +}; +static const array, 4> programmerModePatterns = +{ { + // Hex numbers like 5F, 4A0C, 0xa9, 0xFFull, 47CDh + { + wregex(c_wspcLParens + L"(0[xX])?" + c_hexProgrammerChars + c_uIntSuffixes + c_wspcRParens), + wregex(c_wspcLParens + c_hexProgrammerChars + L"[hH]?" + c_wspcRParens) + }, + // Decimal numbers like -145, 145, 0n145, 123ull etc + { + wregex(c_wspcLParens + L"[-+]?" + c_decProgrammerChars + L"[lL]{0,2}" +c_wspcRParens), + wregex(c_wspcLParens + L"(0[nN])?" + c_decProgrammerChars + c_uIntSuffixes + c_wspcRParens) + }, + // Octal numbers like 06, 010, 0t77, 0o77, 077ull etc + { + wregex(c_wspcLParens + L"(0[otOT])?" + c_octProgrammerChars + c_uIntSuffixes + c_wspcRParens) + }, + // Binary numbers like 011010110, 0010110, 10101001, 1001b, 0b1001, 0y1001, 0b1001ull + { + wregex(c_wspcLParens + L"(0[byBY])?" + c_binProgrammerChars + c_uIntSuffixes + c_wspcRParens), + wregex(c_wspcLParens + c_binProgrammerChars + L"[bB]?" + c_wspcRParens) + } + } }; +static const array unitConverterPatterns = +{ + wregex(c_wspc + L"[-+]?\\d*[.]?\\d*" + c_wspc) }; -static const array unitConverterPatterns = { wregex(c_wspc + L"[-+]?\\d*[.]?\\d*" + c_wspc) }; -void CopyPasteManager::CopyToClipboard(String ^ stringToCopy) +void CopyPasteManager::CopyToClipboard(String^ stringToCopy) { // Copy the string to the clipboard auto dataPackage = ref new DataPackage(); @@ -63,7 +84,7 @@ void CopyPasteManager::CopyToClipboard(String ^ stringToCopy) Clipboard::SetContent(dataPackage); } -task CopyPasteManager::GetStringToPaste(ViewMode mode, CategoryGroupType modeType, int programmerNumberBase, int bitLengthType) +task CopyPasteManager::GetStringToPaste(ViewMode mode, CategoryGroupType modeType, int programmerNumberBase, int bitLengthType) { // Retrieve the text in the clipboard auto dataPackageView = Clipboard::GetContent(); @@ -74,36 +95,35 @@ task CopyPasteManager::GetStringToPaste(ViewMode mode, CategoryGroupTy //-- add support to allow pasting for expressions like 1.3e12(as of now we allow 1.3e+12) return create_task((dataPackageView->GetTextAsync(::StandardDataFormats::Text))) - .then([mode, modeType, programmerNumberBase, - bitLengthType](String ^ pastedText) { return ValidatePasteExpression(pastedText, mode, modeType, programmerNumberBase, bitLengthType); }, - task_continuation_context::use_arbitrary()); + .then([mode, modeType, programmerNumberBase, bitLengthType](String^ pastedText) + { + return ValidatePasteExpression(pastedText, mode, modeType, programmerNumberBase, bitLengthType); + } + , task_continuation_context::use_arbitrary()); } int CopyPasteManager::ClipboardTextFormat() { - int result = -1; - - auto dataPackageView = Clipboard::GetContent(); + const auto dataPackageView = Clipboard::GetContent(); for (int i = 0; i < RTL_NUMBER_OF(supportedFormats); i++) { if (dataPackageView->Contains(supportedFormats[i])) { - result = i; - break; + return i; } } - return result; + return -1; } -String ^ CopyPasteManager::ValidatePasteExpression(String ^ pastedText, ViewMode mode, int programmerNumberBase, int bitLengthType) +String^ CopyPasteManager::ValidatePasteExpression(String^ pastedText, ViewMode mode, int programmerNumberBase, int bitLengthType) { return CopyPasteManager::ValidatePasteExpression(pastedText, mode, NavCategory::GetGroupType(mode), programmerNumberBase, bitLengthType); } // return "NoOp" if pastedText is invalid else return pastedText -String ^ CopyPasteManager::ValidatePasteExpression(String ^ pastedText, ViewMode mode, CategoryGroupType modeType, int programmerNumberBase, int bitLengthType) +String^ CopyPasteManager::ValidatePasteExpression(String^ pastedText, ViewMode mode, CategoryGroupType modeType, int programmerNumberBase, int bitLengthType) { if (pastedText->Length() > MaxPasteableLength) { @@ -115,7 +135,7 @@ String ^ CopyPasteManager::ValidatePasteExpression(String ^ pastedText, ViewMode wstring pasteExpression = pastedText->Data(); // Get english translated expression - String ^ englishString = LocalizationSettings::GetInstance().GetEnglishValueFromLocalizedDigits(pasteExpression); + String^ englishString = LocalizationSettings::GetInstance().GetEnglishValueFromLocalizedDigits(pasteExpression); // Removing the spaces, comma separator from the pasteExpression to allow pasting of expressions like 1 + 2+1,333 pasteExpression = RemoveUnwantedCharsFromWstring(englishString->Data()); @@ -203,8 +223,7 @@ vector CopyPasteManager::ExtractOperands(const wstring& pasteExpression if ((pasteExpression.at(i) == L'+') || (pasteExpression.at(i) == L'-')) { // don't break the expression into operands if the encountered character corresponds to sign command(+-) - if (isPreviousOpenParen || startOfExpression || isPreviousOperator - || ((mode != ViewMode::Programmer) && !((i != 0) && (pasteExpression.at(i - 1) != L'e')))) + if (isPreviousOpenParen || startOfExpression || isPreviousOperator || ((mode != ViewMode::Programmer) && !((i != 0) && (pasteExpression.at(i - 1) != L'e')))) { isPreviousOperator = false; continue; @@ -247,13 +266,8 @@ bool CopyPasteManager::ExpressionRegExMatch(vector operands, ViewMode m return false; } - bool expMatched = true; vector patterns{}; - - pair operandLimits = GetMaxOperandLengthAndValue(mode, modeType, programmerNumberBase, bitLengthType); - size_t maxOperandLength = operandLimits.first; - uint64_t maxOperandValue = operandLimits.second; - + if (mode == ViewMode::Standard) { patterns.assign(standardModePatterns.begin(), standardModePatterns.end()); @@ -271,11 +285,14 @@ bool CopyPasteManager::ExpressionRegExMatch(vector operands, ViewMode m patterns.assign(unitConverterPatterns.begin(), unitConverterPatterns.end()); } - for (const wstring& operand : operands) + const auto [maxOperandLength, maxOperandValue] = GetMaxOperandLengthAndValue(mode, modeType, programmerNumberBase, bitLengthType); + bool expMatched = true; + + for (const auto& operand : operands) { // Each operand only needs to match one of the available patterns. bool operandMatched = false; - for (const wregex& pattern : patterns) + for (const auto& pattern : patterns) { operandMatched = operandMatched || regex_match(operand, pattern); } @@ -284,7 +301,7 @@ bool CopyPasteManager::ExpressionRegExMatch(vector operands, ViewMode m { // Remove characters that are valid in the expression but we do not want to include in length calculations // or which will break conversion from string-to-ULL. - wstring operandValue = SanitizeOperand(operand); + const wstring operandValue = SanitizeOperand(operand); // If an operand exceeds the maximum length allowed, break and return. if (OperandLength(operandValue, mode, modeType, programmerNumberBase) > maxOperandLength) @@ -320,16 +337,16 @@ bool CopyPasteManager::ExpressionRegExMatch(vector operands, ViewMode m pair CopyPasteManager::GetMaxOperandLengthAndValue(ViewMode mode, CategoryGroupType modeType, int programmerNumberBase, int bitLengthType) { - size_t maxLength = 0; - uint64_t maxValue = 0; - + constexpr size_t defaultMaxOperandLength = 0; + constexpr uint64_t defaultMaxValue = 0; + if (mode == ViewMode::Standard) { - maxLength = MaxStandardOperandLength; + return make_pair(MaxStandardOperandLength, defaultMaxValue); } else if (mode == ViewMode::Scientific) { - maxLength = MaxScientificOperandLength; + return make_pair(MaxScientificOperandLength, defaultMaxValue); } else if (mode == ViewMode::Programmer) { @@ -369,15 +386,17 @@ pair CopyPasteManager::GetMaxOperandLengthAndValue(ViewMode mo unsigned int signBit = (programmerNumberBase == DecBase) ? 1 : 0; - maxLength = (size_t)ceil((bitLength - signBit) / bitsPerDigit); - maxValue = UINT64_MAX >> (MaxProgrammerBitLength - (bitLength - signBit)); + const auto maxLength = static_cast(ceil((bitLength - signBit) / bitsPerDigit)); + const uint64_t maxValue = UINT64_MAX >> (MaxProgrammerBitLength - (bitLength - signBit)); + + return make_pair(maxLength, maxValue); } else if (modeType == CategoryGroupType::Converter) { - maxLength = MaxConverterInputLength; + return make_pair(MaxConverterInputLength, defaultMaxValue); } - return make_pair(maxLength, maxValue); + return make_pair(defaultMaxOperandLength, defaultMaxValue); } wstring CopyPasteManager::SanitizeOperand(const wstring& operand) @@ -396,8 +415,7 @@ bool CopyPasteManager::TryOperandToULL(const wstring& operand, int numberBase, u return false; } - // Default to base10 - int intBase = 10; + int intBase; switch (numberBase) { case HexBase: @@ -409,6 +427,7 @@ bool CopyPasteManager::TryOperandToULL(const wstring& operand, int numberBase, u case BinBase: intBase = 2; break; + default: case DecBase: intBase = 10; break; @@ -420,11 +439,11 @@ bool CopyPasteManager::TryOperandToULL(const wstring& operand, int numberBase, u result = stoull(operand, &size, intBase); return true; } - catch (invalid_argument) + catch (const invalid_argument&) { // Do nothing } - catch (out_of_range) + catch (const out_of_range&) { // Do nothing } @@ -432,35 +451,28 @@ bool CopyPasteManager::TryOperandToULL(const wstring& operand, int numberBase, u return false; } -size_t CopyPasteManager::OperandLength(wstring operand, ViewMode mode, CategoryGroupType modeType, int programmerNumberBase) -{ - size_t len = 0; - if (mode == ViewMode::Standard || mode == ViewMode::Scientific) - { - len = StandardScientificOperandLength(operand); - } - else if (mode == ViewMode::Programmer) - { - len = ProgrammerOperandLength(operand, programmerNumberBase); - } - else if (modeType == CategoryGroupType::Converter) - { - len = operand.length(); +size_t CopyPasteManager::OperandLength(const wstring& operand, ViewMode mode, CategoryGroupType modeType, int programmerNumberBase) +{ + if (modeType == CategoryGroupType::Converter) { + return operand.length(); } - return len; + switch(mode) { + case ViewMode::Standard: + case ViewMode::Scientific: + return StandardScientificOperandLength(operand); + + case ViewMode::Programmer: + return ProgrammerOperandLength(operand, programmerNumberBase); + + default: + return 0; + } } -size_t CopyPasteManager::StandardScientificOperandLength(wstring operand) -{ - bool hasDecimal = false; - for (size_t i = 0; i < operand.length(); i++) - { - if (operand[i] == L'.') - { - hasDecimal = true; - } - } +size_t CopyPasteManager::StandardScientificOperandLength(const wstring& operand) +{ + const bool hasDecimal = operand.find('.') != wstring::npos; if (hasDecimal) { @@ -482,8 +494,7 @@ size_t CopyPasteManager::StandardScientificOperandLength(wstring operand) size_t CopyPasteManager::ProgrammerOperandLength(const wstring& operand, int numberBase) { - size_t len = operand.length(); - + vector prefixes{}; vector suffixes{}; switch (numberBase) @@ -504,7 +515,7 @@ size_t CopyPasteManager::ProgrammerOperandLength(const wstring& operand, int num break; default: // No defined prefixes/suffixes - break; + return 0; } // UInt suffixes are common across all modes @@ -514,9 +525,11 @@ size_t CopyPasteManager::ProgrammerOperandLength(const wstring& operand, int num wstring operandUpper = operand; transform(operandUpper.begin(), operandUpper.end(), operandUpper.begin(), towupper); + size_t len = operand.length(); + // Detect if there is a suffix and subtract its length // Check suffixes first to allow e.g. "0b" to result in length 1 (value 0), rather than length 0 (no value). - for (const wstring& suffix : suffixes) + for (const auto& suffix : suffixes) { if (len < suffix.length()) { @@ -531,7 +544,7 @@ size_t CopyPasteManager::ProgrammerOperandLength(const wstring& operand, int num } // Detect if there is a prefix and subtract its length - for (const wstring& prefix : prefixes) + for (const auto& prefix : prefixes) { if (len < prefix.length()) { diff --git a/src/CalcViewModel/Common/CopyPasteManager.h b/src/CalcViewModel/Common/CopyPasteManager.h index 1d23414b..9d6fd669 100644 --- a/src/CalcViewModel/Common/CopyPasteManager.h +++ b/src/CalcViewModel/Common/CopyPasteManager.h @@ -13,21 +13,20 @@ namespace CalculatorUnitTests namespace CalculatorApp { -#define QwordType 1 -#define DwordType 2 -#define WordType 3 -#define ByteType 4 -#define HexBase 5 -#define DecBase 6 -#define OctBase 7 -#define BinBase 8 + inline constexpr auto QwordType = 1; + inline constexpr auto DwordType = 2; + inline constexpr auto WordType = 3; + inline constexpr auto ByteType = 4; + inline constexpr auto HexBase = 5; + inline constexpr auto DecBase = 6; + inline constexpr auto OctBase = 7; + inline constexpr auto BinBase = 8; class CopyPasteManager { public: - static void CopyToClipboard(Platform::String ^ stringToCopy); - static concurrency::task GetStringToPaste(CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, - int programmerNumberBase = -1, int bitLengthType = -1); + static void CopyToClipboard(Platform::String^ stringToCopy); + static concurrency::task GetStringToPaste(CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, int programmerNumberBase = -1, int bitLengthType = -1); static bool HasStringToPaste() { return ClipboardTextFormat() >= 0; @@ -37,24 +36,26 @@ namespace CalculatorApp private: static int ClipboardTextFormat(); - static Platform::String - ^ ValidatePasteExpression(Platform::String ^ pastedText, CalculatorApp::Common::ViewMode mode, int programmerNumberBase, int bitLengthType); - static Platform::String - ^ ValidatePasteExpression(Platform::String ^ pastedText, CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, - int programmerNumberBase, int bitLengthType); + static Platform::String^ ValidatePasteExpression( + Platform::String^ pastedText, + CalculatorApp::Common::ViewMode mode, + int programmerNumberBase, + int bitLengthType); + static Platform::String^ ValidatePasteExpression( + Platform::String^ pastedText, + CalculatorApp::Common::ViewMode mode, + CalculatorApp::Common::CategoryGroupType modeType, + int programmerNumberBase, + int bitLengthType); - static std::vector ExtractOperands(const std::wstring& pasteExpression, CalculatorApp::Common::ViewMode mode, - int programmerNumberBase = -1, int bitLengthType = -1); - static bool ExpressionRegExMatch(std::vector operands, CalculatorApp::Common::ViewMode mode, - CalculatorApp::Common::CategoryGroupType modeType, int programmerNumberBase = -1, int bitLengthType = -1); + static std::vector ExtractOperands(const std::wstring& pasteExpression, CalculatorApp::Common::ViewMode mode, int programmerNumberBase = -1, int bitLengthType = -1); + static bool ExpressionRegExMatch(std::vector operands, CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, int programmerNumberBase = -1, int bitLengthType = -1); - static std::pair GetMaxOperandLengthAndValue(CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, - int programmerNumberBase = -1, int bitLengthType = -1); + static std::pair GetMaxOperandLengthAndValue(CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, int programmerNumberBase = -1, int bitLengthType = -1); static std::wstring SanitizeOperand(const std::wstring& operand); static bool TryOperandToULL(const std::wstring& operand, int numberBase, unsigned long long int& result); - static size_t OperandLength(std::wstring operand, CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, - int programmerNumberBase = -1); - static size_t StandardScientificOperandLength(std::wstring operand); + static size_t OperandLength(const std::wstring& operand, CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, int programmerNumberBase = -1); + static size_t StandardScientificOperandLength(const std::wstring& operand); static size_t ProgrammerOperandLength(const std::wstring& operand, int numberBase); static std::wstring RemoveUnwantedCharsFromWstring(const std::wstring& input); @@ -66,7 +67,7 @@ namespace CalculatorApp static constexpr size_t MaxExponentLength = 4; static constexpr size_t MaxProgrammerBitLength = 64; - static Platform::String ^ supportedFormats[]; + static Platform::String^ supportedFormats[]; friend class CalculatorUnitTests::CopyPasteManagerTest; }; diff --git a/src/CalcViewModel/Common/DateCalculator.cpp b/src/CalcViewModel/Common/DateCalculator.cpp index b6b1388c..de42fbe9 100644 --- a/src/CalcViewModel/Common/DateCalculator.cpp +++ b/src/CalcViewModel/Common/DateCalculator.cpp @@ -20,13 +20,27 @@ DateCalculationEngine::DateCalculationEngine(_In_ String ^ calendarIdentifier) // Returns: True if function succeeds to calculate the date else returns False bool DateCalculationEngine::AddDuration(_In_ DateTime startDate, _In_ const DateDifference& duration, _Out_ DateTime* endDate) { + auto currentCalendarSystem = m_calendar->GetCalendarSystem(); + try { m_calendar->SetDateTime(startDate); if (duration.year != 0) { + // The Japanese Era system can have multiple year partitions within the same year. + // For example, April 30, 2019 is denoted April 30, Heisei 31; May 1, 2019 is denoted as May 1, Reiwa 1. + // The Calendar treats Heisei 31 and Reiwa 1 as separate years, which results in some unexpected behaviors where subtracting a year from Reiwa 1 results in a date in Heisei 31. + // To provide the expected result across era boundaries, we first convert the Japanese era system to a Gregorian system, do date math, and then convert back to the Japanese era system. + // This works because the Japanese era system maintains the same year/month boundaries and durations as the Gregorian system and is only different in display value. + if (currentCalendarSystem == CalendarIdentifiers::Japanese) + { + m_calendar->ChangeCalendarSystem(CalendarIdentifiers::Gregorian); + } + m_calendar->AddYears(duration.year); + + m_calendar->ChangeCalendarSystem(currentCalendarSystem); } if (duration.month != 0) { @@ -41,6 +55,9 @@ bool DateCalculationEngine::AddDuration(_In_ DateTime startDate, _In_ const Date } catch (Platform::InvalidArgumentException ^ ex) { + // ensure that we revert to the correct calendar system + m_calendar->ChangeCalendarSystem(currentCalendarSystem); + // Do nothing return false; } @@ -52,6 +69,8 @@ bool DateCalculationEngine::AddDuration(_In_ DateTime startDate, _In_ const Date // Returns: True if function succeeds to calculate the date else returns False bool DateCalculationEngine::SubtractDuration(_In_ DateTime startDate, _In_ const DateDifference& duration, _Out_ DateTime* endDate) { + auto currentCalendarSystem = m_calendar->GetCalendarSystem(); + // For Subtract the Algorithm is different than Add. Here the smaller units are subtracted first // and then the larger units. try @@ -68,13 +87,28 @@ bool DateCalculationEngine::SubtractDuration(_In_ DateTime startDate, _In_ const } if (duration.year != 0) { + // The Japanese Era system can have multiple year partitions within the same year. + // For example, April 30, 2019 is denoted April 30, Heisei 31; May 1, 2019 is denoted as May 1, Reiwa 1. + // The Calendar treats Heisei 31 and Reiwa 1 as separate years, which results in some unexpected behaviors where subtracting a year from Reiwa 1 results in a date in Heisei 31. + // To provide the expected result across era boundaries, we first convert the Japanese era system to a Gregorian system, do date math, and then convert back to the Japanese era system. + // This works because the Japanese era system maintains the same year/month boundaries and durations as the Gregorian system and is only different in display value. + if (currentCalendarSystem == CalendarIdentifiers::Japanese) + { + m_calendar->ChangeCalendarSystem(CalendarIdentifiers::Gregorian); + } + m_calendar->AddYears(-duration.year); + + m_calendar->ChangeCalendarSystem(currentCalendarSystem); } *endDate = m_calendar->GetDateTime(); } catch (Platform::InvalidArgumentException ^ ex) { + // ensure that we revert to the correct calendar system + m_calendar->ChangeCalendarSystem(currentCalendarSystem); + // Do nothing return false; } diff --git a/src/CalcViewModel/StandardCalculatorViewModel.cpp b/src/CalcViewModel/StandardCalculatorViewModel.cpp index 8d9de3be..be656b2e 100644 --- a/src/CalcViewModel/StandardCalculatorViewModel.cpp +++ b/src/CalcViewModel/StandardCalculatorViewModel.cpp @@ -782,6 +782,12 @@ void StandardCalculatorViewModel::OnPaste(String ^ pastedString, ViewMode mode) NumbersAndOperatorsEnum mappedNumOp = MapCharacterToButtonId(*it, canSendNegate); + if (mappedNumOp == NumbersAndOperatorsEnum::None) + { + ++it; + continue; + } + if (isFirstLegalChar || isPreviousOperator) { isFirstLegalChar = false; @@ -803,74 +809,71 @@ void StandardCalculatorViewModel::OnPaste(String ^ pastedString, ViewMode mode) } } - if (mappedNumOp != NumbersAndOperatorsEnum::None) + switch (mappedNumOp) { - switch (mappedNumOp) + // Opening parenthesis starts a new expression and pushes negation state onto the stack + case NumbersAndOperatorsEnum::OpenParenthesis: + negateStack.push_back(sendNegate); + sendNegate = false; + break; + + // Closing parenthesis pops the negation state off the stack and sends it down to the calc engine + case NumbersAndOperatorsEnum::CloseParenthesis: + if (!negateStack.empty()) { - // Opening parenthesis starts a new expression and pushes negation state onto the stack - case NumbersAndOperatorsEnum::OpenParenthesis: - negateStack.push_back(sendNegate); - sendNegate = false; - break; - - // Closing parenthesis pops the negation state off the stack and sends it down to the calc engine - case NumbersAndOperatorsEnum::CloseParenthesis: - if (!negateStack.empty()) - { - sendNegate = negateStack.back(); - negateStack.pop_back(); - canSendNegate = true; - } - else - { - // Don't send a closing parenthesis if a matching opening parenthesis hasn't been sent already - sendCommand = false; - } - break; - - case NumbersAndOperatorsEnum::Zero: - case NumbersAndOperatorsEnum::One: - case NumbersAndOperatorsEnum::Two: - case NumbersAndOperatorsEnum::Three: - case NumbersAndOperatorsEnum::Four: - case NumbersAndOperatorsEnum::Five: - case NumbersAndOperatorsEnum::Six: - case NumbersAndOperatorsEnum::Seven: - case NumbersAndOperatorsEnum::Eight: - case NumbersAndOperatorsEnum::Nine: - processedDigit = true; - break; - - case NumbersAndOperatorsEnum::Add: - case NumbersAndOperatorsEnum::Subtract: - case NumbersAndOperatorsEnum::Multiply: - case NumbersAndOperatorsEnum::Divide: - isPreviousOperator = true; - break; + sendNegate = negateStack.back(); + negateStack.pop_back(); + canSendNegate = true; } - - if (sendCommand) + else { - sentEquals = (mappedNumOp == NumbersAndOperatorsEnum::Equals); - Command cmdenum = ConvertToOperatorsEnum(mappedNumOp); - m_standardCalculatorManager.SendCommand(cmdenum); + // Don't send a closing parenthesis if a matching opening parenthesis hasn't been sent already + sendCommand = false; + } + break; - // The CalcEngine state machine won't allow the negate command to be sent before any - // other digits, so instead a flag is set and the command is sent after the first appropriate - // command. - if (sendNegate) + case NumbersAndOperatorsEnum::Zero: + case NumbersAndOperatorsEnum::One: + case NumbersAndOperatorsEnum::Two: + case NumbersAndOperatorsEnum::Three: + case NumbersAndOperatorsEnum::Four: + case NumbersAndOperatorsEnum::Five: + case NumbersAndOperatorsEnum::Six: + case NumbersAndOperatorsEnum::Seven: + case NumbersAndOperatorsEnum::Eight: + case NumbersAndOperatorsEnum::Nine: + processedDigit = true; + break; + + case NumbersAndOperatorsEnum::Add: + case NumbersAndOperatorsEnum::Subtract: + case NumbersAndOperatorsEnum::Multiply: + case NumbersAndOperatorsEnum::Divide: + isPreviousOperator = true; + break; + } + + if (sendCommand) + { + sentEquals = (mappedNumOp == NumbersAndOperatorsEnum::Equals); + Command cmdenum = ConvertToOperatorsEnum(mappedNumOp); + m_standardCalculatorManager.SendCommand(cmdenum); + + // The CalcEngine state machine won't allow the negate command to be sent before any + // other digits, so instead a flag is set and the command is sent after the first appropriate + // command. + if (sendNegate) + { + if (canSendNegate) { - if (canSendNegate) - { - Command cmdNegate = ConvertToOperatorsEnum(NumbersAndOperatorsEnum::Negate); - m_standardCalculatorManager.SendCommand(cmdNegate); - } + Command cmdNegate = ConvertToOperatorsEnum(NumbersAndOperatorsEnum::Negate); + m_standardCalculatorManager.SendCommand(cmdNegate); + } - // Can't send negate on a leading zero, so wait until the appropriate time to send it. - if (NumbersAndOperatorsEnum::Zero != mappedNumOp && NumbersAndOperatorsEnum::Decimal != mappedNumOp) - { - sendNegate = false; - } + // Can't send negate on a leading zero, so wait until the appropriate time to send it. + if (NumbersAndOperatorsEnum::Zero != mappedNumOp && NumbersAndOperatorsEnum::Decimal != mappedNumOp) + { + sendNegate = false; } } } diff --git a/src/Calculator.sln b/src/Calculator.sln index 329ff19f..47084340 100644 --- a/src/Calculator.sln +++ b/src/Calculator.sln @@ -18,6 +18,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CalcViewModel", "CalcViewMo EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CalculatorUnitTests", "CalculatorUnitTests\CalculatorUnitTests.vcxproj", "{D3BAED2C-4B07-4E1D-8807-9D6499450349}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CalculatorUITests", "CalculatorUITests\CalculatorUITests.csproj", "{B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM @@ -102,6 +104,22 @@ Global {D3BAED2C-4B07-4E1D-8807-9D6499450349}.Release|x86.ActiveCfg = Release|Win32 {D3BAED2C-4B07-4E1D-8807-9D6499450349}.Release|x86.Build.0 = Release|Win32 {D3BAED2C-4B07-4E1D-8807-9D6499450349}.Release|x86.Deploy.0 = Release|Win32 + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Debug|ARM.ActiveCfg = Debug|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Debug|ARM.Build.0 = Debug|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Debug|ARM64.Build.0 = Debug|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Debug|x64.ActiveCfg = Debug|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Debug|x64.Build.0 = Debug|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Debug|x86.ActiveCfg = Debug|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Debug|x86.Build.0 = Debug|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Release|ARM.ActiveCfg = Release|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Release|ARM.Build.0 = Release|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Release|ARM64.ActiveCfg = Release|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Release|ARM64.Build.0 = Release|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Release|x64.ActiveCfg = Release|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Release|x64.Build.0 = Release|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Release|x86.ActiveCfg = Release|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Calculator/Views/UnitConverter.xaml b/src/Calculator/Views/UnitConverter.xaml index 1d52c3eb..a1a0a8d0 100644 --- a/src/Calculator/Views/UnitConverter.xaml +++ b/src/Calculator/Views/UnitConverter.xaml @@ -615,8 +615,7 @@ x:Uid="RefreshButtonText" Foreground="{ThemeResource SystemControlHyperlinkBaseHighBrush}" Click="CurrencyRefreshButton_Click"/> - - + diff --git a/src/CalculatorUITests/CalculatorSession.cs b/src/CalculatorUITests/CalculatorSession.cs new file mode 100644 index 00000000..9e4caa9b --- /dev/null +++ b/src/CalculatorUITests/CalculatorSession.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenQA.Selenium.Appium; +using OpenQA.Selenium.Appium.Windows; +using System; + +namespace CalculatorUITests +{ + public class CalculatorSession + { + // Note: append /wd/hub to the URL if you're directing the test at Appium + private const string WindowsApplicationDriverUrl = "http://127.0.0.1:4723"; + private const string CalculatorAppId = "Microsoft.WindowsCalculator.Dev_8wekyb3d8bbwe!App"; + protected static WindowsDriver session; + + public static void Setup(TestContext context) + { + // Launch Calculator application if it is not yet launched + if (session == null) + { + // Create a new session to bring up an instance of the Calculator application + // Note: Multiple calculator windows (instances) share the same process Id + var options = new AppiumOptions(); + options.AddAdditionalCapability("app", CalculatorAppId); + options.AddAdditionalCapability("deviceName", "WindowsPC"); + session = new WindowsDriver(new Uri(WindowsApplicationDriverUrl), options); + session.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(180); + Assert.IsNotNull(session); + } + } + + public static void TearDown() + { + // Close the application and delete the session + if (session != null) + { + session.Quit(); + session = null; + } + } + } +} diff --git a/src/CalculatorUITests/CalculatorUITests.csproj b/src/CalculatorUITests/CalculatorUITests.csproj new file mode 100644 index 00000000..42ed8672 --- /dev/null +++ b/src/CalculatorUITests/CalculatorUITests.csproj @@ -0,0 +1,22 @@ + + + + netcoreapp2.1 + + false + + + + + + + + + + + + PreserveNewest + + + + diff --git a/src/CalculatorUITests/CalculatorUITests.runsettings b/src/CalculatorUITests/CalculatorUITests.runsettings new file mode 100644 index 00000000..ac9968d5 --- /dev/null +++ b/src/CalculatorUITests/CalculatorUITests.runsettings @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/CalculatorUITests/ScenarioStandard.cs b/src/CalculatorUITests/ScenarioStandard.cs new file mode 100644 index 00000000..8bc824a3 --- /dev/null +++ b/src/CalculatorUITests/ScenarioStandard.cs @@ -0,0 +1,112 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenQA.Selenium.Appium.Windows; +using System.Threading; +using System; + +namespace CalculatorUITests +{ + [TestClass] + public class StandardModeTests : CalculatorSession + { + private static WindowsElement header; + private static WindowsElement calculatorResult; + + [TestMethod] + public void Addition() + { + // Find the buttons by their names and click them in sequence to perform 1 + 7 = 8 + session.FindElementByName("One").Click(); + session.FindElementByName("Plus").Click(); + session.FindElementByName("Seven").Click(); + session.FindElementByName("Equals").Click(); + Assert.AreEqual("8", GetCalculatorResultText()); + } + + [TestMethod] + public void Division() + { + // Find the buttons by their accessibility ids and click them in sequence to perform 88 / 11 = 8 + session.FindElementByAccessibilityId("num8Button").Click(); + session.FindElementByAccessibilityId("num8Button").Click(); + session.FindElementByAccessibilityId("divideButton").Click(); + session.FindElementByAccessibilityId("num1Button").Click(); + session.FindElementByAccessibilityId("num1Button").Click(); + session.FindElementByAccessibilityId("equalButton").Click(); + Assert.AreEqual("8", GetCalculatorResultText()); + } + + [TestMethod] + public void Multiplication() + { + session.FindElementByAccessibilityId("num9Button").Click(); + session.FindElementByAccessibilityId("multiplyButton").Click(); + session.FindElementByAccessibilityId("num9Button").Click(); + session.FindElementByAccessibilityId("equalButton").Click(); + Assert.AreEqual("81", GetCalculatorResultText()); + } + + [TestMethod] + public void Subtraction() + { + // Find the buttons by their accessibility ids using XPath and click them in sequence to perform 9 - 1 = 8 + session.FindElementByAccessibilityId("num9Button").Click(); + session.FindElementByAccessibilityId("minusButton").Click(); + session.FindElementByAccessibilityId("num1Button").Click(); + session.FindElementByAccessibilityId("equalButton").Click(); + Assert.AreEqual("8", GetCalculatorResultText()); + } + + + [ClassInitialize] + public static void ClassInitialize(TestContext context) + { + // Create session to launch a Calculator window + Setup(context); + // Identify calculator mode by locating the header + try + { + header = session.FindElementByAccessibilityId("Header"); + } + catch + { + header = session.FindElementByAccessibilityId("ContentPresenter"); + } + + // Ensure that calculator is in standard mode + if (!header.Text.Equals("Standard", StringComparison.OrdinalIgnoreCase)) + { + session.FindElementByAccessibilityId("TogglePaneButton").Click(); + Thread.Sleep(TimeSpan.FromSeconds(1)); + var splitViewPane = session.FindElementByClassName("SplitViewPane"); + splitViewPane.FindElementByName("Standard Calculator").Click(); + Thread.Sleep(TimeSpan.FromSeconds(1)); + Assert.IsTrue(header.Text.Equals("Standard", StringComparison.OrdinalIgnoreCase)); + } + + // Locate the calculatorResult element + calculatorResult = session.FindElementByAccessibilityId("CalculatorResults"); + Assert.IsNotNull(calculatorResult); + } + + [ClassCleanup] + public static void ClassCleanup() + { + TearDown(); + } + + [TestInitialize] + public void Clear() + { + session.FindElementByName("Clear").Click(); + Assert.AreEqual("0", GetCalculatorResultText()); + } + + private string GetCalculatorResultText() + { + return calculatorResult.Text.Replace("Display is", string.Empty).Trim(); + } + } +} diff --git a/src/CalculatorUnitTests/DateCalculatorUnitTests.cpp b/src/CalculatorUnitTests/DateCalculatorUnitTests.cpp index 4c02dd05..0318f152 100644 --- a/src/CalculatorUnitTests/DateCalculatorUnitTests.cpp +++ b/src/CalculatorUnitTests/DateCalculatorUnitTests.cpp @@ -44,914 +44,677 @@ namespace DateCalculationUnitTests DateTimeTestCase datetimeAddCase[c_addCases]; DateTimeTestCase datetimeSubtractCase[c_subtractCases]; + // Test Class - TEST_CLASS(DateCalculatorUnitTests){ public: TEST_CLASS_INITIALIZE(TestClassSetup){ /* Test Case Data */ - - // Dates - DD.MM.YYYY - /*31.12.9999*/ date[0].wYear = 9999; - date[0].wMonth = 12; - date[0].wDayOfWeek = 5; - date[0].wDay = 31; - date[0].wHour = 0; - date[0].wMinute = 0; - date[0].wSecond = 0; - date[0].wMilliseconds = 0; - /*30.12.9999*/ date[1].wYear = 9999; - date[1].wMonth = 12; - date[1].wDayOfWeek = 4; - date[1].wDay = 30; - date[1].wHour = 0; - date[1].wMinute = 0; - date[1].wSecond = 0; - date[1].wMilliseconds = 0; - /*31.12.9998*/ date[2].wYear = 9998; - date[2].wMonth = 12; - date[2].wDayOfWeek = 4; - date[2].wDay = 31; - date[2].wHour = 0; - date[2].wMinute = 0; - date[2].wSecond = 0; - date[2].wMilliseconds = 0; - /*01.01.1601*/ date[3].wYear = 1601; - date[3].wMonth = 1; - date[3].wDayOfWeek = 1; - date[3].wDay = 1; - date[3].wHour = 0; - date[3].wMinute = 0; - date[3].wSecond = 0; - date[3].wMilliseconds = 0; - /*02.01.1601*/ date[4].wYear = 1601; - date[4].wMonth = 1; - date[4].wDayOfWeek = 2; - date[4].wDay = 2; - date[4].wHour = 0; - date[4].wMinute = 0; - date[4].wSecond = 0; - date[4].wMilliseconds = 0; - /*10.05.2008*/ date[5].wYear = 2008; - date[5].wMonth = 5; - date[5].wDayOfWeek = 6; - date[5].wDay = 10; - date[5].wHour = 0; - date[5].wMinute = 0; - date[5].wSecond = 0; - date[5].wMilliseconds = 0; - /*10.03.2008*/ date[6].wYear = 2008; - date[6].wMonth = 3; - date[6].wDayOfWeek = 1; - date[6].wDay = 10; - date[6].wHour = 0; - date[6].wMinute = 0; - date[6].wSecond = 0; - date[6].wMilliseconds = 0; - /*29.02.2008*/ date[7].wYear = 2008; - date[7].wMonth = 2; - date[7].wDayOfWeek = 5; - date[7].wDay = 29; - date[7].wHour = 0; - date[7].wMinute = 0; - date[7].wSecond = 0; - date[7].wMilliseconds = 0; - /*28.02.2007*/ date[8].wYear = 2007; - date[8].wMonth = 2; - date[8].wDayOfWeek = 3; - date[8].wDay = 28; - date[8].wHour = 0; - date[8].wMinute = 0; - date[8].wSecond = 0; - date[8].wMilliseconds = 0; - /*10.03.2007*/ date[9].wYear = 2007; - date[9].wMonth = 3; - date[9].wDayOfWeek = 6; - date[9].wDay = 10; - date[9].wHour = 0; - date[9].wMinute = 0; - date[9].wSecond = 0; - date[9].wMilliseconds = 0; - /*10.05.2007*/ date[10].wYear = 2007; - date[10].wMonth = 5; - date[10].wDayOfWeek = 4; - date[10].wDay = 10; - date[10].wHour = 0; - date[10].wMinute = 0; - date[10].wSecond = 0; - date[10].wMilliseconds = 0; - /*29.01.2008*/ date[11].wYear = 2008; - date[11].wMonth = 1; - date[11].wDayOfWeek = 2; - date[11].wDay = 29; - date[11].wHour = 0; - date[11].wMinute = 0; - date[11].wSecond = 0; - date[11].wMilliseconds = 0; - /*28.01.2007*/ date[12].wYear = 2007; - date[12].wMonth = 1; - date[12].wDayOfWeek = 0; - date[12].wDay = 28; - date[12].wHour = 0; - date[12].wMinute = 0; - date[12].wSecond = 0; - date[12].wMilliseconds = 0; - /*31.01.2008*/ date[13].wYear = 2008; - date[13].wMonth = 1; - date[13].wDayOfWeek = 4; - date[13].wDay = 31; - date[13].wHour = 0; - date[13].wMinute = 0; - date[13].wSecond = 0; - date[13].wMilliseconds = 0; - /*31.03.2008*/ date[14].wYear = 2008; - date[14].wMonth = 3; - date[14].wDayOfWeek = 1; - date[14].wDay = 31; - date[14].wHour = 0; - date[14].wMinute = 0; - date[14].wSecond = 0; - date[14].wMilliseconds = 0; - - // Date Differences - dateDifference[0].year = 1; - dateDifference[0].month = 1; - dateDifference[1].month = 1; - dateDifference[1].day = 10; - dateDifference[2].day = 2; - /*date[2]-[0]*/ dateDifference[3].week = 52; - dateDifference[3].day = 1; - /*date[2]-[0]*/ dateDifference[4].year = 1; - dateDifference[5].day = 365; - dateDifference[6].month = 1; - dateDifference[7].month = 1; - dateDifference[7].day = 2; - dateDifference[8].day = 31; - dateDifference[9].month = 11; - dateDifference[9].day = 1; - dateDifference[10].year = 8398; - dateDifference[10].month = 11; - dateDifference[10].day = 30; - dateDifference[11].year = 2008; - dateDifference[12].year = 7991; - dateDifference[12].month = 11; - dateDifference[13].week = 416998; - dateDifference[13].day = 1; - - /* Test Cases */ - - // Date Difference test cases - datetimeDifftest[0].startDate = date[0]; - datetimeDifftest[0].endDate = date[3]; - datetimeDifftest[0].dateDiff = dateDifference[10]; - datetimeDifftest[1].startDate = date[0]; - datetimeDifftest[1].endDate = date[2]; - datetimeDifftest[1].dateDiff = dateDifference[5]; - datetimeDifftest[2].startDate = date[0]; - datetimeDifftest[2].endDate = date[2]; - datetimeDifftest[2].dateDiff = dateDifference[4]; - datetimeDifftest[3].startDate = date[0]; - datetimeDifftest[3].endDate = date[2]; - datetimeDifftest[3].dateDiff = dateDifference[3]; - datetimeDifftest[4].startDate = date[14]; - datetimeDifftest[4].endDate = date[7]; - datetimeDifftest[4].dateDiff = dateDifference[7]; - datetimeDifftest[5].startDate = date[14]; - datetimeDifftest[5].endDate = date[7]; - datetimeDifftest[5].dateDiff = dateDifference[8]; - datetimeDifftest[6].startDate = date[11]; - datetimeDifftest[6].endDate = date[8]; - datetimeDifftest[6].dateDiff = dateDifference[9]; - datetimeDifftest[7].startDate = date[13]; - datetimeDifftest[7].endDate = date[0]; - datetimeDifftest[7].dateDiff = dateDifference[12]; - datetimeDifftest[8].startDate = date[13]; - datetimeDifftest[8].endDate = date[0]; - datetimeDifftest[8].dateDiff = dateDifference[13]; - - // Date Add Out of Bound test cases (Negative tests) - /*OutofBound*/ datetimeBoundAdd[0].startDate = date[1]; - datetimeBoundAdd[0].endDate = date[0]; - datetimeBoundAdd[0].dateDiff = dateDifference[2]; // on Add date[0] not used - /*OutofBound*/ datetimeBoundAdd[1].startDate = date[2]; - datetimeBoundAdd[1].endDate = date[0]; - datetimeBoundAdd[1].dateDiff = dateDifference[11]; // on Add date[0] not used - - // Date Subtract Out of Bound test cases (Negative tests) - /*OutofBound*/ datetimeBoundSubtract[0].startDate = date[3]; - datetimeBoundSubtract[0].endDate = date[0]; - datetimeBoundSubtract[0].dateDiff = dateDifference[2]; // on subtract date[0] not used - /*OutofBound*/ datetimeBoundSubtract[1].startDate = date[14]; - datetimeBoundSubtract[1].endDate = date[0]; - datetimeBoundSubtract[1].dateDiff = dateDifference[11]; // on subtract date[0] not used - - // Date Add test cases (Positive tests) - datetimeAddCase[0].startDate = date[13]; - datetimeAddCase[0].endDate = date[7]; - datetimeAddCase[0].dateDiff = dateDifference[6]; // add - datetimeAddCase[1].startDate = date[14]; - datetimeAddCase[1].endDate = date[5]; - datetimeAddCase[1].dateDiff = dateDifference[1]; // add - datetimeAddCase[2].startDate = date[13]; - datetimeAddCase[2].endDate = date[6]; - datetimeAddCase[2].dateDiff = dateDifference[1]; // add - - // Date Subtract test cases (Positive tests) - datetimeSubtractCase[0].startDate = date[14]; - datetimeSubtractCase[0].endDate = date[7]; - datetimeSubtractCase[0].dateDiff = dateDifference[6]; // subtract - datetimeSubtractCase[1].startDate = date[6]; - datetimeSubtractCase[1].endDate = date[11]; - datetimeSubtractCase[1].dateDiff = dateDifference[1]; // subtract - datetimeSubtractCase[2].startDate = date[9]; - datetimeSubtractCase[2].endDate = date[12]; - datetimeSubtractCase[2].dateDiff = dateDifference[1]; // subtract -} - -/* Duration Between Two Date Tests -- Timediff obtained after calculation should be checked to be identical */ -TEST_METHOD(TestDateDiff) -{ - // TODO - MSFT 10331900, fix this test - - // for (int testIndex = 0; testIndex < c_diffTestCase; testIndex++) - //{ - // DateDifference diff; - // DateUnit dateOutputFormat; - - // switch (testIndex) - // { - // case 0: - // case 2: - // dateOutputFormat = DateUnit::Year | DateUnit::Month | DateUnit::Day; - // break; - // case 1: - // dateOutputFormat = DateUnit::Day; - // break; - // case 3: - // case 8: - // dateOutputFormat = DateUnit::Week | DateUnit::Day; - // break; - // case 7: - // dateOutputFormat = DateUnit::Year | DateUnit::Month | DateUnit::Day; - // break; - // case 4: - // case 6: - // dateOutputFormat = DateUnit::Month | DateUnit::Day; - // break; - // case 5: - // dateOutputFormat = DateUnit::Day; - // break; - // } - - // // Calculate the difference - // m_DateCalcEngine.GetDateDifference(DateUtils::SystemTimeToDateTime(datetimeDifftest[testIndex].startDate), - // DateUtils::SystemTimeToDateTime(datetimeDifftest[testIndex].endDate), dateOutputFormat, &diff); - - // // Assert for the result - // bool areIdentical = true; - // if (diff.year != datetimeDifftest[testIndex].dateDiff.year || - // diff.month != datetimeDifftest[testIndex].dateDiff.month || - // diff.week != datetimeDifftest[testIndex].dateDiff.week || - // diff.day != datetimeDifftest[testIndex].dateDiff.day) - // { - // areIdentical = false; - // } - - // VERIFY_IS_TRUE(areIdentical); - //} -} - -/*Add Out of bound Tests*/ -TEST_METHOD(TestAddOob) -{ - // TODO - MSFT 10331900, fix this test - - // for (int testIndex = 0; testIndex< c_numAddOobDate; testIndex++) - //{ - // DateTime endDate; - - // // Add Duration - // bool isValid = m_DateCalcEngine.AddDuration(DateUtils::SystemTimeToDateTime(datetimeBoundAdd[testIndex].startDate), - // datetimeBoundAdd[testIndex].dateDiff, &endDate); - - // // Assert for the result - // VERIFY_IS_FALSE(isValid); - //} -} - -/*Subtract Out of bound Tests*/ -TEST_METHOD(TestSubtractOob) -{ - for (int testIndex = 0; testIndex < c_numSubtractOobDate; testIndex++) + TEST_CLASS(DateCalculatorUnitTests) { - DateTime endDate; + public: + TEST_CLASS_INITIALIZE(TestClassSetup) + { + /* Test Case Data */ - // Subtract Duration - bool isValid = m_DateCalcEngine.SubtractDuration(DateUtils::SystemTimeToDateTime(datetimeBoundSubtract[testIndex].startDate), - datetimeBoundSubtract[testIndex].dateDiff, &endDate); + // Dates - DD.MM.YYYY + /*31.12.9999*/ date[0].wYear = 9999; date[0].wMonth = 12; date[0].wDayOfWeek = 5; date[0].wDay = 31; date[0].wHour = 0; date[0].wMinute = 0; date[0].wSecond = 0; date[0].wMilliseconds = 0; + /*30.12.9999*/ date[1].wYear = 9999; date[1].wMonth = 12; date[1].wDayOfWeek = 4; date[1].wDay = 30; date[1].wHour = 0; date[1].wMinute = 0; date[1].wSecond = 0; date[1].wMilliseconds = 0; + /*31.12.9998*/ date[2].wYear = 9998; date[2].wMonth = 12; date[2].wDayOfWeek = 4; date[2].wDay = 31; date[2].wHour = 0; date[2].wMinute = 0; date[2].wSecond = 0; date[2].wMilliseconds = 0; + /*01.01.1601*/ date[3].wYear = 1601; date[3].wMonth = 1; date[3].wDayOfWeek = 1; date[3].wDay = 1; date[3].wHour = 0; date[3].wMinute = 0; date[3].wSecond = 0; date[3].wMilliseconds = 0; + /*02.01.1601*/ date[4].wYear = 1601; date[4].wMonth = 1; date[4].wDayOfWeek = 2; date[4].wDay = 2; date[4].wHour = 0; date[4].wMinute = 0; date[4].wSecond = 0; date[4].wMilliseconds = 0; + /*10.05.2008*/ date[5].wYear = 2008; date[5].wMonth = 5; date[5].wDayOfWeek = 6; date[5].wDay = 10; date[5].wHour = 0; date[5].wMinute = 0; date[5].wSecond = 0; date[5].wMilliseconds = 0; + /*10.03.2008*/ date[6].wYear = 2008; date[6].wMonth = 3; date[6].wDayOfWeek = 1; date[6].wDay = 10; date[6].wHour = 0; date[6].wMinute = 0; date[6].wSecond = 0; date[6].wMilliseconds = 0; + /*29.02.2008*/ date[7].wYear = 2008; date[7].wMonth = 2; date[7].wDayOfWeek = 5; date[7].wDay = 29; date[7].wHour = 0; date[7].wMinute = 0; date[7].wSecond = 0; date[7].wMilliseconds = 0; + /*28.02.2007*/ date[8].wYear = 2007; date[8].wMonth = 2; date[8].wDayOfWeek = 3; date[8].wDay = 28; date[8].wHour = 0; date[8].wMinute = 0; date[8].wSecond = 0; date[8].wMilliseconds = 0; + /*10.03.2007*/ date[9].wYear = 2007; date[9].wMonth = 3; date[9].wDayOfWeek = 6; date[9].wDay = 10; date[9].wHour = 0; date[9].wMinute = 0; date[9].wSecond = 0; date[9].wMilliseconds = 0; + /*10.05.2007*/ date[10].wYear = 2007; date[10].wMonth = 5; date[10].wDayOfWeek = 4; date[10].wDay = 10; date[10].wHour = 0; date[10].wMinute = 0; date[10].wSecond = 0; date[10].wMilliseconds = 0; + /*29.01.2008*/ date[11].wYear = 2008; date[11].wMonth = 1; date[11].wDayOfWeek = 2; date[11].wDay = 29; date[11].wHour = 0; date[11].wMinute = 0; date[11].wSecond = 0; date[11].wMilliseconds = 0; + /*28.01.2007*/ date[12].wYear = 2007; date[12].wMonth = 1; date[12].wDayOfWeek = 0; date[12].wDay = 28; date[12].wHour = 0; date[12].wMinute = 0; date[12].wSecond = 0; date[12].wMilliseconds = 0; + /*31.01.2008*/ date[13].wYear = 2008; date[13].wMonth = 1; date[13].wDayOfWeek = 4; date[13].wDay = 31; date[13].wHour = 0; date[13].wMinute = 0; date[13].wSecond = 0; date[13].wMilliseconds = 0; + /*31.03.2008*/ date[14].wYear = 2008; date[14].wMonth = 3; date[14].wDayOfWeek = 1; date[14].wDay = 31; date[14].wHour = 0; date[14].wMinute = 0; date[14].wSecond = 0; date[14].wMilliseconds = 0; - // Assert for the result - VERIFY_IS_FALSE(isValid); - } -} + // Date Differences + dateDifference[0].year = 1; dateDifference[0].month = 1; + dateDifference[1].month = 1; dateDifference[1].day = 10; + dateDifference[2].day = 2; + /*date[2]-[0]*/ dateDifference[3].week = 52; dateDifference[3].day = 1; + /*date[2]-[0]*/ dateDifference[4].year = 1; + dateDifference[5].day = 365; + dateDifference[6].month = 1; + dateDifference[7].month = 1; dateDifference[7].day = 2; + dateDifference[8].day = 31; + dateDifference[9].month = 11; dateDifference[9].day = 1; + dateDifference[10].year = 8398; dateDifference[10].month = 11; dateDifference[10].day = 30; + dateDifference[11].year = 2008; + dateDifference[12].year = 7991; dateDifference[12].month = 11; + dateDifference[13].week = 416998; dateDifference[13].day = 1; -// Add Tests -TEST_METHOD(TestAddition) -{ - // TODO - MSFT 10331900, fix this test - // for (int testIndex = 0; testIndex < c_addCases; testIndex++) - //{ - // DateTime endDate; - // // Add Duration - // bool isValid = m_DateCalcEngine.AddDuration(DateUtils::SystemTimeToDateTime(datetimeAddCase[testIndex].startDate), - // datetimeAddCase[testIndex].dateDiff, &endDate); + /* Test Cases */ - // // Assert for the result - // VERIFY_IS_TRUE(isValid); + // Date Difference test cases + datetimeDifftest[0].startDate = date[0]; datetimeDifftest[0].endDate = date[3]; datetimeDifftest[0].dateDiff = dateDifference[10]; + datetimeDifftest[1].startDate = date[0]; datetimeDifftest[1].endDate = date[2]; datetimeDifftest[1].dateDiff = dateDifference[5]; + datetimeDifftest[2].startDate = date[0]; datetimeDifftest[2].endDate = date[2]; datetimeDifftest[2].dateDiff = dateDifference[4]; + datetimeDifftest[3].startDate = date[0]; datetimeDifftest[3].endDate = date[2]; datetimeDifftest[3].dateDiff = dateDifference[3]; + datetimeDifftest[4].startDate = date[14]; datetimeDifftest[4].endDate = date[7]; datetimeDifftest[4].dateDiff = dateDifference[7]; + datetimeDifftest[5].startDate = date[14]; datetimeDifftest[5].endDate = date[7]; datetimeDifftest[5].dateDiff = dateDifference[8]; + datetimeDifftest[6].startDate = date[11]; datetimeDifftest[6].endDate = date[8]; datetimeDifftest[6].dateDiff = dateDifference[9]; + datetimeDifftest[7].startDate = date[13]; datetimeDifftest[7].endDate = date[0]; datetimeDifftest[7].dateDiff = dateDifference[12]; + datetimeDifftest[8].startDate = date[13]; datetimeDifftest[8].endDate = date[0]; datetimeDifftest[8].dateDiff = dateDifference[13]; - // SYSTEMTIME systemTime = DateUtils::DateTimeToSystemTime(endDate); - // if (systemTime.wYear != datetimeAddCase[testIndex].endDate.wYear || - // systemTime.wMonth != datetimeAddCase[testIndex].endDate.wMonth || - // systemTime.wDay != datetimeAddCase[testIndex].endDate.wDay || - // systemTime.wDayOfWeek != datetimeAddCase[testIndex].endDate.wDayOfWeek) - // { - // isValid = false; - // } + // Date Add Out of Bound test cases (Negative tests) + /*OutofBound*/ datetimeBoundAdd[0].startDate = date[1]; datetimeBoundAdd[0].endDate = date[0]; datetimeBoundAdd[0].dateDiff = dateDifference[2]; // on Add date[0] not used + /*OutofBound*/ datetimeBoundAdd[1].startDate = date[2]; datetimeBoundAdd[1].endDate = date[0]; datetimeBoundAdd[1].dateDiff = dateDifference[11]; // on Add date[0] not used - // VERIFY_IS_TRUE(isValid); - //} -} + // Date Subtract Out of Bound test cases (Negative tests) + /*OutofBound*/ datetimeBoundSubtract[0].startDate = date[3]; datetimeBoundSubtract[0].endDate = date[0]; datetimeBoundSubtract[0].dateDiff = dateDifference[2]; // on subtract date[0] not used + /*OutofBound*/ datetimeBoundSubtract[1].startDate = date[14]; datetimeBoundSubtract[1].endDate = date[0]; datetimeBoundSubtract[1].dateDiff = dateDifference[11]; // on subtract date[0] not used -// Subtract Tests -TEST_METHOD(TestSubtraction) -{ - // TODO - MSFT 10331900, fix this test + // Date Add test cases (Positive tests) + datetimeAddCase[0].startDate = date[13]; datetimeAddCase[0].endDate = date[7]; datetimeAddCase[0].dateDiff = dateDifference[6];// add + datetimeAddCase[1].startDate = date[14]; datetimeAddCase[1].endDate = date[5]; datetimeAddCase[1].dateDiff = dateDifference[1];// add + datetimeAddCase[2].startDate = date[13]; datetimeAddCase[2].endDate = date[6]; datetimeAddCase[2].dateDiff = dateDifference[1];// add - // for (int testIndex = 0; testIndex < c_subtractCases; testIndex++) - //{ - // DateTime endDate; + // Date Subtract test cases (Positive tests) + datetimeSubtractCase[0].startDate = date[14]; datetimeSubtractCase[0].endDate = date[7]; datetimeSubtractCase[0].dateDiff = dateDifference[6];// subtract + datetimeSubtractCase[1].startDate = date[6]; datetimeSubtractCase[1].endDate = date[11]; datetimeSubtractCase[1].dateDiff = dateDifference[1];// subtract + datetimeSubtractCase[2].startDate = date[9]; datetimeSubtractCase[2].endDate = date[12]; datetimeSubtractCase[2].dateDiff = dateDifference[1];// subtract + } - // // Subtract Duration - // bool isValid = m_DateCalcEngine.SubtractDuration(DateUtils::SystemTimeToDateTime(datetimeSubtractCase[testIndex].startDate), - // datetimeSubtractCase[testIndex].dateDiff, &endDate); - // // assert for the result - // VERIFY_IS_TRUE(isValid); + /* Duration Between Two Date Tests -- Timediff obtained after calculation should be checked to be identical */ + TEST_METHOD(TestDateDiff) + { + // TODO - MSFT 10331900, fix this test - // SYSTEMTIME systemTime = DateUtils::DateTimeToSystemTime(endDate); - // if (systemTime.wYear != datetimeSubtractCase[testIndex].endDate.wYear || - // systemTime.wMonth != datetimeSubtractCase[testIndex].endDate.wMonth || - // systemTime.wDay != datetimeSubtractCase[testIndex].endDate.wDay || - // systemTime.wDayOfWeek != datetimeSubtractCase[testIndex].endDate.wDayOfWeek) - // { - // isValid = false; - // } + //for (int testIndex = 0; testIndex < c_diffTestCase; testIndex++) + //{ + // DateDifference diff; + // DateUnit dateOutputFormat; - // VERIFY_IS_TRUE(isValid); - //} -} + // switch (testIndex) + // { + // case 0: + // case 2: + // dateOutputFormat = DateUnit::Year | DateUnit::Month | DateUnit::Day; + // break; + // case 1: + // dateOutputFormat = DateUnit::Day; + // break; + // case 3: + // case 8: + // dateOutputFormat = DateUnit::Week | DateUnit::Day; + // break; + // case 7: + // dateOutputFormat = DateUnit::Year | DateUnit::Month | DateUnit::Day; + // break; + // case 4: + // case 6: + // dateOutputFormat = DateUnit::Month | DateUnit::Day; + // break; + // case 5: + // dateOutputFormat = DateUnit::Day; + // break; + // } -private: -} -; + // // Calculate the difference + // m_DateCalcEngine.GetDateDifference(DateUtils::SystemTimeToDateTime(datetimeDifftest[testIndex].startDate), DateUtils::SystemTimeToDateTime(datetimeDifftest[testIndex].endDate), dateOutputFormat, &diff); -TEST_CLASS(DateCalculatorViewModelTests){ public: TEST_CLASS_INITIALIZE(TestClassSetup){ /* Test Case Data */ + // // Assert for the result + // bool areIdentical = true; + // if (diff.year != datetimeDifftest[testIndex].dateDiff.year || + // diff.month != datetimeDifftest[testIndex].dateDiff.month || + // diff.week != datetimeDifftest[testIndex].dateDiff.week || + // diff.day != datetimeDifftest[testIndex].dateDiff.day) + // { + // areIdentical = false; + // } - // Dates - DD.MM.YYYY - /*31.12.9999*/ date[0].wYear = 9999; -date[0].wMonth = 12; -date[0].wDayOfWeek = 5; -date[0].wDay = 31; -date[0].wHour = 0; -date[0].wMinute = 0; -date[0].wSecond = 0; -date[0].wMilliseconds = 0; -/*30.12.9999*/ date[1].wYear = 9999; -date[1].wMonth = 12; -date[1].wDayOfWeek = 4; -date[1].wDay = 30; -date[1].wHour = 0; -date[1].wMinute = 0; -date[1].wSecond = 0; -date[1].wMilliseconds = 0; -/*31.12.9998*/ date[2].wYear = 9998; -date[2].wMonth = 12; -date[2].wDayOfWeek = 4; -date[2].wDay = 31; -date[2].wHour = 0; -date[2].wMinute = 0; -date[2].wSecond = 0; -date[2].wMilliseconds = 0; -/*01.01.1601*/ date[3].wYear = 1601; -date[3].wMonth = 1; -date[3].wDayOfWeek = 1; -date[3].wDay = 1; -date[3].wHour = 0; -date[3].wMinute = 0; -date[3].wSecond = 0; -date[3].wMilliseconds = 0; -/*02.01.1601*/ date[4].wYear = 1601; -date[4].wMonth = 1; -date[4].wDayOfWeek = 2; -date[4].wDay = 2; -date[4].wHour = 0; -date[4].wMinute = 0; -date[4].wSecond = 0; -date[4].wMilliseconds = 0; -/*10.05.2008*/ date[5].wYear = 2008; -date[5].wMonth = 5; -date[5].wDayOfWeek = 6; -date[5].wDay = 10; -date[5].wHour = 0; -date[5].wMinute = 0; -date[5].wSecond = 0; -date[5].wMilliseconds = 0; -/*10.03.2008*/ date[6].wYear = 2008; -date[6].wMonth = 3; -date[6].wDayOfWeek = 1; -date[6].wDay = 10; -date[6].wHour = 0; -date[6].wMinute = 0; -date[6].wSecond = 0; -date[6].wMilliseconds = 0; -/*29.02.2008*/ date[7].wYear = 2008; -date[7].wMonth = 2; -date[7].wDayOfWeek = 5; -date[7].wDay = 29; -date[7].wHour = 0; -date[7].wMinute = 0; -date[7].wSecond = 0; -date[7].wMilliseconds = 0; -/*28.02.2007*/ date[8].wYear = 2007; -date[8].wMonth = 2; -date[8].wDayOfWeek = 3; -date[8].wDay = 28; -date[8].wHour = 0; -date[8].wMinute = 0; -date[8].wSecond = 0; -date[8].wMilliseconds = 0; -/*10.03.2007*/ date[9].wYear = 2007; -date[9].wMonth = 3; -date[9].wDayOfWeek = 6; -date[9].wDay = 10; -date[9].wHour = 0; -date[9].wMinute = 0; -date[9].wSecond = 0; -date[9].wMilliseconds = 0; -/*10.05.2007*/ date[10].wYear = 2007; -date[10].wMonth = 5; -date[10].wDayOfWeek = 4; -date[10].wDay = 10; -date[10].wHour = 0; -date[10].wMinute = 0; -date[10].wSecond = 0; -date[10].wMilliseconds = 0; -/*29.01.2008*/ date[11].wYear = 2008; -date[11].wMonth = 1; -date[11].wDayOfWeek = 2; -date[11].wDay = 29; -date[11].wHour = 0; -date[11].wMinute = 0; -date[11].wSecond = 0; -date[11].wMilliseconds = 0; -/*28.01.2007*/ date[12].wYear = 2007; -date[12].wMonth = 1; -date[12].wDayOfWeek = 0; -date[12].wDay = 28; -date[12].wHour = 0; -date[12].wMinute = 0; -date[12].wSecond = 0; -date[12].wMilliseconds = 0; -/*31.01.2008*/ date[13].wYear = 2008; -date[13].wMonth = 1; -date[13].wDayOfWeek = 4; -date[13].wDay = 31; -date[13].wHour = 0; -date[13].wMinute = 0; -date[13].wSecond = 0; -date[13].wMilliseconds = 0; -/*31.03.2008*/ date[14].wYear = 2008; -date[14].wMonth = 3; -date[14].wDayOfWeek = 1; -date[14].wDay = 31; -date[14].wHour = 0; -date[14].wMinute = 0; -date[14].wSecond = 0; -date[14].wMilliseconds = 0; + // VERIFY_IS_TRUE(areIdentical); + //} + } -// Date Differences -dateDifference[0].year = 1; -dateDifference[0].month = 1; -dateDifference[1].month = 1; -dateDifference[1].day = 10; -dateDifference[2].day = 2; -/*date[2]-[0]*/ dateDifference[3].week = 52; -dateDifference[3].day = 1; -/*date[2]-[0]*/ dateDifference[4].year = 1; -dateDifference[5].day = 365; -dateDifference[6].month = 1; -dateDifference[7].month = 1; -dateDifference[7].day = 2; -dateDifference[8].day = 31; -dateDifference[9].month = 11; -dateDifference[9].day = 1; -dateDifference[10].year = 8398; -dateDifference[10].month = 11; -dateDifference[10].day = 30; -dateDifference[11].year = 2008; -dateDifference[12].year = 7991; -dateDifference[12].month = 11; -dateDifference[13].week = 416998; -dateDifference[13].day = 1; + /*Add Out of bound Tests*/ + TEST_METHOD(TestAddOob) + { + // TODO - MSFT 10331900, fix this test -/* Test Cases */ + //for (int testIndex = 0; testIndex< c_numAddOobDate; testIndex++) + //{ + // DateTime endDate; -// Date Difference test cases -datetimeDifftest[0].startDate = date[0]; -datetimeDifftest[0].endDate = date[3]; -datetimeDifftest[0].dateDiff = dateDifference[10]; -datetimeDifftest[1].startDate = date[0]; -datetimeDifftest[1].endDate = date[2]; -datetimeDifftest[1].dateDiff = dateDifference[5]; -datetimeDifftest[2].startDate = date[0]; -datetimeDifftest[2].endDate = date[2]; -datetimeDifftest[2].dateDiff = dateDifference[4]; -datetimeDifftest[3].startDate = date[0]; -datetimeDifftest[3].endDate = date[2]; -datetimeDifftest[3].dateDiff = dateDifference[3]; -datetimeDifftest[4].startDate = date[14]; -datetimeDifftest[4].endDate = date[7]; -datetimeDifftest[4].dateDiff = dateDifference[7]; -datetimeDifftest[5].startDate = date[14]; -datetimeDifftest[5].endDate = date[7]; -datetimeDifftest[5].dateDiff = dateDifference[8]; -datetimeDifftest[6].startDate = date[11]; -datetimeDifftest[6].endDate = date[8]; -datetimeDifftest[6].dateDiff = dateDifference[9]; -datetimeDifftest[7].startDate = date[13]; -datetimeDifftest[7].endDate = date[0]; -datetimeDifftest[7].dateDiff = dateDifference[12]; -datetimeDifftest[8].startDate = date[13]; -datetimeDifftest[8].endDate = date[0]; -datetimeDifftest[8].dateDiff = dateDifference[13]; + // // Add Duration + // bool isValid = m_DateCalcEngine.AddDuration(DateUtils::SystemTimeToDateTime(datetimeBoundAdd[testIndex].startDate), datetimeBoundAdd[testIndex].dateDiff, &endDate); -// Date Add Out of Bound test cases (Negative tests) -/*OutofBound*/ datetimeBoundAdd[0].startDate = date[1]; -datetimeBoundAdd[0].endDate = date[0]; -datetimeBoundAdd[0].dateDiff = dateDifference[2]; // on Add date[0] not used -/*OutofBound*/ datetimeBoundAdd[1].startDate = date[2]; -datetimeBoundAdd[1].endDate = date[0]; -datetimeBoundAdd[1].dateDiff = dateDifference[11]; // on Add date[0] not used + // // Assert for the result + // VERIFY_IS_FALSE(isValid); + //} + } -// Date Subtract Out of Bound test cases (Negative tests) -/*OutofBound*/ datetimeBoundSubtract[0].startDate = date[3]; -datetimeBoundSubtract[0].endDate = date[0]; -datetimeBoundSubtract[0].dateDiff = dateDifference[2]; // on subtract date[0] not used -/*OutofBound*/ datetimeBoundSubtract[1].startDate = date[14]; -datetimeBoundSubtract[1].endDate = date[0]; -datetimeBoundSubtract[1].dateDiff = dateDifference[11]; // on subtract date[0] not used + /*Subtract Out of bound Tests*/ + TEST_METHOD(TestSubtractOob) + { + for (int testIndex = 0; testIndex< c_numSubtractOobDate; testIndex++) + { + DateTime endDate; -// Date Add test cases (Positive tests) -datetimeAddCase[0].startDate = date[13]; -datetimeAddCase[0].endDate = date[7]; -datetimeAddCase[0].dateDiff = dateDifference[6]; // add -datetimeAddCase[1].startDate = date[14]; -datetimeAddCase[1].endDate = date[5]; -datetimeAddCase[1].dateDiff = dateDifference[1]; // add -datetimeAddCase[2].startDate = date[13]; -datetimeAddCase[2].endDate = date[6]; -datetimeAddCase[2].dateDiff = dateDifference[1]; // add + // Subtract Duration + bool isValid = m_DateCalcEngine.SubtractDuration(DateUtils::SystemTimeToDateTime(datetimeBoundSubtract[testIndex].startDate), datetimeBoundSubtract[testIndex].dateDiff, &endDate); -// Date Subtract test cases (Positive tests) -datetimeSubtractCase[0].startDate = date[14]; -datetimeSubtractCase[0].endDate = date[7]; -datetimeSubtractCase[0].dateDiff = dateDifference[6]; // subtract -datetimeSubtractCase[1].startDate = date[6]; -datetimeSubtractCase[1].endDate = date[11]; -datetimeSubtractCase[1].dateDiff = dateDifference[1]; // subtract -datetimeSubtractCase[2].startDate = date[9]; -datetimeSubtractCase[2].endDate = date[12]; -datetimeSubtractCase[2].dateDiff = dateDifference[1]; // subtract -} + // Assert for the result + VERIFY_IS_FALSE(isValid); + } + } -TEST_METHOD(DateCalcViewModelInitializationTest) -{ - auto viewModel = ref new DateCalculatorViewModel(); + // Add Tests + TEST_METHOD(TestAddition) + { + // TODO - MSFT 10331900, fix this test - // Check for the initialized values - VERIFY_IS_TRUE(viewModel->IsDateDiffMode); - VERIFY_IS_TRUE(viewModel->IsAddMode); + //for (int testIndex = 0; testIndex < c_addCases; testIndex++) + //{ + // DateTime endDate; - VERIFY_IS_TRUE(0 != viewModel->FromDate.UniversalTime); - VERIFY_IS_TRUE(0 != viewModel->ToDate.UniversalTime); - VERIFY_IS_TRUE(0 != viewModel->StartDate.UniversalTime); + // // Add Duration + // bool isValid = m_DateCalcEngine.AddDuration(DateUtils::SystemTimeToDateTime(datetimeAddCase[testIndex].startDate), datetimeAddCase[testIndex].dateDiff, &endDate); - VERIFY_ARE_EQUAL(0, viewModel->DaysOffset); - VERIFY_ARE_EQUAL(0, viewModel->MonthsOffset); - VERIFY_ARE_EQUAL(0, viewModel->YearsOffset); + // // Assert for the result + // VERIFY_IS_TRUE(isValid); - VERIFY_IS_TRUE(viewModel->IsDiffInDays); - VERIFY_ARE_EQUAL(StringReference(L"Same dates"), viewModel->StrDateDiffResult); - VERIFY_IS_NULL(viewModel->StrDateDiffResultInDays); + // SYSTEMTIME systemTime = DateUtils::DateTimeToSystemTime(endDate); + // if (systemTime.wYear != datetimeAddCase[testIndex].endDate.wYear || + // systemTime.wMonth != datetimeAddCase[testIndex].endDate.wMonth || + // systemTime.wDay != datetimeAddCase[testIndex].endDate.wDay || + // systemTime.wDayOfWeek != datetimeAddCase[testIndex].endDate.wDayOfWeek) + // { + // isValid = false; + // } - VERIFY_IS_NULL(viewModel->StrDateResult); -} + // VERIFY_IS_TRUE(isValid); + //} + } -TEST_METHOD(DateCalcViewModelAddSubtractInitTest) -{ - auto viewModel = ref new DateCalculatorViewModel(); - viewModel->IsDateDiffMode = false; + // Subtract Tests + TEST_METHOD(TestSubtraction) + { + // TODO - MSFT 10331900, fix this test - // Check for the initialized values - VERIFY_IS_FALSE(viewModel->IsDateDiffMode); - VERIFY_IS_TRUE(viewModel->IsAddMode); + //for (int testIndex = 0; testIndex < c_subtractCases; testIndex++) + //{ + // DateTime endDate; - VERIFY_IS_TRUE(0 != viewModel->FromDate.UniversalTime); - VERIFY_IS_TRUE(0 != viewModel->ToDate.UniversalTime); - VERIFY_IS_TRUE(0 != viewModel->StartDate.UniversalTime); + // // Subtract Duration + // bool isValid = m_DateCalcEngine.SubtractDuration(DateUtils::SystemTimeToDateTime(datetimeSubtractCase[testIndex].startDate), datetimeSubtractCase[testIndex].dateDiff, &endDate); - VERIFY_ARE_EQUAL(0, viewModel->DaysOffset); - VERIFY_ARE_EQUAL(0, viewModel->MonthsOffset); - VERIFY_ARE_EQUAL(0, viewModel->YearsOffset); + // // assert for the result + // VERIFY_IS_TRUE(isValid); - VERIFY_IS_TRUE(viewModel->IsDiffInDays); - VERIFY_ARE_EQUAL(StringReference(L"Same dates"), viewModel->StrDateDiffResult); - VERIFY_IS_NULL(viewModel->StrDateDiffResultInDays); + // SYSTEMTIME systemTime = DateUtils::DateTimeToSystemTime(endDate); + // if (systemTime.wYear != datetimeSubtractCase[testIndex].endDate.wYear || + // systemTime.wMonth != datetimeSubtractCase[testIndex].endDate.wMonth || + // systemTime.wDay != datetimeSubtractCase[testIndex].endDate.wDay || + // systemTime.wDayOfWeek != datetimeSubtractCase[testIndex].endDate.wDayOfWeek) + // { + // isValid = false; + // } - VERIFY_IS_NOT_NULL(viewModel->StrDateResult); - VERIFY_IS_TRUE(StringReference(L"") != viewModel->StrDateResult); -} + // VERIFY_IS_TRUE(isValid); + //} + } -TEST_METHOD(DateCalcViewModelDateDiffDaylightSavingTimeTest) -{ - auto viewModel = ref new DateCalculatorViewModel(); + private: - viewModel->IsDateDiffMode = true; - VERIFY_IS_TRUE(viewModel->IsDateDiffMode); + }; - // 29.02.2008 - viewModel->FromDate = DateUtils::SystemTimeToDateTime(datetimeDifftest[5].startDate); - // 31.03.2008 - viewModel->ToDate = DateUtils::SystemTimeToDateTime(datetimeDifftest[5].endDate); - - //// Assert for the result - VERIFY_IS_FALSE(viewModel->IsDiffInDays); - VERIFY_ARE_EQUAL(StringReference(L"31 days"), viewModel->StrDateDiffResultInDays); - VERIFY_ARE_EQUAL(StringReference(L"1 month, 2 days"), viewModel->StrDateDiffResult); - - // Daylight Saving Time - Clock Forward - // 10.03.2019 - SYSTEMTIME startDate; - startDate.wYear = 2019; - startDate.wMonth = 03; - startDate.wDay = 10; - startDate.wDayOfWeek = 0; - startDate.wHour = startDate.wMinute = 0; - startDate.wSecond = startDate.wMilliseconds = 0; - viewModel->FromDate = DateUtils::SystemTimeToDateTime(startDate); - // 11.03.2019 - SYSTEMTIME endDate; - endDate.wYear = 2019; - endDate.wMonth = 03; - endDate.wDay = 11; - endDate.wDayOfWeek = 0; - endDate.wHour = endDate.wMinute = 0; - endDate.wSecond = endDate.wMilliseconds = 0; - viewModel->ToDate = DateUtils::SystemTimeToDateTime(endDate); - VERIFY_IS_TRUE(viewModel->IsDiffInDays); - VERIFY_ARE_EQUAL(StringReference(L"1 day"), viewModel->StrDateDiffResult); - - endDate.wDay += 6; - viewModel->ToDate = DateUtils::SystemTimeToDateTime(endDate); - VERIFY_IS_FALSE(viewModel->IsDiffInDays); - VERIFY_ARE_EQUAL(StringReference(L"1 week"), viewModel->StrDateDiffResult); - - // Daylight Saving Time - Clock Backward - // 03.11.2019 - startDate.wMonth = 11; - startDate.wDay = 03; - viewModel->FromDate = DateUtils::SystemTimeToDateTime(startDate); - // 04.11.2019 - endDate.wMonth = 11; - endDate.wDay = 04; - viewModel->ToDate = DateUtils::SystemTimeToDateTime(endDate); - VERIFY_IS_TRUE(viewModel->IsDiffInDays); - VERIFY_ARE_EQUAL(StringReference(L"1 day"), viewModel->StrDateDiffResult); -} - -TEST_METHOD(DateCalcViewModelAddTest) -{ - // TODO - MSFT 10331900, fix this test - // A few issues to be investigated.. - // The date returned by DateUtils::GetLongDate can be a different string than expected - // based on the values of date[7]. This is because date[7] is in UTC but GetLongDate - // doesn't format according to UTC. If it did, the test would still be incorrect because - // the ViewModel is not necessarily in UTC. - // - // The DateTime value assigned to StartDate after the conversion SystemTimeToDateTime is not - // the same DateTime value as if the user were to select the same date from the CalendarDatePicker. - // This means testing a specific date here, is *not* the same as selecting that date in the app. - - // auto viewModel = ref new DateCalculatorViewModel(); - // viewModel->Initialize(); - - // viewModel->IsDateDiffMode = false; - // viewModel->IsAddMode = true; - // VERIFY_IS_FALSE(viewModel->IsDateDiffMode); - // VERIFY_IS_TRUE(viewModel->IsAddMode); - - // viewModel->StartDate = DateUtils::SystemTimeToDateTime(datetimeAddCase[0].startDate); - // viewModel->DaysOffset = datetimeAddCase[0].dateDiff.day; - // viewModel->MonthsOffset = datetimeAddCase[0].dateDiff.month; - // viewModel->YearsOffset = datetimeAddCase[0].dateDiff.year; - - //// Assert for the result - // VERIFY_ARE_EQUAL(DateUtils::GetLongDate(date[7]), viewModel->StrDateResult); -} - -TEST_METHOD(DateCalcViewModelSubtractTest) -{ - // TODO - MSFT 10331900, fix this test - // A few issues to be investigated.. - // The date returned by DateUtils::GetLongDate can be a different string than expected - // based on the values of date[7]. This is because date[7] is in UTC but GetLongDate - // doesn't format according to UTC. If it did, the test would still be incorrect because - // the ViewModel is not necessarily in UTC. - // - // The DateTime value assigned to StartDate after the conversion SystemTimeToDateTime is not - // the same DateTime value as if the user were to select the same date from the CalendarDatePicker. - // This means testing a specific date here, is *not* the same as selecting that date in the app. - - // auto viewModel = ref new DateCalculatorViewModel(); - // viewModel->Initialize(); - - // viewModel->IsDateDiffMode = false; - // viewModel->IsAddMode = false; - // VERIFY_IS_FALSE(viewModel->IsDateDiffMode); - // VERIFY_IS_FALSE(viewModel->IsAddMode); - - // viewModel->StartDate = DateUtils::SystemTimeToDateTime(datetimeSubtractCase[0].startDate); - // viewModel->DaysOffset = datetimeSubtractCase[0].dateDiff.day; - // viewModel->MonthsOffset = datetimeSubtractCase[0].dateDiff.month; - // viewModel->YearsOffset = datetimeSubtractCase[0].dateDiff.year; - - //// Assert for the result - // VERIFY_ARE_EQUAL(DateUtils::GetLongDate(date[7]), viewModel->StrDateResult); -} - -TEST_METHOD(DateCalcViewModelAddOobTest) -{ - // TODO - MSFT 10331900, fix this test - // Curiously enough, this test fails because it fails to go Oob. - // Possibly need to update test to use a new max date. - - // auto viewModel = ref new DateCalculatorViewModel(); - // viewModel->Initialize(); - - // viewModel->IsDateDiffMode = false; - // viewModel->IsAddMode = true; - // VERIFY_IS_FALSE(viewModel->IsDateDiffMode); - // VERIFY_IS_TRUE(viewModel->IsAddMode); - - // for (int testIndex = 0; testIndex< c_numAddOobDate; testIndex++) - //{ - // viewModel->StartDate = DateUtils::SystemTimeToDateTime(datetimeBoundAdd[testIndex].startDate); - // viewModel->DaysOffset = datetimeBoundAdd[testIndex].dateDiff.day; - // viewModel->MonthsOffset = datetimeBoundAdd[testIndex].dateDiff.month; - // viewModel->YearsOffset = datetimeBoundAdd[testIndex].dateDiff.year; - - // // Assert for the result - // VERIFY_ARE_EQUAL(StringReference(L"Date out of Bound"), viewModel->StrDateResult); - //} -} - -TEST_METHOD(DateCalcViewModelSubtractOobTest) -{ - auto viewModel = ref new DateCalculatorViewModel(); - - viewModel->IsDateDiffMode = false; - viewModel->IsAddMode = false; - VERIFY_IS_FALSE(viewModel->IsDateDiffMode); - VERIFY_IS_FALSE(viewModel->IsAddMode); - - for (int testIndex = 0; testIndex < c_numSubtractOobDate; testIndex++) + TEST_CLASS(DateCalculatorViewModelTests) { - viewModel->StartDate = DateUtils::SystemTimeToDateTime(datetimeBoundSubtract[testIndex].startDate); - viewModel->DaysOffset = datetimeBoundSubtract[testIndex].dateDiff.day; - viewModel->MonthsOffset = datetimeBoundSubtract[testIndex].dateDiff.month; - viewModel->YearsOffset = datetimeBoundSubtract[testIndex].dateDiff.year; + public: + TEST_CLASS_INITIALIZE(TestClassSetup) + { + /* Test Case Data */ - // Assert for the result - VERIFY_ARE_EQUAL(StringReference(L"Date out of Bound"), viewModel->StrDateResult); - } -} - -TEST_METHOD(DateCalcViewModelDateDiffIgnoreSignTest) -{ - auto viewModel = ref new DateCalculatorViewModel(); - - viewModel->IsDateDiffMode = true; - VERIFY_IS_TRUE(viewModel->IsDateDiffMode); - - viewModel->FromDate = DateUtils::SystemTimeToDateTime(date[10]); - viewModel->ToDate = DateUtils::SystemTimeToDateTime(date[6]); - - VERIFY_IS_FALSE(viewModel->IsDiffInDays); - VERIFY_ARE_EQUAL(StringReference(L"305 days"), viewModel->StrDateDiffResultInDays); - VERIFY_ARE_EQUAL(StringReference(L"10 months"), viewModel->StrDateDiffResult); - viewModel->FromDate = DateUtils::SystemTimeToDateTime(date[6]); - viewModel->ToDate = DateUtils::SystemTimeToDateTime(date[10]); - VERIFY_IS_FALSE(viewModel->IsDiffInDays); - VERIFY_ARE_EQUAL(StringReference(L"305 days"), viewModel->StrDateDiffResultInDays); - VERIFY_ARE_EQUAL(StringReference(L"10 months"), viewModel->StrDateDiffResult); -} - -TEST_METHOD(DateCalcViewModelDateDiffTest) -{ - // TODO - MSFT 10331900, fix this test - // The last VERIFY checks with expected value "8398 years, 11 months, 4 weeks, 2 days" - // The viewmodel result is something like "8398 years, 12 months, 6568892 weeks, 1 day", - // which shows there is a problem with the viewmodel's reduction algorithm. - - // auto viewModel = ref new DateCalculatorViewModel(); - // viewModel->Initialize(); - - // viewModel->IsDateDiffMode = true; - // VERIFY_IS_TRUE(viewModel->IsDateDiffMode); - - // viewModel->FromDate = DateUtils::SystemTimeToDateTime(datetimeDifftest[0].startDate); - // viewModel->ToDate = DateUtils::SystemTimeToDateTime(datetimeDifftest[0].endDate); - - //// Assert for the result - // VERIFY_IS_FALSE(viewModel->IsDiffInDays); - // VERIFY_ARE_EQUAL(StringReference(L"3067670 days"), viewModel->StrDateDiffResultInDays); - // VERIFY_ARE_EQUAL(StringReference(L"8398 years, 11 months, 4 weeks, 2 days"), viewModel->StrDateDiffResult); -} - -TEST_METHOD(DateCalcViewModelDateDiffResultInPositiveDaysTest) -{ - auto viewModel = ref new DateCalculatorViewModel(); - - viewModel->IsDateDiffMode = true; - VERIFY_IS_TRUE(viewModel->IsDateDiffMode); - - viewModel->FromDate = DateUtils::SystemTimeToDateTime(date[1]); - viewModel->ToDate = DateUtils::SystemTimeToDateTime(date[0]); - - // Assert for the result - VERIFY_IS_TRUE(viewModel->IsDiffInDays); - VERIFY_ARE_EQUAL(StringReference(L"1 day"), viewModel->StrDateDiffResult); - VERIFY_IS_NULL(viewModel->StrDateDiffResultInDays); -} - -TEST_METHOD(DateCalcViewModelDateDiffFromDateHigherThanToDate) -{ - auto viewModel = ref new DateCalculatorViewModel(); - - viewModel->IsDateDiffMode = true; - VERIFY_IS_TRUE(viewModel->IsDateDiffMode); - - viewModel->FromDate = DateUtils::SystemTimeToDateTime(date[0]); - viewModel->ToDate = DateUtils::SystemTimeToDateTime(date[1]); - - // Assert for the result - VERIFY_IS_TRUE(viewModel->IsDiffInDays); - VERIFY_ARE_EQUAL(StringReference(L"1 day"), viewModel->StrDateDiffResult); - VERIFY_IS_NULL(viewModel->StrDateDiffResultInDays); -} - -// Tests that the automation name for the resulting date in Add Mode -// contains the DayOfWeek, Day, Month, and Year -TEST_METHOD(DateCalcViewModelAddSubtractResultAutomationNameTest) -{ - auto viewModel = ref new DateCalculatorViewModel(); - - auto cal = ref new Calendar(); - cal->Year = 2007; - cal->Month = 5; - cal->Day = 10; - cal->Hour = 12; - cal->Period = 2; - cal->Nanosecond = 0; - cal->Second = 0; - - DateTime startDate = cal->GetDateTime(); - viewModel->StartDate = startDate; - - viewModel->IsDateDiffMode = false; - viewModel->IsAddMode = true; - - wstring actualValue = viewModel->StrDateResultAutomationName->Data(); - - // Verify each component is present in the result - wstring components[] = { L"dayofweek.full", L"month.full", L"year.full", L"day" }; - - for (const wstring& component : components) - { - auto formatter = ref new DateTimeFormatter(ref new String(component.c_str())); - wstring expectedValue = formatter->Format(startDate)->Data(); - wstring message = L"Verifying " + component + L" is present in the result"; - VERIFY_IS_TRUE(actualValue.find(expectedValue) != wstring::npos, message.c_str()); - } -} -} -; + // Dates - DD.MM.YYYY + /*31.12.9999*/ date[0].wYear = 9999; date[0].wMonth = 12; date[0].wDayOfWeek = 5; date[0].wDay = 31; date[0].wHour = 0; date[0].wMinute = 0; date[0].wSecond = 0; date[0].wMilliseconds = 0; + /*30.12.9999*/ date[1].wYear = 9999; date[1].wMonth = 12; date[1].wDayOfWeek = 4; date[1].wDay = 30; date[1].wHour = 0; date[1].wMinute = 0; date[1].wSecond = 0; date[1].wMilliseconds = 0; + /*31.12.9998*/ date[2].wYear = 9998; date[2].wMonth = 12; date[2].wDayOfWeek = 4; date[2].wDay = 31; date[2].wHour = 0; date[2].wMinute = 0; date[2].wSecond = 0; date[2].wMilliseconds = 0; + /*01.01.1601*/ date[3].wYear = 1601; date[3].wMonth = 1; date[3].wDayOfWeek = 1; date[3].wDay = 1; date[3].wHour = 0; date[3].wMinute = 0; date[3].wSecond = 0; date[3].wMilliseconds = 0; + /*02.01.1601*/ date[4].wYear = 1601; date[4].wMonth = 1; date[4].wDayOfWeek = 2; date[4].wDay = 2; date[4].wHour = 0; date[4].wMinute = 0; date[4].wSecond = 0; date[4].wMilliseconds = 0; + /*10.05.2008*/ date[5].wYear = 2008; date[5].wMonth = 5; date[5].wDayOfWeek = 6; date[5].wDay = 10; date[5].wHour = 0; date[5].wMinute = 0; date[5].wSecond = 0; date[5].wMilliseconds = 0; + /*10.03.2008*/ date[6].wYear = 2008; date[6].wMonth = 3; date[6].wDayOfWeek = 1; date[6].wDay = 10; date[6].wHour = 0; date[6].wMinute = 0; date[6].wSecond = 0; date[6].wMilliseconds = 0; + /*29.02.2008*/ date[7].wYear = 2008; date[7].wMonth = 2; date[7].wDayOfWeek = 5; date[7].wDay = 29; date[7].wHour = 0; date[7].wMinute = 0; date[7].wSecond = 0; date[7].wMilliseconds = 0; + /*28.02.2007*/ date[8].wYear = 2007; date[8].wMonth = 2; date[8].wDayOfWeek = 3; date[8].wDay = 28; date[8].wHour = 0; date[8].wMinute = 0; date[8].wSecond = 0; date[8].wMilliseconds = 0; + /*10.03.2007*/ date[9].wYear = 2007; date[9].wMonth = 3; date[9].wDayOfWeek = 6; date[9].wDay = 10; date[9].wHour = 0; date[9].wMinute = 0; date[9].wSecond = 0; date[9].wMilliseconds = 0; + /*10.05.2007*/ date[10].wYear = 2007; date[10].wMonth = 5; date[10].wDayOfWeek = 4; date[10].wDay = 10; date[10].wHour = 0; date[10].wMinute = 0; date[10].wSecond = 0; date[10].wMilliseconds = 0; + /*29.01.2008*/ date[11].wYear = 2008; date[11].wMonth = 1; date[11].wDayOfWeek = 2; date[11].wDay = 29; date[11].wHour = 0; date[11].wMinute = 0; date[11].wSecond = 0; date[11].wMilliseconds = 0; + /*28.01.2007*/ date[12].wYear = 2007; date[12].wMonth = 1; date[12].wDayOfWeek = 0; date[12].wDay = 28; date[12].wHour = 0; date[12].wMinute = 0; date[12].wSecond = 0; date[12].wMilliseconds = 0; + /*31.01.2008*/ date[13].wYear = 2008; date[13].wMonth = 1; date[13].wDayOfWeek = 4; date[13].wDay = 31; date[13].wHour = 0; date[13].wMinute = 0; date[13].wSecond = 0; date[13].wMilliseconds = 0; + /*31.03.2008*/ date[14].wYear = 2008; date[14].wMonth = 3; date[14].wDayOfWeek = 1; date[14].wDay = 31; date[14].wHour = 0; date[14].wMinute = 0; date[14].wSecond = 0; date[14].wMilliseconds = 0; + + // Date Differences + dateDifference[0].year = 1; dateDifference[0].month = 1; + dateDifference[1].month = 1; dateDifference[1].day = 10; + dateDifference[2].day = 2; + /*date[2]-[0]*/ dateDifference[3].week = 52; dateDifference[3].day = 1; + /*date[2]-[0]*/ dateDifference[4].year = 1; + dateDifference[5].day = 365; + dateDifference[6].month = 1; + dateDifference[7].month = 1; dateDifference[7].day = 2; + dateDifference[8].day = 31; + dateDifference[9].month = 11; dateDifference[9].day = 1; + dateDifference[10].year = 8398; dateDifference[10].month = 11; dateDifference[10].day = 30; + dateDifference[11].year = 2008; + dateDifference[12].year = 7991; dateDifference[12].month = 11; + dateDifference[13].week = 416998; dateDifference[13].day = 1; + + + + /* Test Cases */ + + // Date Difference test cases + datetimeDifftest[0].startDate = date[0]; datetimeDifftest[0].endDate = date[3]; datetimeDifftest[0].dateDiff = dateDifference[10]; + datetimeDifftest[1].startDate = date[0]; datetimeDifftest[1].endDate = date[2]; datetimeDifftest[1].dateDiff = dateDifference[5]; + datetimeDifftest[2].startDate = date[0]; datetimeDifftest[2].endDate = date[2]; datetimeDifftest[2].dateDiff = dateDifference[4]; + datetimeDifftest[3].startDate = date[0]; datetimeDifftest[3].endDate = date[2]; datetimeDifftest[3].dateDiff = dateDifference[3]; + datetimeDifftest[4].startDate = date[14]; datetimeDifftest[4].endDate = date[7]; datetimeDifftest[4].dateDiff = dateDifference[7]; + datetimeDifftest[5].startDate = date[14]; datetimeDifftest[5].endDate = date[7]; datetimeDifftest[5].dateDiff = dateDifference[8]; + datetimeDifftest[6].startDate = date[11]; datetimeDifftest[6].endDate = date[8]; datetimeDifftest[6].dateDiff = dateDifference[9]; + datetimeDifftest[7].startDate = date[13]; datetimeDifftest[7].endDate = date[0]; datetimeDifftest[7].dateDiff = dateDifference[12]; + datetimeDifftest[8].startDate = date[13]; datetimeDifftest[8].endDate = date[0]; datetimeDifftest[8].dateDiff = dateDifference[13]; + + // Date Add Out of Bound test cases (Negative tests) + /*OutofBound*/ datetimeBoundAdd[0].startDate = date[1]; datetimeBoundAdd[0].endDate = date[0]; datetimeBoundAdd[0].dateDiff = dateDifference[2]; // on Add date[0] not used + /*OutofBound*/ datetimeBoundAdd[1].startDate = date[2]; datetimeBoundAdd[1].endDate = date[0]; datetimeBoundAdd[1].dateDiff = dateDifference[11]; // on Add date[0] not used + + // Date Subtract Out of Bound test cases (Negative tests) + /*OutofBound*/ datetimeBoundSubtract[0].startDate = date[3]; datetimeBoundSubtract[0].endDate = date[0]; datetimeBoundSubtract[0].dateDiff = dateDifference[2]; // on subtract date[0] not used + /*OutofBound*/ datetimeBoundSubtract[1].startDate = date[14]; datetimeBoundSubtract[1].endDate = date[0]; datetimeBoundSubtract[1].dateDiff = dateDifference[11]; // on subtract date[0] not used + + // Date Add test cases (Positive tests) + datetimeAddCase[0].startDate = date[13]; datetimeAddCase[0].endDate = date[7]; datetimeAddCase[0].dateDiff = dateDifference[6];// add + datetimeAddCase[1].startDate = date[14]; datetimeAddCase[1].endDate = date[5]; datetimeAddCase[1].dateDiff = dateDifference[1];// add + datetimeAddCase[2].startDate = date[13]; datetimeAddCase[2].endDate = date[6]; datetimeAddCase[2].dateDiff = dateDifference[1];// add + + // Date Subtract test cases (Positive tests) + datetimeSubtractCase[0].startDate = date[14]; datetimeSubtractCase[0].endDate = date[7]; datetimeSubtractCase[0].dateDiff = dateDifference[6];// subtract + datetimeSubtractCase[1].startDate = date[6]; datetimeSubtractCase[1].endDate = date[11]; datetimeSubtractCase[1].dateDiff = dateDifference[1];// subtract + datetimeSubtractCase[2].startDate = date[9]; datetimeSubtractCase[2].endDate = date[12]; datetimeSubtractCase[2].dateDiff = dateDifference[1];// subtract + } + + TEST_METHOD(DateCalcViewModelInitializationTest) + { + auto viewModel = ref new DateCalculatorViewModel(); + + // Check for the initialized values + VERIFY_IS_TRUE(viewModel->IsDateDiffMode); + VERIFY_IS_TRUE(viewModel->IsAddMode); + + VERIFY_IS_TRUE(0 != viewModel->FromDate.UniversalTime); + VERIFY_IS_TRUE(0 != viewModel->ToDate.UniversalTime); + VERIFY_IS_TRUE(0 != viewModel->StartDate.UniversalTime); + + VERIFY_ARE_EQUAL(0, viewModel->DaysOffset); + VERIFY_ARE_EQUAL(0, viewModel->MonthsOffset); + VERIFY_ARE_EQUAL(0, viewModel->YearsOffset); + + VERIFY_IS_TRUE(viewModel->IsDiffInDays); + VERIFY_ARE_EQUAL(StringReference(L"Same dates"), viewModel->StrDateDiffResult); + VERIFY_IS_NULL(viewModel->StrDateDiffResultInDays); + + VERIFY_IS_NULL(viewModel->StrDateResult); + } + + TEST_METHOD(DateCalcViewModelAddSubtractInitTest) + { + auto viewModel = ref new DateCalculatorViewModel(); + viewModel->IsDateDiffMode = false; + + // Check for the initialized values + VERIFY_IS_FALSE(viewModel->IsDateDiffMode); + VERIFY_IS_TRUE(viewModel->IsAddMode); + + VERIFY_IS_TRUE(0 != viewModel->FromDate.UniversalTime); + VERIFY_IS_TRUE(0 != viewModel->ToDate.UniversalTime); + VERIFY_IS_TRUE(0 != viewModel->StartDate.UniversalTime); + + VERIFY_ARE_EQUAL(0, viewModel->DaysOffset); + VERIFY_ARE_EQUAL(0, viewModel->MonthsOffset); + VERIFY_ARE_EQUAL(0, viewModel->YearsOffset); + + VERIFY_IS_TRUE(viewModel->IsDiffInDays); + VERIFY_ARE_EQUAL(StringReference(L"Same dates"), viewModel->StrDateDiffResult); + VERIFY_IS_NULL(viewModel->StrDateDiffResultInDays); + + VERIFY_IS_NOT_NULL(viewModel->StrDateResult); + VERIFY_IS_TRUE(StringReference(L"") != viewModel->StrDateResult); + } + + TEST_METHOD(DateCalcViewModelDateDiffDaylightSavingTimeTest) + { + auto viewModel = ref new DateCalculatorViewModel(); + + viewModel->IsDateDiffMode = true; + VERIFY_IS_TRUE(viewModel->IsDateDiffMode); + + // 29.02.2008 + viewModel->FromDate = DateUtils::SystemTimeToDateTime(datetimeDifftest[5].startDate); + // 31.03.2008 + viewModel->ToDate = DateUtils::SystemTimeToDateTime(datetimeDifftest[5].endDate); + + //// Assert for the result + VERIFY_IS_FALSE(viewModel->IsDiffInDays); + VERIFY_ARE_EQUAL(StringReference(L"31 days"), viewModel->StrDateDiffResultInDays); + VERIFY_ARE_EQUAL(StringReference(L"1 month, 2 days"), viewModel->StrDateDiffResult); + + // Daylight Saving Time - Clock Forward + // 10.03.2019 + SYSTEMTIME startDate; + startDate.wYear = 2019; + startDate.wMonth = 03; + startDate.wDay = 10; + startDate.wDayOfWeek = 0; + startDate.wHour = startDate.wMinute = 0; + startDate.wSecond = startDate.wMilliseconds = 0; + viewModel->FromDate = DateUtils::SystemTimeToDateTime(startDate); + // 11.03.2019 + SYSTEMTIME endDate; + endDate.wYear = 2019; + endDate.wMonth = 03; + endDate.wDay = 11; + endDate.wDayOfWeek = 0; + endDate.wHour = endDate.wMinute = 0; + endDate.wSecond = endDate.wMilliseconds = 0; + viewModel->ToDate = DateUtils::SystemTimeToDateTime(endDate); + VERIFY_IS_TRUE(viewModel->IsDiffInDays); + VERIFY_ARE_EQUAL(StringReference(L"1 day"), viewModel->StrDateDiffResult); + + endDate.wDay += 6; + viewModel->ToDate = DateUtils::SystemTimeToDateTime(endDate); + VERIFY_IS_FALSE(viewModel->IsDiffInDays); + VERIFY_ARE_EQUAL(StringReference(L"1 week"), viewModel->StrDateDiffResult); + + // Daylight Saving Time - Clock Backward + // 03.11.2019 + startDate.wMonth = 11; + startDate.wDay = 03; + viewModel->FromDate = DateUtils::SystemTimeToDateTime(startDate); + // 04.11.2019 + endDate.wMonth = 11; + endDate.wDay = 04; + viewModel->ToDate = DateUtils::SystemTimeToDateTime(endDate); + VERIFY_IS_TRUE(viewModel->IsDiffInDays); + VERIFY_ARE_EQUAL(StringReference(L"1 day"), viewModel->StrDateDiffResult); + } + + TEST_METHOD(DateCalcViewModelAddTest) + { + // TODO - MSFT 10331900, fix this test + // A few issues to be investigated.. + // The date returned by DateUtils::GetLongDate can be a different string than expected + // based on the values of date[7]. This is because date[7] is in UTC but GetLongDate + // doesn't format according to UTC. If it did, the test would still be incorrect because + // the ViewModel is not necessarily in UTC. + // + // The DateTime value assigned to StartDate after the conversion SystemTimeToDateTime is not + // the same DateTime value as if the user were to select the same date from the CalendarDatePicker. + // This means testing a specific date here, is *not* the same as selecting that date in the app. + + //auto viewModel = ref new DateCalculatorViewModel(); + //viewModel->Initialize(); + + //viewModel->IsDateDiffMode = false; + //viewModel->IsAddMode = true; + //VERIFY_IS_FALSE(viewModel->IsDateDiffMode); + //VERIFY_IS_TRUE(viewModel->IsAddMode); + + //viewModel->StartDate = DateUtils::SystemTimeToDateTime(datetimeAddCase[0].startDate); + //viewModel->DaysOffset = datetimeAddCase[0].dateDiff.day; + //viewModel->MonthsOffset = datetimeAddCase[0].dateDiff.month; + //viewModel->YearsOffset = datetimeAddCase[0].dateDiff.year; + + //// Assert for the result + //VERIFY_ARE_EQUAL(DateUtils::GetLongDate(date[7]), viewModel->StrDateResult); + } + + TEST_METHOD(DateCalcViewModelSubtractTest) + { + // TODO - MSFT 10331900, fix this test + // A few issues to be investigated.. + // The date returned by DateUtils::GetLongDate can be a different string than expected + // based on the values of date[7]. This is because date[7] is in UTC but GetLongDate + // doesn't format according to UTC. If it did, the test would still be incorrect because + // the ViewModel is not necessarily in UTC. + // + // The DateTime value assigned to StartDate after the conversion SystemTimeToDateTime is not + // the same DateTime value as if the user were to select the same date from the CalendarDatePicker. + // This means testing a specific date here, is *not* the same as selecting that date in the app. + + //auto viewModel = ref new DateCalculatorViewModel(); + //viewModel->Initialize(); + + //viewModel->IsDateDiffMode = false; + //viewModel->IsAddMode = false; + //VERIFY_IS_FALSE(viewModel->IsDateDiffMode); + //VERIFY_IS_FALSE(viewModel->IsAddMode); + + //viewModel->StartDate = DateUtils::SystemTimeToDateTime(datetimeSubtractCase[0].startDate); + //viewModel->DaysOffset = datetimeSubtractCase[0].dateDiff.day; + //viewModel->MonthsOffset = datetimeSubtractCase[0].dateDiff.month; + //viewModel->YearsOffset = datetimeSubtractCase[0].dateDiff.year; + + //// Assert for the result + //VERIFY_ARE_EQUAL(DateUtils::GetLongDate(date[7]), viewModel->StrDateResult); + } + + TEST_METHOD(DateCalcViewModelAddOobTest) + { + // TODO - MSFT 10331900, fix this test + // Curiously enough, this test fails because it fails to go Oob. + // Possibly need to update test to use a new max date. + + //auto viewModel = ref new DateCalculatorViewModel(); + //viewModel->Initialize(); + + //viewModel->IsDateDiffMode = false; + //viewModel->IsAddMode = true; + //VERIFY_IS_FALSE(viewModel->IsDateDiffMode); + //VERIFY_IS_TRUE(viewModel->IsAddMode); + + //for (int testIndex = 0; testIndex< c_numAddOobDate; testIndex++) + //{ + // viewModel->StartDate = DateUtils::SystemTimeToDateTime(datetimeBoundAdd[testIndex].startDate); + // viewModel->DaysOffset = datetimeBoundAdd[testIndex].dateDiff.day; + // viewModel->MonthsOffset = datetimeBoundAdd[testIndex].dateDiff.month; + // viewModel->YearsOffset = datetimeBoundAdd[testIndex].dateDiff.year; + + // // Assert for the result + // VERIFY_ARE_EQUAL(StringReference(L"Date out of Bound"), viewModel->StrDateResult); + //} + } + + TEST_METHOD(DateCalcViewModelSubtractOobTest) + { + auto viewModel = ref new DateCalculatorViewModel(); + + viewModel->IsDateDiffMode = false; + viewModel->IsAddMode = false; + VERIFY_IS_FALSE(viewModel->IsDateDiffMode); + VERIFY_IS_FALSE(viewModel->IsAddMode); + + for (int testIndex = 0; testIndex < c_numSubtractOobDate; testIndex++) + { + viewModel->StartDate = DateUtils::SystemTimeToDateTime(datetimeBoundSubtract[testIndex].startDate); + viewModel->DaysOffset = datetimeBoundSubtract[testIndex].dateDiff.day; + viewModel->MonthsOffset = datetimeBoundSubtract[testIndex].dateDiff.month; + viewModel->YearsOffset = datetimeBoundSubtract[testIndex].dateDiff.year; + + // Assert for the result + VERIFY_ARE_EQUAL(StringReference(L"Date out of Bound"), viewModel->StrDateResult); + } + } + + TEST_METHOD(DateCalcViewModelDateDiffIgnoreSignTest) + { + auto viewModel = ref new DateCalculatorViewModel(); + + viewModel->IsDateDiffMode = true; + VERIFY_IS_TRUE(viewModel->IsDateDiffMode); + + viewModel->FromDate = DateUtils::SystemTimeToDateTime(date[10]); + viewModel->ToDate = DateUtils::SystemTimeToDateTime(date[6]); + + VERIFY_IS_FALSE(viewModel->IsDiffInDays); + VERIFY_ARE_EQUAL(StringReference(L"305 days"), viewModel->StrDateDiffResultInDays); + VERIFY_ARE_EQUAL(StringReference(L"10 months"), viewModel->StrDateDiffResult); + viewModel->FromDate = DateUtils::SystemTimeToDateTime(date[6]); + viewModel->ToDate = DateUtils::SystemTimeToDateTime(date[10]); + VERIFY_IS_FALSE(viewModel->IsDiffInDays); + VERIFY_ARE_EQUAL(StringReference(L"305 days"), viewModel->StrDateDiffResultInDays); + VERIFY_ARE_EQUAL(StringReference(L"10 months"), viewModel->StrDateDiffResult); + } + + TEST_METHOD(DateCalcViewModelDateDiffTest) + { + // TODO - MSFT 10331900, fix this test + // The last VERIFY checks with expected value "8398 years, 11 months, 4 weeks, 2 days" + // The viewmodel result is something like "8398 years, 12 months, 6568892 weeks, 1 day", + // which shows there is a problem with the viewmodel's reduction algorithm. + + //auto viewModel = ref new DateCalculatorViewModel(); + //viewModel->Initialize(); + + //viewModel->IsDateDiffMode = true; + //VERIFY_IS_TRUE(viewModel->IsDateDiffMode); + + //viewModel->FromDate = DateUtils::SystemTimeToDateTime(datetimeDifftest[0].startDate); + //viewModel->ToDate = DateUtils::SystemTimeToDateTime(datetimeDifftest[0].endDate); + + //// Assert for the result + //VERIFY_IS_FALSE(viewModel->IsDiffInDays); + //VERIFY_ARE_EQUAL(StringReference(L"3067670 days"), viewModel->StrDateDiffResultInDays); + //VERIFY_ARE_EQUAL(StringReference(L"8398 years, 11 months, 4 weeks, 2 days"), viewModel->StrDateDiffResult); + } + + TEST_METHOD(DateCalcViewModelDateDiffResultInPositiveDaysTest) + { + auto viewModel = ref new DateCalculatorViewModel(); + + viewModel->IsDateDiffMode = true; + VERIFY_IS_TRUE(viewModel->IsDateDiffMode); + + viewModel->FromDate = DateUtils::SystemTimeToDateTime(date[1]); + viewModel->ToDate = DateUtils::SystemTimeToDateTime(date[0]); + + // Assert for the result + VERIFY_IS_TRUE(viewModel->IsDiffInDays); + VERIFY_ARE_EQUAL(StringReference(L"1 day"), viewModel->StrDateDiffResult); + VERIFY_IS_NULL(viewModel->StrDateDiffResultInDays); + } + + TEST_METHOD(DateCalcViewModelDateDiffFromDateHigherThanToDate) + { + auto viewModel = ref new DateCalculatorViewModel(); + + viewModel->IsDateDiffMode = true; + VERIFY_IS_TRUE(viewModel->IsDateDiffMode); + + viewModel->FromDate = DateUtils::SystemTimeToDateTime(date[0]); + viewModel->ToDate = DateUtils::SystemTimeToDateTime(date[1]); + + // Assert for the result + VERIFY_IS_TRUE(viewModel->IsDiffInDays); + VERIFY_ARE_EQUAL(StringReference(L"1 day"), viewModel->StrDateDiffResult); + VERIFY_IS_NULL(viewModel->StrDateDiffResultInDays); + } + + // Tests that the automation name for the resulting date in Add Mode + // contains the DayOfWeek, Day, Month, and Year + TEST_METHOD(DateCalcViewModelAddSubtractResultAutomationNameTest) + { + auto viewModel = ref new DateCalculatorViewModel(); + + auto cal = ref new Calendar(); + cal->Year = 2007; + cal->Month = 5; + cal->Day = 10; + cal->Hour = 12; + cal->Period = 2; + cal->Nanosecond = 0; + cal->Second = 0; + + DateTime startDate = cal->GetDateTime(); + viewModel->StartDate = startDate; + + viewModel->IsDateDiffMode = false; + viewModel->IsAddMode = true; + + wstring actualValue = viewModel->StrDateResultAutomationName->Data(); + + // Verify each component is present in the result + wstring components[] = { + L"dayofweek.full", + L"month.full", + L"year.full", + L"day" + }; + + for (const wstring &component : components) + { + auto formatter = ref new DateTimeFormatter(ref new String(component.c_str())); + wstring expectedValue = formatter->Format(startDate)->Data(); + wstring message = L"Verifying " + component + L" is present in the result"; + VERIFY_IS_TRUE(actualValue.find(expectedValue) != wstring::npos, message.c_str()); + } + } + + TEST_METHOD(JaEraTransitionAddition) + { + auto viewModel = make_unique(CalendarIdentifiers::Japanese); + auto cal = ref new Calendar(); + + // Showa period ended in Jan 1989. + cal->Year = 1989; + cal->Month = 1; + cal->Day = 1; + auto startTime = cal->GetDateTime(); + + cal->Year = 1990; + cal->Month = 1; + cal->Day = 1; + + // Expect that adding a year across boundaries adds the equivalent in the Gregorian calendar. + auto expectedResult = cal->GetDateTime(); + DateDifference duration; + duration.year = 1; + + DateTime actualResult; + viewModel->AddDuration(startTime, duration, &actualResult); + + VERIFY_ARE_EQUAL(expectedResult.UniversalTime, actualResult.UniversalTime); + } + + TEST_METHOD(JaEraTransitionSubtraction) + { + auto viewModel = make_unique(CalendarIdentifiers::Japanese); + auto cal = ref new Calendar(); + + // Showa period ended in Jan 1989. + cal->Year = 1990; + cal->Month = 1; + cal->Day = 1; + auto startTime = cal->GetDateTime(); + + cal->Year = 1989; + cal->Month = 1; + cal->Day = 1; + + // Expect that adding a year across boundaries adds the equivalent in the Gregorian calendar. + auto expectedResult = cal->GetDateTime(); + DateDifference duration; + duration.year = 1; + + DateTime actualResult; + viewModel->SubtractDuration(startTime, duration, &actualResult); + + VERIFY_ARE_EQUAL(expectedResult.UniversalTime, actualResult.UniversalTime); + } + }; }