Skip to content

Commit

Permalink
Merge pull request #417 from stephenegriffin/flag
Browse files Browse the repository at this point in the history
unit tests for flags
  • Loading branch information
stephenegriffin authored Nov 2, 2020
2 parents 1b95f9c + 5c07f30 commit 6d30b0e
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 114 deletions.
1 change: 1 addition & 0 deletions UnitTest/UnitTest.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,7 @@
<ClInclude Include="UnitTest.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="tests\flagtest.cpp" />
<ClCompile Include="tests\proptagTest.cpp" />
<ClCompile Include="tests\blockPVtest.cpp" />
<ClCompile Include="tests\blocktest.cpp" />
Expand Down
3 changes: 3 additions & 0 deletions UnitTest/UnitTest.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@
<ClCompile Include="tests\proptagTest.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="tests\flagtest.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="res\UnitTest.rc">
Expand Down
89 changes: 89 additions & 0 deletions UnitTest/tests/flagtest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#include <UnitTest/stdafx.h>
#include <UnitTest/UnitTest.h>
#include <core/interpret/flags.h>
#include <core/mapi/extraPropTags.h>

namespace flagtest
{
TEST_CLASS(flagtest)
{
public:
// Without this, clang gets weird
static const bool dummy_var = true;

TEST_CLASS_INITIALIZE(initialize) { unittest::init(); }

TEST_METHOD(Test_InterpretFlags)
{
// flagVALUE
unittest::AreEqualEx(L"BMR_EQZ", flags::InterpretFlags(flagBitmask, BMR_EQZ));
unittest::AreEqualEx(L"BMR_NEZ", flags::InterpretFlags(flagBitmask, BMR_NEZ));

// flagFLAG with null flagVALUE
unittest::AreEqualEx(
L"MSG_DEFAULTS_NONE", flags::InterpretFlags(PROP_ID(PR_CERT_DEFAULTS), MSG_DEFAULTS_NONE));
unittest::AreEqualEx(
L"MSG_DEFAULTS_FOR_FORMAT | MSG_DEFAULTS_SEND_CERT",
flags::InterpretFlags(PROP_ID(PR_CERT_DEFAULTS), MSG_DEFAULTS_FOR_FORMAT | MSG_DEFAULTS_SEND_CERT));
unittest::AreEqualEx(
L"MSG_DEFAULTS_GLOBAL | 0x8",
flags::InterpretFlags(PROP_ID(PR_CERT_DEFAULTS), MSG_DEFAULTS_GLOBAL | 0x8));

// flagFLAG mixed with flagVALUE
unittest::AreEqualEx(
L"MAPI_P1 | MAPI_SUBMITTED | MAPI_ORIG",
flags::InterpretFlags(PROP_ID(PR_RECIPIENT_TYPE), MAPI_P1 | MAPI_SUBMITTED | MAPI_ORIG));
unittest::AreEqualEx(
L"MAPI_P1 | MAPI_TO", flags::InterpretFlags(PROP_ID(PR_RECIPIENT_TYPE), MAPI_P1 | MAPI_TO));
unittest::AreEqualEx(
L"MAPI_SUBMITTED | 0x5", flags::InterpretFlags(PROP_ID(PR_RECIPIENT_TYPE), MAPI_SUBMITTED | 0x5));
unittest::AreEqualEx(
L"MAPI_SUBMITTED | 0x20000000 | 0x5", flags::InterpretFlags(PROP_ID(PR_RECIPIENT_TYPE), MAPI_SUBMITTED | 0x20000005));

// flagFLAG with no null
unittest::AreEqualEx(L"0x0", flags::InterpretFlags(PROP_ID(PR_SUBMIT_FLAGS), 0));
unittest::AreEqualEx(
L"SUBMITFLAG_LOCKED", flags::InterpretFlags(PROP_ID(PR_SUBMIT_FLAGS), SUBMITFLAG_LOCKED));

// FLAG_ENTRY3RDBYTE and FLAG_ENTRY4THBYTE
unittest::AreEqualEx(
L"DTE_FLAG_REMOTE_VALID | Remote: DT_MAILUSER | Local: DT_SEC_DISTLIST",
flags::InterpretFlags(
PROP_ID(PR_DISPLAY_TYPE_EX),
DTE_FLAG_REMOTE_VALID | DTE_REMOTE(DT_ROOM) | DTE_LOCAL(DT_SEC_DISTLIST)));

// FLAG_ENTRYHIGHBYTES
unittest::AreEqualEx(
L"CPU: MAPIFORM_CPU_X86 | MAPIFORM_OS_WIN_95",
flags::InterpretFlags(
PROP_ID(PR_FORM_HOST_MAP), MAPIFORM_PLATFORM(MAPIFORM_CPU_X86, MAPIFORM_OS_WIN_95)));

// NON_PROP_FLAG_ENTRYLOWERNIBBLE
unittest::AreEqualEx(
L"BFLAGS_MASK_OUTLOOK | Type: BFLAGS_INTERNAL_MAILUSER | Home Fax",
flags::InterpretFlags(flagWABEntryIDType, BFLAGS_MASK_OUTLOOK | BFLAGS_INTERNAL_MAILUSER | 0x10));
}

TEST_METHOD(Test_AllFlagsToString)
{
unittest::AreEqualEx(L"", flags::AllFlagsToString(NULL, true));
unittest::AreEqualEx(L"", flags::AllFlagsToString(0xFFFFFFFF, true));
unittest::AreEqualEx(
L"0x10000000 MAPI_P1\r\n"
L"0x80000000 MAPI_SUBMITTED\r\n"
L"0x00000000 MAPI_ORIG\r\n"
L"0x00000001 MAPI_TO\r\n"
L"0x00000002 MAPI_CC\r\n"
L"0x00000003 MAPI_BCC",
flags::AllFlagsToString(PROP_ID(PR_RECIPIENT_TYPE), true));
unittest::AreEqualEx(
L"268435456 MAPI_P1\r\n"
L"-2147483648 MAPI_SUBMITTED\r\n"
L" 0 MAPI_ORIG\r\n"
L" 1 MAPI_TO\r\n"
L" 2 MAPI_CC\r\n"
L" 3 MAPI_BCC",
flags::AllFlagsToString(PROP_ID(PR_RECIPIENT_TYPE), false));
}
};
} // namespace flagtest
161 changes: 49 additions & 112 deletions core/interpret/flags.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,180 +24,117 @@ namespace flags
if (FlagArray[ulCurEntry].ulFlagName != ulFlagName) return L"";

