General improvement of the CopyPasteManager:

- Reject expressions like '4 8 15' (the current app ignores spaces and understands 4815)
- Accept 16e23 and not only 16e+23
- ignore more types of white spaces characters
- remove Calculator::IsValidRegularExpression (was buggy and not used)
- Simplify prototype of ValidatePasteExpression (return a bool instead of a string we didn't need)
- simplify regex
This commit is contained in:
Rudy Huyn 2019-03-06 22:43:23 -08:00
commit c0cfe54c51
8 changed files with 152 additions and 139 deletions

View file

@ -25,39 +25,42 @@ String^ CopyPasteManager::supportedFormats[] =
constexpr wstring_view c_validCharacterSet{ L"0123456789()+-*/.abcdefABCDEF" }; constexpr wstring_view c_validCharacterSet{ L"0123456789()+-*/.abcdefABCDEF" };
// [\s\x85] means white-space characters // [\s\x85] means white-space characters
static const wstring c_wspc = L"[\\s\\x85]*"; static const wstring c_wspcLParens = L"[(]*";
static const wstring c_wspcLParens = c_wspc + L"[(]*" + c_wspc; static const wstring c_wspcRParens = L"[)]*";
static const wstring c_wspcRParens = c_wspc + L"[)]*" + c_wspc;
static const wstring c_signedDecFloat = L"[-+]?\\d*(\\d|[.])\\d*"; static const wstring c_signedDecFloat = L"[-+]?\\d*(\\d|[.])\\d*";
// Programmer Mode Integer patterns // Programmer Mode Integer patterns
// Support digit separators ` (WinDbg/MASM), ' (C++), and _ (C# and other languages) // Support digit separators ` (WinDbg/MASM), ' (C++), and _ (C# and other languages)
static const wstring c_hexProgrammerChars = L"([a-f]|[A-F]|\\d)+((_|'|`)([a-f]|[A-F]|\\d)+)*"; static const wstring c_hexProgrammerChars = L"[a-fA-F0-9]+([_'`][a-fA-F0-9]+)*";
static const wstring c_decProgrammerChars = L"\\d+((_|'|`)\\d+)*"; static const wstring c_decProgrammerChars = L"\\d+([_'`]\\d+)*";
static const wstring c_octProgrammerChars = L"[0-7]+((_|'|`)[0-7]+)*"; static const wstring c_octProgrammerChars = L"[0-7]+([_'`][0-7]+)*";
static const wstring c_binProgrammerChars = L"[0-1]+((_|'|`)[0-1]+)*"; static const wstring c_binProgrammerChars = L"[0-1]+([_'`][0-1]+)*";
static const wstring c_uIntSuffixes = L"[uU]?[lL]{0,2}"; static const wstring c_uIntSuffixes = L"[uU]?[lL]{0,2}";
// Regex used to format the string coming from the clipboard
static const wstring c_formatSpaces = L"[\\s\\x85]+";
static const wstring c_formatTrimAndRemoveUselessChars = L"^ | $|,|\"";
// RegEx Patterns used by various modes // RegEx Patterns used by various modes
static const array<wregex, 1> standardModePatterns = static const array<wregex, 1> standardModePatterns =
{ {
wregex(c_wspc + c_signedDecFloat + c_wspc) wregex(c_signedDecFloat)
}; };
static const array<wregex, 2> scientificModePatterns = static const array<wregex, 2> scientificModePatterns =
{ {
wregex(c_wspcLParens + c_signedDecFloat + c_wspcRParens), wregex(c_wspcLParens + c_signedDecFloat + c_wspcRParens),
wregex(c_wspcLParens + c_signedDecFloat + L"[e]([+]|[-])+\\d+" + c_wspcRParens) wregex(c_wspcLParens + c_signedDecFloat + L"e[+-]?\\d+" + c_wspcRParens)
}; };
static const array<array<wregex, 5>, 4> programmerModePatterns = static const array<array<wregex, 5>, 4> programmerModePatterns =
{ { { {
// Hex numbers like 5F, 4A0C, 0xa9, 0xFFull, 47CDh // Hex numbers like 5F, 4A0C, 0xa9, 0xFFull, 47CDh
{ {
wregex(c_wspcLParens + L"(0[xX])?" + c_hexProgrammerChars + c_uIntSuffixes + c_wspcRParens), wregex(c_wspcLParens + L"(0[xX])?" + c_hexProgrammerChars + c_uIntSuffixes + c_wspcRParens),
wregex(c_wspcLParens + c_hexProgrammerChars + L"[hH]?" + c_wspcRParens) wregex(c_wspcLParens + c_hexProgrammerChars + L"[hH]?" + c_wspcRParens)
}, },
// Decimal numbers like -145, 145, 0n145, 123ull etc // Decimal numbers like -145, 145, 0n145, 123ull etc
{ {
wregex(c_wspcLParens + L"[-+]?" + c_decProgrammerChars + L"[lL]{0,2}" +c_wspcRParens), wregex(c_wspcLParens + L"[-+]?" + c_decProgrammerChars + L"[lL]{0,2}" + c_wspcRParens),
wregex(c_wspcLParens + L"(0[nN])?" + c_decProgrammerChars + c_uIntSuffixes + c_wspcRParens) wregex(c_wspcLParens + L"(0[nN])?" + c_decProgrammerChars + c_uIntSuffixes + c_wspcRParens)
}, },
// Octal numbers like 06, 010, 0t77, 0o77, 077ull etc // Octal numbers like 06, 010, 0t77, 0o77, 077ull etc
@ -72,9 +75,29 @@ static const array<array<wregex, 5>, 4> programmerModePatterns =
} }; } };
static const array<wregex, 1> unitConverterPatterns = static const array<wregex, 1> unitConverterPatterns =
{ {
wregex(c_wspc + L"[-+]?\\d*[.]?\\d*" + c_wspc) wregex(c_signedDecFloat)
}; };
//Used by ExtractOperands
//Trim extra blank space before and after the string we want to extract (c_formatSpaces already replaces the sequences of white spaces by a single blank character)
std::wstring _subStrWithBasicTrim(const std::wstring & text, unsigned int pos, unsigned int len)
{
if (len == 0)
{
return wstring();
}
if (text[pos] == ' ')
{
++pos;
--len;
}
if (text[pos + len - 1] == ' ')
--len;
return text.substr(pos, len);
}
void CopyPasteManager::CopyToClipboard(String^ stringToCopy) void CopyPasteManager::CopyToClipboard(String^ stringToCopy)
{ {
// Copy the string to the clipboard // Copy the string to the clipboard
@ -89,14 +112,15 @@ task<String^> CopyPasteManager::GetStringToPaste(ViewMode mode, CategoryGroupTyp
auto dataPackageView = Clipboard::GetContent(); auto dataPackageView = Clipboard::GetContent();
// TODO: Support all formats supported by ClipboardHasText // TODO: Support all formats supported by ClipboardHasText
//-- add support to avoid pasting of expressions like 12 34(as of now we allow 1234)
//-- add support to allow pasting for expressions like .2 , -.2
//-- 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))) return create_task((dataPackageView->GetTextAsync(::StandardDataFormats::Text)))
.then([mode, modeType, programmerNumberBase, bitLengthType](String^ pastedText) .then([mode, modeType, programmerNumberBase, bitLengthType](String^ pastedText)
{ {
return ValidatePasteExpression(pastedText, mode, modeType, programmerNumberBase, bitLengthType); if (ValidatePasteExpression(pastedText, mode, modeType, programmerNumberBase, bitLengthType))
{
return pastedText;
}
return ref new String(PasteErrorString);
} }
, task_continuation_context::use_arbitrary()); , task_continuation_context::use_arbitrary());
} }
@ -118,29 +142,36 @@ int CopyPasteManager::ClipboardTextFormat()
return result; return result;
} }
String^ CopyPasteManager::ValidatePasteExpression(String^ pastedText, ViewMode mode, int programmerNumberBase, int bitLengthType) bool CopyPasteManager::ValidatePasteExpression(String^ pastedText, ViewMode mode, int programmerNumberBase, int bitLengthType)
{ {
return CopyPasteManager::ValidatePasteExpression(pastedText, mode, NavCategory::GetGroupType(mode), programmerNumberBase, bitLengthType); return CopyPasteManager::ValidatePasteExpression(pastedText, mode, NavCategory::GetGroupType(mode), programmerNumberBase, bitLengthType);
} }
// return "NoOp" if pastedText is invalid else return pastedText bool 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) if (pastedText->Length() > MaxPasteableLength)
{ {
// return NoOp to indicate don't paste anything.
TraceLogger::GetInstance().LogInvalidInputPasted(L"PastedExpressionSizeGreaterThanMaxAllowed", L"MoreThanMaxInput", mode, programmerNumberBase, bitLengthType); TraceLogger::GetInstance().LogInvalidInputPasted(L"PastedExpressionSizeGreaterThanMaxAllowed", L"MoreThanMaxInput", mode, programmerNumberBase, bitLengthType);
return StringReference(PasteErrorString); return false;
} }
wstring pasteExpression = pastedText->Data(); wstring pasteExpression = pastedText->Data();
// Verify if string contains the en-US digit separator if the local digit separator is a different one.
wchar_t localDigitSeparator = LocalizationSettings::GetInstance().GetDecimalSeparator();
if (localDigitSeparator != '.' && pasteExpression.find('.') != std::string::npos)
{
return false;
}
// Get english translated expression // 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 //replace all sequences of whitespace characters by 1 single blank space character (u+0020).
pasteExpression = Utils::RemoveUnwantedCharsFromWstring(englishString->Data()); pasteExpression = regex_replace(englishString->Data(), wregex(c_formatSpaces),
mode == ViewMode::Programmer? L"" : L" ");
//Trim the string and remove commas
pasteExpression = regex_replace(pasteExpression, wregex(c_formatTrimAndRemoveUselessChars), L"");
// If the last character is an = sign, remove it from the pasteExpression to allow evaluating the result on paste. // If the last character is an = sign, remove it from the pasteExpression to allow evaluating the result on paste.
if (!pasteExpression.empty() && pasteExpression.back() == L'=') if (!pasteExpression.empty() && pasteExpression.back() == L'=')
@ -153,8 +184,7 @@ String^ CopyPasteManager::ValidatePasteExpression(String^ pastedText, ViewMode m
vector<wstring> operands = ExtractOperands(pasteExpression, mode, programmerNumberBase, bitLengthType); vector<wstring> operands = ExtractOperands(pasteExpression, mode, programmerNumberBase, bitLengthType);
if (operands.empty()) if (operands.empty())
{ {
// return NoOp to indicate don't paste anything. return false;
return StringReference(PasteErrorString);
} }
if (modeType == CategoryGroupType::Converter) if (modeType == CategoryGroupType::Converter)
@ -166,10 +196,10 @@ String^ CopyPasteManager::ValidatePasteExpression(String^ pastedText, ViewMode m
if (!ExpressionRegExMatch(operands, mode, modeType, programmerNumberBase, bitLengthType)) if (!ExpressionRegExMatch(operands, mode, modeType, programmerNumberBase, bitLengthType))
{ {
TraceLogger::GetInstance().LogInvalidInputPasted(L"InvalidExpressionForPresentMode", pastedText->Data(), mode, programmerNumberBase, bitLengthType); TraceLogger::GetInstance().LogInvalidInputPasted(L"InvalidExpressionForPresentMode", pastedText->Data(), mode, programmerNumberBase, bitLengthType);
return StringReference(PasteErrorString); return false;
} }
return ref new String(pastedText->Data()); return true;
} }
vector<wstring> CopyPasteManager::ExtractOperands(const wstring& pasteExpression, ViewMode mode, int programmerNumberBase, int bitLengthType) vector<wstring> CopyPasteManager::ExtractOperands(const wstring& pasteExpression, ViewMode mode, int programmerNumberBase, int bitLengthType)
@ -236,7 +266,7 @@ vector<wstring> CopyPasteManager::ExtractOperands(const wstring& pasteExpression
expLength = 0; expLength = 0;
haveOperator = true; haveOperator = true;
isPreviousOperator = true; isPreviousOperator = true;
operands.push_back(pasteExpression.substr(lastIndex, i - lastIndex)); operands.push_back(_subStrWithBasicTrim(pasteExpression, lastIndex, i - lastIndex));
lastIndex = i + 1; lastIndex = i + 1;
} }
else else
@ -255,7 +285,7 @@ vector<wstring> CopyPasteManager::ExtractOperands(const wstring& pasteExpression
} }
else else
{ {
operands.push_back(pasteExpression.substr(lastIndex, pasteExpression.length() - 1)); operands.push_back(_subStrWithBasicTrim(pasteExpression, lastIndex, pasteExpression.length() - lastIndex));
} }
return operands; return operands;
@ -298,7 +328,11 @@ bool CopyPasteManager::ExpressionRegExMatch(vector<wstring> operands, ViewMode m
bool operandMatched = false; bool operandMatched = false;
for (const wregex& pattern : patterns) for (const wregex& pattern : patterns)
{ {
operandMatched = operandMatched || regex_match(operand, pattern); if (regex_match(operand, pattern))
{
operandMatched = true;
break;
}
} }
if (operandMatched) if (operandMatched)

View file

@ -37,12 +37,12 @@ namespace CalculatorApp
private: private:
static int ClipboardTextFormat(); static int ClipboardTextFormat();
static Platform::String^ ValidatePasteExpression( static bool ValidatePasteExpression(
Platform::String^ pastedText, Platform::String^ pastedText,
CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::ViewMode mode,
int programmerNumberBase, int programmerNumberBase,
int bitLengthType); int bitLengthType);
static Platform::String^ ValidatePasteExpression( static bool ValidatePasteExpression(
Platform::String^ pastedText, Platform::String^ pastedText,
CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::ViewMode mode,
CalculatorApp::Common::CategoryGroupType modeType, CalculatorApp::Common::CategoryGroupType modeType,

View file

@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved. // Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. // Licensed under the MIT License.
// //
@ -86,13 +86,6 @@ bool Utils::IsLastCharacterTarget(_In_ wstring const &input, _In_ wchar_t target
return !input.empty() && input.back() == target; return !input.empty() && input.back() == target;
} }
//return wstring after removing characters like space, comma, and double quotes
wstring Utils::RemoveUnwantedCharsFromWstring(wstring input)
{
wchar_t unWantedChars[] = { L' ', L',', L'"', 8234, 8235, 8236, 8237 };
return RemoveUnwantedCharsFromWstring(input, unWantedChars, 6);
}
//return wstring after removing characters specified by unwantedChars array //return wstring after removing characters specified by unwantedChars array
wstring Utils::RemoveUnwantedCharsFromWstring(wstring input, wchar_t* unwantedChars, unsigned int size) wstring Utils::RemoveUnwantedCharsFromWstring(wstring input, wchar_t* unwantedChars, unsigned int size)
{ {

View file

@ -280,7 +280,6 @@ namespace Utils
Platform::String^ GetStringValue(Platform::String^ input); Platform::String^ GetStringValue(Platform::String^ input);
bool IsLastCharacterTarget(std::wstring const &input, wchar_t target); bool IsLastCharacterTarget(std::wstring const &input, wchar_t target);
std::wstring RemoveUnwantedCharsFromWstring(std::wstring inputString, wchar_t* unwantedChars, unsigned int size); std::wstring RemoveUnwantedCharsFromWstring(std::wstring inputString, wchar_t* unwantedChars, unsigned int size);
std::wstring RemoveUnwantedCharsFromWstring(std::wstring input);
double GetDoubleFromWstring(std::wstring input); double GetDoubleFromWstring(std::wstring input);
int GetWindowId(); int GetWindowId();
void RunOnUIThreadNonblocking(std::function<void()>&& function, _In_ Windows::UI::Core::CoreDispatcher^ currentDispatcher); void RunOnUIThreadNonblocking(std::function<void()>&& function, _In_ Windows::UI::Core::CoreDispatcher^ currentDispatcher);

View file

@ -42,7 +42,7 @@ namespace CalculatorApp::ViewModel
StringReference IsInError(L"IsInError"); StringReference IsInError(L"IsInError");
StringReference BinaryDisplayValue(L"BinaryDisplayValue"); StringReference BinaryDisplayValue(L"BinaryDisplayValue");
} }
namespace CalculatorResourceKeys namespace CalculatorResourceKeys
{ {
StringReference CalculatorExpression(L"Format_CalculatorExpression"); StringReference CalculatorExpression(L"Format_CalculatorExpression");
@ -574,7 +574,7 @@ void StandardCalculatorViewModel::OnButtonPressed(Object^ parameter)
if (IsInError) if (IsInError)
{ {
m_standardCalculatorManager.SendCommand(Command::CommandCLEAR); m_standardCalculatorManager.SendCommand(Command::CommandCLEAR);
if (!IsRecoverableCommand((int)numOpEnum)) if (!IsRecoverableCommand((int)numOpEnum))
{ {
return; return;
@ -862,14 +862,26 @@ void StandardCalculatorViewModel::OnPaste(String^ pastedString, ViewMode mode)
if (mappedNumOp == NumbersAndOperatorsEnum::Exp) if (mappedNumOp == NumbersAndOperatorsEnum::Exp)
{ {
++it; ++it;
if (!(MapCharacterToButtonId(*it, canSendNegate) == NumbersAndOperatorsEnum::Add)) switch (MapCharacterToButtonId(*it, canSendNegate))
{ {
Command cmdNegate = ConvertToOperatorsEnum(NumbersAndOperatorsEnum::Negate); case NumbersAndOperatorsEnum::Subtract:
m_standardCalculatorManager.SendCommand(cmdNegate); {
Command cmdNegate = ConvertToOperatorsEnum(NumbersAndOperatorsEnum::Negate);
m_standardCalculatorManager.SendCommand(cmdNegate);
++it;
}
break;
case NumbersAndOperatorsEnum::Add:
{
++it;
}
break;
} }
} }
else
++it; {
++it;
}
} }
} }
@ -1400,29 +1412,29 @@ void StandardCalculatorViewModel::SaveEditedCommand(_In_ unsigned int tokenPosit
switch (nOpCode) switch (nOpCode)
{ {
case static_cast<int>(Command::CommandASIN) : case static_cast<int>(Command::CommandASIN) :
updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandSIN), true, angleType); updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandSIN), true, angleType);
break; break;
case static_cast<int>(Command::CommandACOS) : case static_cast<int>(Command::CommandACOS) :
updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandCOS), true, angleType); updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandCOS), true, angleType);
break; break;
case static_cast<int>(Command::CommandATAN) : case static_cast<int>(Command::CommandATAN) :
updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandTAN), true, angleType); updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandTAN), true, angleType);
break; break;
case static_cast<int>(Command::CommandASINH) : case static_cast<int>(Command::CommandASINH) :
updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandSINH), true, angleType); updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandSINH), true, angleType);
break; break;
case static_cast<int>(Command::CommandACOSH) : case static_cast<int>(Command::CommandACOSH) :
updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandCOSH), true, angleType); updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandCOSH), true, angleType);
break; break;
case static_cast<int>(Command::CommandATANH) : case static_cast<int>(Command::CommandATANH) :
updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandTANH), true, angleType); updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandTANH), true, angleType);
break; break;
case static_cast<int>(Command::CommandPOWE) : case static_cast<int>(Command::CommandPOWE) :
updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandLN), true, angleType); updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandLN), true, angleType);
break; break;
default: default:
updatedToken = CCalcEngine::OpCodeToUnaryString(nOpCode, false, angleType); updatedToken = CCalcEngine::OpCodeToUnaryString(nOpCode, false, angleType);
} }
if ((token.first.length() > 0) && (token.first[token.first.length() - 1] == L'(')) if ((token.first.length() > 0) && (token.first[token.first.length() - 1] == L'('))
{ {

View file

@ -690,28 +690,6 @@ void Calculator::expressionContainer_LayoutUpdated(_In_ Object^ sender, _In_ Obj
expressionText->UpdateScrollButtons(); expressionText->UpdateScrollButtons();
} }
bool Calculator::IsValidRegularExpression(std::wstring str)
{
bool result = false;
std::wregex regexPatterns[3];
regexPatterns[0] = L"[-]{0,1}[0-9]{0,}[.]{0,1}[0-9]{0,}";
regexPatterns[1] = L"[-]{0,1}[0-9]{0,}[.]{0,1}[0-9]{0,}[e]{1}[+]{1}[0-9]{1,}";
regexPatterns[2] = L"[-]{0,1}[0-9]{0,}[.]{0,1}[0-9]{0,}[e]{1}[-]{1}[0-9]{1,}";
const auto& localizer = LocalizationSettings::GetInstance();
String^ englishString = localizer.GetEnglishValueFromLocalizedDigits(str);
for (int i = 0; i < 3; ++i)
{
if (regex_match(englishString->Data(), regexPatterns[i]))
{
result = true;
break;
}
}
return result;
}
void Calculator::DockPanelTapped(_In_ TappedRoutedEventArgs^ e) void Calculator::DockPanelTapped(_In_ TappedRoutedEventArgs^ e)
{ {
int index = DockPivot->SelectedIndex; int index = DockPivot->SelectedIndex;

View file

@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved. // Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. // Licensed under the MIT License.
#pragma once #pragma once
@ -131,7 +131,6 @@ namespace CalculatorApp
void OnMemoryFlyOutTapped(_In_ Platform::Object^ sender, _In_ Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e); void OnMemoryFlyOutTapped(_In_ Platform::Object^ sender, _In_ Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e);
void OnHistoryFlyOutTapped(_In_ Platform::Object^ sender, _In_ Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e); void OnHistoryFlyOutTapped(_In_ Platform::Object^ sender, _In_ Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e);
void expressionContainer_LayoutUpdated(_In_ Platform::Object^ sender, _In_ Platform::Object^ e); void expressionContainer_LayoutUpdated(_In_ Platform::Object^ sender, _In_ Platform::Object^ e);
bool IsValidRegularExpression(std::wstring str);
void DockPanelTapped(_In_ Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e); void DockPanelTapped(_In_ Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e);
void OnResultsLayoutChanged(_In_ Platform::Object^ sender, _In_ Platform::Object^ e); void OnResultsLayoutChanged(_In_ Platform::Object^ sender, _In_ Platform::Object^ e);
void SetResultStyles(); void SetResultStyles();

View file

@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved. // Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. // Licensed under the MIT License.
#include "pch.h" #include "pch.h"
@ -28,7 +28,7 @@ namespace CalculatorUnitTests
int size = sizeof(dataSet)/sizeof(*dataSet);\ int size = sizeof(dataSet)/sizeof(*dataSet);\
while(--size)\ while(--size)\
{\ {\
VERIFY_ARE_EQUAL(func(dataSet[size]), dataSet[size]);\ VERIFY_IS_TRUE(func(dataSet[size]));\
}\ }\
} }
@ -37,7 +37,7 @@ namespace CalculatorUnitTests
int size = sizeof(dataSet)/sizeof(*dataSet);;\ int size = sizeof(dataSet)/sizeof(*dataSet);;\
while(--size)\ while(--size)\
{\ {\
VERIFY_ARE_EQUAL(func(dataSet[size]), StringReference(L"NoOp"));\ VERIFY_IS_FALSE(func(dataSet[size]));\
}\ }\
} }
@ -71,15 +71,15 @@ namespace CalculatorUnitTests
{ {
exp_TooLong += L"-1234567"; exp_TooLong += L"-1234567";
} }
VERIFY_ARE_EQUAL(m_CopyPasteManager.ValidatePasteExpression(StringReference(exp_TooLong.c_str()), ViewMode::Standard, CategoryGroupType::Calculator, -1, -1), StringReference(exp_TooLong.c_str()), L"Verify ValidatePasteExpression handles expressions up to max length"); VERIFY_IS_TRUE(m_CopyPasteManager.ValidatePasteExpression(StringReference(exp_TooLong.c_str()), ViewMode::Standard, CategoryGroupType::Calculator, -1, -1), L"Verify ValidatePasteExpression handles expressions up to max length");
exp_TooLong += L"1"; exp_TooLong += L"1";
VERIFY_ARE_EQUAL(m_CopyPasteManager.ValidatePasteExpression(StringReference(exp_TooLong.c_str()), ViewMode::Standard, CategoryGroupType::Calculator, -1, -1), StringReference(L"NoOp"), L"Verify ValidatePasteExpression returns NoOp for strings over max length"); VERIFY_IS_FALSE(m_CopyPasteManager.ValidatePasteExpression(StringReference(exp_TooLong.c_str()), ViewMode::Standard, CategoryGroupType::Calculator, -1, -1), L"Verify ValidatePasteExpression returns NoOp for strings over max length");
VERIFY_ARE_EQUAL(m_CopyPasteManager.ValidatePasteExpression(StringReference(L""), ViewMode::Standard, CategoryGroupType::Calculator, -1, -1), StringReference(L"NoOp"), L"Verify empty string is invalid"); VERIFY_IS_FALSE(m_CopyPasteManager.ValidatePasteExpression(StringReference(L""), ViewMode::Standard, CategoryGroupType::Calculator, -1, -1), L"Verify empty string is invalid");
VERIFY_ARE_EQUAL(m_CopyPasteManager.ValidatePasteExpression(StringReference(L"123e456"), ViewMode::Standard, CategoryGroupType::Calculator, -1, -1), StringReference(L"NoOp"), L"Verify pasting unsupported strings for the current mode is invalid"); VERIFY_IS_FALSE(m_CopyPasteManager.ValidatePasteExpression(StringReference(L"123e+456"), ViewMode::Standard, CategoryGroupType::Calculator, -1, -1), L"Verify pasting unsupported strings for the current mode is invalid");
VERIFY_ARE_EQUAL(m_CopyPasteManager.ValidatePasteExpression(StringReference(L"123"), ViewMode::None, CategoryGroupType::None, -1, -1), StringReference(L"NoOp"), L"Verify pasting without a ViewMode or Category is invalid"); VERIFY_IS_FALSE(m_CopyPasteManager.ValidatePasteExpression(StringReference(L"123"), ViewMode::None, CategoryGroupType::None, -1, -1), L"Verify pasting without a ViewMode or Category is invalid");
}; };
TEST_METHOD(ValidateExtractOperands) TEST_METHOD(ValidateExtractOperands)
@ -89,9 +89,7 @@ namespace CalculatorUnitTests
vector<wstring> oneOperand = { L"123456" }; vector<wstring> oneOperand = { L"123456" };
VERIFY_ARE_EQUAL(m_CopyPasteManager.ExtractOperands(L"123456", ViewMode::Standard), oneOperand); VERIFY_ARE_EQUAL(m_CopyPasteManager.ExtractOperands(L"123456", ViewMode::Standard), oneOperand);
oneOperand = { L"123^456" }; oneOperand = { L"123^456" };
VERIFY_ARE_EQUAL(m_CopyPasteManager.ExtractOperands(L"123^456", ViewMode::Standard), oneOperand); VERIFY_ARE_EQUAL(m_CopyPasteManager.ExtractOperands(L"123^456", ViewMode::Standard), oneOperand); vector<wstring> twoOperands = { L"123", L"456" };
vector<wstring> twoOperands = { L"123", L"456" };
VERIFY_ARE_EQUAL(m_CopyPasteManager.ExtractOperands(L"123+456", ViewMode::Standard), twoOperands); VERIFY_ARE_EQUAL(m_CopyPasteManager.ExtractOperands(L"123+456", ViewMode::Standard), twoOperands);
VERIFY_ARE_EQUAL(m_CopyPasteManager.ExtractOperands(L"123-456", ViewMode::Standard), twoOperands); VERIFY_ARE_EQUAL(m_CopyPasteManager.ExtractOperands(L"123-456", ViewMode::Standard), twoOperands);
VERIFY_ARE_EQUAL(m_CopyPasteManager.ExtractOperands(L"123*456", ViewMode::Standard), twoOperands); VERIFY_ARE_EQUAL(m_CopyPasteManager.ExtractOperands(L"123*456", ViewMode::Standard), twoOperands);
@ -313,97 +311,97 @@ namespace CalculatorUnitTests
private: private:
CopyPasteManager m_CopyPasteManager; CopyPasteManager m_CopyPasteManager;
String^ ValidateStandardPasteExpression(_In_ String^ pastedText) bool ValidateStandardPasteExpression(_In_ String^ pastedText)
{ {
return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Standard, -1/*number base*/, -1/*bitlength Type*/); return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Standard, -1/*number base*/, -1/*bitlength Type*/);
} }
String^ ValidateScientificPasteExpression(_In_ String^ pastedText) bool ValidateScientificPasteExpression(_In_ String^ pastedText)
{ {
return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Scientific, -1/*number base*/, -1/*bitlength Type*/); return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Scientific, -1/*number base*/, -1/*bitlength Type*/);
} }
String^ ValidateConverterPasteExpression(_In_ String^ pastedText) bool ValidateConverterPasteExpression(_In_ String^ pastedText)
{ {
return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::None, CategoryGroupType::Converter, -1/*number base*/, -1/*bitlength Type*/); return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::None, CategoryGroupType::Converter, -1/*number base*/, -1/*bitlength Type*/);
} }
String^ ValidateProgrammerHexQwordPasteExpression(_In_ String^ pastedText) bool ValidateProgrammerHexQwordPasteExpression(_In_ String^ pastedText)
{ {
return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, HexBase/*number base*/, QwordType/*bitlength Type*/); return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, HexBase/*number base*/, QwordType/*bitlength Type*/);
} }
String^ ValidateProgrammerHexDwordPasteExpression(_In_ String^ pastedText) bool ValidateProgrammerHexDwordPasteExpression(_In_ String^ pastedText)
{ {
return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, HexBase/*number base*/, DwordType/*bitlength Type*/); return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, HexBase/*number base*/, DwordType/*bitlength Type*/);
} }
String^ ValidateProgrammerHexWordPasteExpression(_In_ String^ pastedText) bool ValidateProgrammerHexWordPasteExpression(_In_ String^ pastedText)
{ {
return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, HexBase/*number base*/, WordType/*bitlength Type*/); return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, HexBase/*number base*/, WordType/*bitlength Type*/);
} }
String^ ValidateProgrammerHexBytePasteExpression(_In_ String^ pastedText) bool ValidateProgrammerHexBytePasteExpression(_In_ String^ pastedText)
{ {
return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, HexBase/*number base*/, ByteType/*bitlength Type*/); return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, HexBase/*number base*/, ByteType/*bitlength Type*/);
} }
String^ ValidateProgrammerDecQwordPasteExpression(_In_ String^ pastedText) bool ValidateProgrammerDecQwordPasteExpression(_In_ String^ pastedText)
{ {
return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, DecBase/*number base*/, QwordType/*bitlength Type*/); return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, DecBase/*number base*/, QwordType/*bitlength Type*/);
} }
String^ ValidateProgrammerDecDwordPasteExpression(_In_ String^ pastedText) bool ValidateProgrammerDecDwordPasteExpression(_In_ String^ pastedText)
{ {
return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, DecBase/*number base*/, DwordType/*bitlength Type*/); return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, DecBase/*number base*/, DwordType/*bitlength Type*/);
} }
String^ ValidateProgrammerDecWordPasteExpression(_In_ String^ pastedText) bool ValidateProgrammerDecWordPasteExpression(_In_ String^ pastedText)
{ {
return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, DecBase/*number base*/, WordType/*bitlength Type*/); return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, DecBase/*number base*/, WordType/*bitlength Type*/);
} }
String^ ValidateProgrammerDecBytePasteExpression(_In_ String^ pastedText) bool ValidateProgrammerDecBytePasteExpression(_In_ String^ pastedText)
{ {
return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, DecBase/*number base*/, ByteType/*bitlength Type*/); return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, DecBase/*number base*/, ByteType/*bitlength Type*/);
} }
String^ ValidateProgrammerOctQwordPasteExpression(_In_ String^ pastedText) bool ValidateProgrammerOctQwordPasteExpression(_In_ String^ pastedText)
{ {
return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, OctBase/*number base*/, QwordType/*bitlength Type*/); return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, OctBase/*number base*/, QwordType/*bitlength Type*/);
} }
String^ ValidateProgrammerOctDwordPasteExpression(_In_ String^ pastedText) bool ValidateProgrammerOctDwordPasteExpression(_In_ String^ pastedText)
{ {
return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, OctBase/*number base*/, DwordType/*bitlength Type*/); return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, OctBase/*number base*/, DwordType/*bitlength Type*/);
} }
String^ ValidateProgrammerOctWordPasteExpression(_In_ String^ pastedText) bool ValidateProgrammerOctWordPasteExpression(_In_ String^ pastedText)
{ {
return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, OctBase/*number base*/, WordType/*bitlength Type*/); return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, OctBase/*number base*/, WordType/*bitlength Type*/);
} }
String^ ValidateProgrammerOctBytePasteExpression(_In_ String^ pastedText) bool ValidateProgrammerOctBytePasteExpression(_In_ String^ pastedText)
{ {
return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, OctBase/*number base*/, ByteType/*bitlength Type*/); return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, OctBase/*number base*/, ByteType/*bitlength Type*/);
} }
String^ ValidateProgrammerBinQwordPasteExpression(_In_ String^ pastedText) bool ValidateProgrammerBinQwordPasteExpression(_In_ String^ pastedText)
{ {
return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, BinBase/*number base*/, QwordType/*bitlength Type*/); return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, BinBase/*number base*/, QwordType/*bitlength Type*/);
} }
String^ ValidateProgrammerBinDwordPasteExpression(_In_ String^ pastedText) bool ValidateProgrammerBinDwordPasteExpression(_In_ String^ pastedText)
{ {
return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, BinBase/*number base*/, DwordType/*bitlength Type*/); return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, BinBase/*number base*/, DwordType/*bitlength Type*/);
} }
String^ ValidateProgrammerBinWordPasteExpression(_In_ String^ pastedText) bool ValidateProgrammerBinWordPasteExpression(_In_ String^ pastedText)
{ {
return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, BinBase/*number base*/, WordType/*bitlength Type*/); return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, BinBase/*number base*/, WordType/*bitlength Type*/);
} }
String^ ValidateProgrammerBinBytePasteExpression(_In_ String^ pastedText) bool ValidateProgrammerBinBytePasteExpression(_In_ String^ pastedText)
{ {
return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, BinBase/*number base*/, ByteType/*bitlength Type*/); return m_CopyPasteManager.ValidatePasteExpression(pastedText, ViewMode::Programmer, BinBase/*number base*/, ByteType/*bitlength Type*/);
} }
@ -455,17 +453,17 @@ namespace CalculatorUnitTests
START_LOOP(input) START_LOOP(input)
// paste number in standard mode and then validate the pastability of displayed number for other modes // paste number in standard mode and then validate the pastability of displayed number for other modes
scvm->OnPaste(input[size], ViewMode::Standard); scvm->OnPaste(input[size], ViewMode::Standard);
VERIFY_ARE_EQUAL(ValidateStandardPasteExpression(scvm->DisplayValue), scvm->DisplayValue); VERIFY_IS_TRUE(ValidateStandardPasteExpression(scvm->DisplayValue));
VERIFY_ARE_EQUAL(ValidateScientificPasteExpression(scvm->DisplayValue), scvm->DisplayValue); VERIFY_IS_TRUE(ValidateScientificPasteExpression(scvm->DisplayValue));
VERIFY_ARE_EQUAL(ValidateProgrammerHexQwordPasteExpression(scvm->DisplayValue), scvm->DisplayValue); VERIFY_IS_TRUE(ValidateProgrammerHexQwordPasteExpression(scvm->DisplayValue));
END_LOOP END_LOOP
} }
void CopyPasteManagerTest::ValidateStandardPasteExpressionTest() void CopyPasteManagerTest::ValidateStandardPasteExpressionTest()
{ {
String^ positiveInput[] = { L"123", L"+123", L"-133", L"12345.", L"+12.34", L"12.345", L"012.034", L"-23.032", L"-.123", L".1234", L"012.012", L"123+456", L"123+-234", L"123*-345", L"123*4*-3", L"123*+4*-3", L"1,234", L"1 2 3", L"\n\r1,234\n", L"\f\n1+2\t\r\v\x85", L"\n 1+\n2 ", L"1\"2", L"1234567891234567"/*boundary condition <=16 digits*/, L"2+2=", L"2+2= " }; String^ positiveInput[] = { L"123", L"+123", L"-133", L"12345.", L"+12.34", L"12.345", L"012.034", L"-23.032", L"-.123", L".1234", L"012.012", L"123+456", L"123+-234", L"123*-345", L"123*4*-3", L"123*+4*-3", L"1,234", L"\n\r1,234\n", L"\f\n1+2\t\r\v\x85", L"\n 1+\n2 ", L"1\"2", L"1234567891234567"/*boundary condition <=16 digits*/, L"2+2=", L"2+2= " };
String^ negativeInput[] = { L"(123)+(456)", L"1.2e23"/*unsigned exponent*/, L"12345e-23", L"abcdef", L"xyz", L"ABab", L"e+234", L"12345678912345678"/*boundary condition: greater than 16 digits*/, L"SIN(2)", L"2+2==", L"2=+2" }; String^ negativeInput[] = { L"(123)+(456)", L"1.2e23"/*unsigned exponent*/, L"12345e-23", L"abcdef", L"xyz", L"ABab", L"e+234", L"12345678912345678"/*boundary condition: greater than 16 digits*/, L"SIN(2)", L"2+2==", L"2=+2", L"1 2 3" };
ASSERT_POSITIVE_TESTCASES(ValidateStandardPasteExpression, positiveInput); ASSERT_POSITIVE_TESTCASES(ValidateStandardPasteExpression, positiveInput);
ASSERT_NEGATIVE_TESTCASES(ValidateStandardPasteExpression, negativeInput); ASSERT_NEGATIVE_TESTCASES(ValidateStandardPasteExpression, negativeInput);
@ -473,8 +471,8 @@ namespace CalculatorUnitTests
void CopyPasteManagerTest::ValidateScientificPasteExpressionTest() void CopyPasteManagerTest::ValidateScientificPasteExpressionTest()
{ {
String^ positiveInput[] = { L"123", L"+123", L"-133", L"123+456", L"12345e+023", L"1,234", L"1.23", L"-.123", L".1234", L"012.012", L"123+-234", L"123*-345", L"123*4*-3", L"123*+4*-3", L"1 2 3", L"\n\r1,234\n", L"\f\n1+2\t\r\v\x85", L"\n 1+\n2 ", L"1\"2", L"1.2e+023", L"12345e-23", L"(123)+(456)", L"12345678912345678123456789012345", L"(123)+(456)=", L"2+2= " }; String^ positiveInput[] = { L"1.2e23", L"123", L"+123", L"-133", L"123+456", L"12345e+023", L"1,234", L"1.23", L"-.123", L".1234", L"012.012", L"123+-234", L"123*-345", L"123*4*-3", L"123*+4*-3", L"\n\r1,234\n", L"\f\n1+2\t\r\v\x85", L"\n 1+\n2 ", L"1\"2", L"1.2e+023", L"12345e-23", L"(123)+(456)", L"12345678912345678123456789012345", L"(123)+(456)=", L"2+2= " };
String^ negativeInput[] = { L"1.2e23"/*unsigned exponent*/, L"abcdef", L"xyz", L"ABab", L"e+234", L"123456789123456781234567890123456"/*boundary condition: greater than 32 digits*/, L"SIN(2)", L"2+2==", L"2=+2" }; String^ negativeInput[] = { L"abcdef", L"xyz", L"ABab", L"e+234", L"123456789123456781234567890123456"/*boundary condition: greater than 32 digits*/, L"SIN(2)", L"2+2==", L"2=+2", L"1 2 3" };
ASSERT_POSITIVE_TESTCASES(ValidateScientificPasteExpression, positiveInput); ASSERT_POSITIVE_TESTCASES(ValidateScientificPasteExpression, positiveInput);
ASSERT_NEGATIVE_TESTCASES(ValidateScientificPasteExpression, negativeInput); ASSERT_NEGATIVE_TESTCASES(ValidateScientificPasteExpression, negativeInput);
@ -590,8 +588,8 @@ namespace CalculatorUnitTests
void CopyPasteManagerTest::ValidateConverterPasteExpressionTest() void CopyPasteManagerTest::ValidateConverterPasteExpressionTest()
{ {
String^ positiveInput[] = { L"123", L"+123", L"-133", L"12345.", L"012.012", L"1,234", L"1 2 3", L"\n\r1,234\n", L"\f\n12\t\r\v\x85", L"1\"2", L"100=", L"100= " }; String^ positiveInput[] = { L"123", L"+123", L"-133", L"12345.", L"012.012", L"1,234", L"\n\r1,234\n", L"\f\n12\t\r\v\x85", L"1\"2", L"100=", L"100= " };
String^ negativeInput[] = { L"(123)+(456)", L"1.2e23"/*unsigned exponent*/, L"12345e-23", L"\n 1+\n2 ", L"123+456", L"abcdef", L"\n 1+\n2 ", L"xyz", L"ABab", L"e+234", L"12345678912345678"/*boundary condition: greater than 16 bits*/, L"SIN(2)", L"123+-234", L"100==", L"=100" }; String^ negativeInput[] = { L"(123)+(456)", L"1.2e23"/*unsigned exponent*/, L"12345e-23", L"\n 1+\n2 ", L"123+456", L"abcdef", L"\n 1+\n2 ", L"xyz", L"ABab", L"e+234", L"12345678912345678"/*boundary condition: greater than 16 bits*/, L"SIN(2)", L"123+-234", L"100==", L"=100", L"1 2 3" };
ASSERT_POSITIVE_TESTCASES(ValidateConverterPasteExpression, positiveInput); ASSERT_POSITIVE_TESTCASES(ValidateConverterPasteExpression, positiveInput);
ASSERT_NEGATIVE_TESTCASES(ValidateConverterPasteExpression, negativeInput); ASSERT_NEGATIVE_TESTCASES(ValidateConverterPasteExpression, negativeInput);