testtools/micromock/inc/mockcallargument.h (251 lines of code) (raw):
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef MOCKCALLARGUMENT_H
#define MOCKCALLARGUMENT_H
#pragma once
#include "stdafx.h"
#include "mockvalue.h"
#include "mockcallargumentbase.h"
#include "micromockexception.h"
#include "stddef.h"
template<typename T>
class CMockCallArgument :
public CMockValue<T>, public CMockCallArgumentBase
{
public:
CMockCallArgument(_In_ T argValue, _In_ bool IsIgnoreable = true) :
CMockValue<T>(argValue),
m_Ignored(false)
{
m_IsIgnoreable = IsIgnoreable;
}
virtual ~CMockCallArgument()
{
for (size_t i = 0; i < m_BufferValidations.size(); i++)
{
void* buffer = m_BufferValidations[i].m_Buffer;
if (buffer != NULL)
{
free(buffer);
}
}
for (size_t i = 0; i < m_CopyOutArgumentBuffers.size(); i++)
{
void* buffer = m_CopyOutArgumentBuffers[i].m_Buffer;
if (buffer != NULL)
{
free(buffer);
}
}
}
// TODO: this should be fixed to be const
bool operator==(_In_ CMockCallArgument<T>& rhs)
{
bool result = true;
if (m_BufferValidations.size() > 0 &&
rhs.m_BufferValidations.size() == 0)
{
return rhs == *this;
}
if (rhs.m_BufferValidations.size() > 0)
{
UINT8* argBufferValidationMask = rhs.CreateBufferValidationMask();
if (NULL != argBufferValidationMask)
{
FormatArgumentBufferString(rhs.m_BufferValidations, *((UINT8**)&this->CMockValue<T>::m_Value));
rhs.FormatArgumentBufferString(rhs.m_BufferValidations, argBufferValidationMask);
free(argBufferValidationMask);
}
else
{
MOCK_THROW(CMicroMockException(MICROMOCK_EXCEPTION_ALLOCATION_FAILURE,
_T("Failed allocating buffer validation mask")));
}
result = result && ValidateArgumentBuffer(rhs.m_BufferValidations);
}
if (result &&
!m_Ignored &&
!rhs.m_Ignored)
{
// simply invoke the CMockValue == operator to take advantage of
// any specializations defined in tests
result = *((CMockValue<T>*)this) == (*(CMockValue<T>*)&rhs);
}
return result;
}
bool ValidateArgumentBuffer(_In_ const std::vector<BUFFER_ARGUMENT_DATA>& bufferValidations) const
{
bool result = true;
for (size_t i = 0; i < bufferValidations.size(); i++)
{
UINT8* argBuffer = *((UINT8**)&this->CMockValue<T>::m_Value);
if (NULL == argBuffer)
{
result = false;
break;
}
result &= (memcmp(argBuffer + bufferValidations[i].m_Offset, bufferValidations[i].m_Buffer,
bufferValidations[i].m_ByteCount) == 0);
}
return result;
}
virtual std::tstring ToString() const
{
if (m_ArgumentAsString.length() > 0)
{
return m_ArgumentAsString;
}
else
{
return this->CMockValue<T>::ToString();
}
}
virtual void CopyOutArgumentDataFrom(_In_ const CMockCallArgumentBase* sourceMockCallArgument)
{
const CMockCallArgument<T>* argument = (const CMockCallArgument<T>*)(sourceMockCallArgument);
if (NULL != argument)
{
for (size_t i = 0; i < argument->m_CopyOutArgumentBuffers.size(); i++)
{
UINT8* argBuffer = *((UINT8**)&this->m_OriginalValue);
if (NULL != argBuffer)
{
memmove(argBuffer + argument->m_CopyOutArgumentBuffers[i].m_Offset,
argument->m_CopyOutArgumentBuffers[i].m_Buffer,
argument->m_CopyOutArgumentBuffers[i].m_ByteCount);
}
}
}
}
virtual bool EqualTo(_In_ const CMockCallArgumentBase* right)
{
return (*this == *(CMockCallArgument<T>*)(const CMockCallArgument<T>*)(right));
}
void AddBufferValidation(const void* expectedBuffer, _In_ size_t bytesToValidate, _In_ size_t byteOffset = 0)
{
if ((NULL != expectedBuffer) &&
(bytesToValidate > 0))
{
for (size_t i = byteOffset; i < byteOffset + bytesToValidate; i++)
{
UINT8 argBufferValidateByte = 0;
if ((GetArgBufferValidationExpectedByte(i, &argBufferValidateByte) == true) &&
(argBufferValidateByte != ((UINT8*)expectedBuffer)[i - byteOffset]))
{
MOCK_THROW(CMicroMockException(MICROMOCK_EXCEPTION_INVALID_VALIDATE_BUFFERS,
_T("Validate argument buffer specified 2 times for the same byte")));
}
}
BUFFER_ARGUMENT_DATA argumentValidationData;
argumentValidationData.m_Buffer = malloc(bytesToValidate);
if (NULL != argumentValidationData.m_Buffer)
{
(void)memcpy(argumentValidationData.m_Buffer, expectedBuffer, bytesToValidate);
}
argumentValidationData.m_ByteCount = bytesToValidate;
argumentValidationData.m_Offset = byteOffset;
m_BufferValidations.push_back(argumentValidationData);
// ignore the argument
SetIgnored(true);
}
else
{
MOCK_THROW(CMicroMockException(MICROMOCK_EXCEPTION_INVALID_VALIDATE_BUFFERS,
_T("Invalid arguments for AddBufferValidation.")));
}
}
virtual void AddCopyOutArgumentBuffer(const void* injectedBuffer, _In_ size_t bytesToCopy, _In_ size_t byteOffset = 0)
{
BUFFER_ARGUMENT_DATA outArgumentCopyData;
outArgumentCopyData.m_Buffer = malloc(bytesToCopy);
if (NULL != outArgumentCopyData.m_Buffer)
{
(void)memcpy(outArgumentCopyData.m_Buffer, injectedBuffer, bytesToCopy);
}
outArgumentCopyData.m_ByteCount = bytesToCopy;
outArgumentCopyData.m_Offset = byteOffset;
m_CopyOutArgumentBuffers.push_back(outArgumentCopyData);
// ignore the argument
SetIgnored(true);
}
virtual void SetIgnored(_In_ bool ignored)
{
if (m_IsIgnoreable == true)
{
m_Ignored = ignored;
}
}
private:
void FormatArgumentBufferString(_In_ std::vector<BUFFER_ARGUMENT_DATA>& bufferValidations, _In_ const UINT8* buffer)
{
size_t pos = 0;
std::tostringstream strStream;
strStream << std::hex << std::uppercase;
if (NULL != buffer)
{
sort(bufferValidations.begin(), bufferValidations.end());
strStream << _T("[");
for (size_t i = 0; i < bufferValidations.size(); i++)
{
// fill with ".."
while (pos < bufferValidations[i].m_Offset)
{
if (pos > 0)
{
strStream << _T(" ");
}
strStream << _T("..");
pos++;
}
// put the actual bytes in
while (pos < bufferValidations[i].m_ByteCount + bufferValidations[i].m_Offset)
{
if (pos > 0)
{
strStream << _T(" ");
}
strStream << std::setfill(_T('0')) << std::setw(2) << (unsigned int)buffer[pos];
pos++;
}
}
strStream << _T("]");
m_ArgumentAsString = strStream.str();
}
else
{
MOCK_THROW(CMicroMockException(MICROMOCK_EXCEPTION_INVALID_ARGUMENT,
_T("NULL buffer argument attempted to be used for argument validation.")));
}
}
bool GetArgBufferValidationExpectedByte(_In_ size_t pos, UINT8* buffer) const
{
bool result = false;
for (size_t i = 0; i < m_BufferValidations.size(); i++)
{
if ((m_BufferValidations[i].m_Offset <= pos) &&
(pos < m_BufferValidations[i].m_Offset + m_BufferValidations[i].m_ByteCount))
{
*buffer = ((UINT8*)m_BufferValidations[i].m_Buffer)[pos - m_BufferValidations[i].m_Offset];
result = true;
}
}
return result;
}
UINT8* CreateBufferValidationMask()
{
UINT8* result = NULL;
if (m_BufferValidations.size() > 0)
{
size_t bufferSize;
sort(m_BufferValidations.begin(), m_BufferValidations.end());
bufferSize = m_BufferValidations[m_BufferValidations.size() - 1].m_Offset + m_BufferValidations[m_BufferValidations.size() - 1].m_ByteCount;
result = (UINT8*)malloc(bufferSize);
if (NULL != result)
{
for (size_t i = 0; i < m_BufferValidations.size(); i++)
{
(void)memcpy(result + m_BufferValidations[i].m_Offset,
m_BufferValidations[i].m_Buffer, m_BufferValidations[i].m_ByteCount);
}
}
}
return result;
}
std::vector<BUFFER_ARGUMENT_DATA> m_BufferValidations;
std::vector<BUFFER_ARGUMENT_DATA> m_CopyOutArgumentBuffers;
std::tstring m_ArgumentAsString;
bool m_Ignored;
bool m_IsIgnoreable; /*used for "time is never ignoreable"*/
};
#endif // MOCKCALLARGUMENT_H