// We've matched our flag name to the array - we SHOULD return a string at this point
auto bNeedSeparator = false;

auto flags = std::vector<std::wstring>{};
auto lTempValue = lFlagValue;
std::wstring szTempString;
for (; FlagArray[ulCurEntry].ulFlagName == ulFlagName; ulCurEntry++)
while (FlagArray[ulCurEntry].ulFlagName == ulFlagName)
{
if (flagFLAG == FlagArray[ulCurEntry].ulFlagType)
switch (FlagArray[ulCurEntry].ulFlagType)
{
case flagFLAG:
if (FlagArray[ulCurEntry].lFlagValue & lTempValue)
{
if (bNeedSeparator)
{
szTempString += L" | "; // STRING_OK
}

szTempString += FlagArray[ulCurEntry].lpszName;
flags.push_back(FlagArray[ulCurEntry].lpszName);
lTempValue &= ~FlagArray[ulCurEntry].lFlagValue;
bNeedSeparator = true;
}
}
else if (flagVALUE == FlagArray[ulCurEntry].ulFlagType)
{
break;
case flagVALUE:
if (FlagArray[ulCurEntry].lFlagValue == lTempValue)
{
if (bNeedSeparator)
{
szTempString += L" | "; // STRING_OK
}

szTempString += FlagArray[ulCurEntry].lpszName;
flags.push_back(FlagArray[ulCurEntry].lpszName);
lTempValue = 0;
bNeedSeparator = true;
}
}
else if (flagVALUEHIGHBYTES == FlagArray[ulCurEntry].ulFlagType)
{
break;
case flagVALUEHIGHBYTES:
if (FlagArray[ulCurEntry].lFlagValue == (lTempValue >> 16 & 0xFFFF))
{
if (bNeedSeparator)
{
szTempString += L" | "; // STRING_OK
}

szTempString += FlagArray[ulCurEntry].lpszName;
flags.push_back(FlagArray[ulCurEntry].lpszName);
lTempValue = lTempValue - (FlagArray[ulCurEntry].lFlagValue << 16);
bNeedSeparator = true;
}
}
else if (flagVALUE3RDBYTE == FlagArray[ulCurEntry].ulFlagType)
{
break;
case flagVALUE3RDBYTE:
if (FlagArray[ulCurEntry].lFlagValue == (lTempValue >> 8 & 0xFF))
{
if (bNeedSeparator)
{
szTempString += L" | "; // STRING_OK
}

szTempString += FlagArray[ulCurEntry].lpszName;
flags.push_back(FlagArray[ulCurEntry].lpszName);
lTempValue = lTempValue - (FlagArray[ulCurEntry].lFlagValue << 8);
bNeedSeparator = true;
}
}
else if (flagVALUE4THBYTE == FlagArray[ulCurEntry].ulFlagType)
{
break;
case flagVALUE4THBYTE:
if (FlagArray[ulCurEntry].lFlagValue == (lTempValue & 0xFF))
{
if (bNeedSeparator)
{
szTempString += L" | "; // STRING_OK
}

szTempString += FlagArray[ulCurEntry].lpszName;
flags.push_back(FlagArray[ulCurEntry].lpszName);
lTempValue = lTempValue - FlagArray[ulCurEntry].lFlagValue;
bNeedSeparator = true;
}
}
else if (flagVALUELOWERNIBBLE == FlagArray[ulCurEntry].ulFlagType)
{
break;
case flagVALUELOWERNIBBLE:
if (FlagArray[ulCurEntry].lFlagValue == (lTempValue & 0x0F))
{
if (bNeedSeparator)
{
szTempString += L" | "; // STRING_OK
}

szTempString += FlagArray[ulCurEntry].lpszName;
flags.push_back(FlagArray[ulCurEntry].lpszName);
lTempValue = lTempValue - FlagArray[ulCurEntry].lFlagValue;
bNeedSeparator = true;
}
}
else if (flagCLEARBITS == FlagArray[ulCurEntry].ulFlagType)
{
break;
case flagCLEARBITS:
// find any bits we need to clear
const auto lClearedBits = FlagArray[ulCurEntry].lFlagValue & lTempValue;
// report what we found
if (0 != lClearedBits)
if (lClearedBits != 0)
{
if (bNeedSeparator)
{
szTempString += L" | "; // STRING_OK
}

szTempString += strings::format(L"0x%X", lClearedBits); // STRING_OK
flags.push_back(strings::format(L"0x%X", lClearedBits)); // STRING_OK
// clear the bits out
lTempValue &= ~FlagArray[ulCurEntry].lFlagValue;
bNeedSeparator = true;
}
break;
}

ulCurEntry++;
}

// We know if we've found anything already because bNeedSeparator will be true
// If bNeedSeparator isn't true, we found nothing and need to tack on
// Otherwise, it's true, and we only tack if lTempValue still has something in it
if (!bNeedSeparator || lTempValue)
if (lTempValue || flags.empty())
{
if (bNeedSeparator)
{
szTempString += L" | "; // STRING_OK
}

szTempString += strings::format(L"0x%X", lTempValue); // STRING_OK
flags.push_back(strings::format(L"0x%X", lTempValue)); // STRING_OK
}

return szTempString;
return strings::join(flags, L" | ");
}

// Returns a list of all known flags/values for a flag name.
// For instance, for flagFuzzyLevel, would return:
// \r\n0x00000000 FL_FULLSTRING\r\n\
// 0x00000001 FL_SUBSTRING\r\n\
// 0x00000002 FL_PREFIX\r\n\
// 0x00010000 FL_IGNORECASE\r\n\
// 0x00020000 FL_IGNORENONSPACE\r\n\
// 0x00040000 FL_LOOSE
// 0x00000000 FL_FULLSTRING\r\n\
// 0x00000001 FL_SUBSTRING\r\n\
// 0x00000002 FL_PREFIX\r\n\
// 0x00010000 FL_IGNORECASE\r\n\
// 0x00020000 FL_IGNORENONSPACE\r\n\
// 0x00040000 FL_LOOSE
//
// Since the string is always appended to a prompt we include \r\n at the start
std::wstring AllFlagsToString(ULONG ulFlagName, bool bHex)
{
std::wstring szFlagString;
if (!ulFlagName) return szFlagString;
if (FlagArray.empty()) return szFlagString;
if (!ulFlagName) return L"";
if (FlagArray.empty()) return L"";

ULONG ulCurEntry = 0;
std::wstring szTempString;

while (ulCurEntry < FlagArray.size() && FlagArray[ulCurEntry].ulFlagName != ulFlagName)
{
ulCurEntry++;
}

if (FlagArray[ulCurEntry].ulFlagName != ulFlagName) return szFlagString;
if (ulCurEntry == FlagArray.size() || FlagArray[ulCurEntry].ulFlagName != ulFlagName) return L"";

// We've matched our flag name to the array - we SHOULD return a string at this point
for (; FlagArray[ulCurEntry].ulFlagName == ulFlagName; ulCurEntry++)
auto flags = std::vector<std::wstring>{};
while (FlagArray[ulCurEntry].ulFlagName == ulFlagName)
{
if (flagCLEARBITS == FlagArray[ulCurEntry].ulFlagType)
if (flagCLEARBITS != FlagArray[ulCurEntry].ulFlagType)
{
// keep going
}
else
{
if (bHex)
{
szFlagString += strings::formatmessage(
IDS_FLAGTOSTRINGHEX, FlagArray[ulCurEntry].lFlagValue, FlagArray[ulCurEntry].lpszName);
}
else
{
szFlagString += strings::formatmessage(
IDS_FLAGTOSTRINGDEC, FlagArray[ulCurEntry].lFlagValue, FlagArray[ulCurEntry].lpszName);
}
flags.push_back(strings::formatmessage(
bHex ? IDS_FLAGTOSTRINGHEX : IDS_FLAGTOSTRINGDEC,
FlagArray[ulCurEntry].lFlagValue,
FlagArray[ulCurEntry].lpszName));
}

ulCurEntry++;
}

return szFlagString;
return strings::join(flags, L"\r\n");
}
} // namespace flags
4 changes: 2 additions & 2 deletions core/res/MFCMapi.rc2
Original file line number Diff line number Diff line change
Expand Up @@ -1480,8 +1480,8 @@ IDS_SIDFLAGS "Flags: 0x%1!08X!"

IDS_INVALIDSD "This is not a valid security descriptor."

IDS_FLAGTOSTRINGHEX "\r\n0x%1!08X! %2!ws!"
IDS_FLAGTOSTRINGDEC "\r\n%1!5d! %2!ws!"
IDS_FLAGTOSTRINGHEX "0x%1!08X! %2!ws!"
IDS_FLAGTOSTRINGDEC "%1!5d! %2!ws!"

IDS_PTI8FORMATLABEL "I8: %1!X!-%2!08X!"
IDS_PTI8FORMAT "%1!X!-%2!08X!"
Expand Down

0 comments on commit 6d30b0e

Please sign in to comment.