How To Use
 All Modules Pages
C++ Example: comparing two XML files (XmlCheck::Compare)
#include "stdafx.h"
#include "MyTestCommands.h"
#include <pugixml.hpp>
#include <TestToolBox\TestToolBox.h>
#include <boost/test/unit_test.hpp>
namespace TTB = TestToolBox;
// This is an example of a test case file without any test suite
TTB_BOOST_TEST_CASE(Compare)
{
/// [xmlcheck compare sample]
static const std::string SHORT_DEMO_A =
R"(<SomeNode id="3" attrA="1.2">
<SubNodeB>0.2</SubNodeB>
</SomeNode>
)";
static const std::string SHORT_DEMO_B =
R"(<SomeNode id="3" attrA="1.8">
<SubNodeB>0.4</SubNodeB>
</SomeNode>
)";
/// [xmlcheck compare sample]
static const std::string SHORT_DEMO_C =
R"(<SomeNode id="3" attrA="1.8">
<SubNodeB>0.4</SubNodeB>
<UnexpectedNode>some data</UnexpectedNode>
</SomeNode>
)";
/// [xmlcheck compare read sample data]
pugi::xml_document docA;
pugi::xml_node rootA = TTB::TheXmlCheck()->ReadFromString(docA, SHORT_DEMO_A);
pugi::xml_document docB;
pugi::xml_node rootB = TTB::TheXmlCheck()->ReadFromString(docB, SHORT_DEMO_B);
/// [xmlcheck compare read sample data]
pugi::xml_document docC;
pugi::xml_node rootC = TTB::TheXmlCheck()->ReadFromString(docC, SHORT_DEMO_C);
/// [xmlcheck compare full]
TTB_INFO("Compare A and B, check all entries of xml trees");
bool equal = TTB::TheXmlCheck()->Compare(docA, docB);
TTB_EXP("Difference detected");
TTB_EXP("lhs: attrA: 1.2(path: /SomeNode/attrA, pos: 1)");
TTB_EXP("rhs: attrA: 1.8(path: /SomeNode/attrA, pos: 1)");
// Optional check of return value:
TTB_CHECK_EQUAL(equal, false); // i.e. difference found
/// [xmlcheck compare full]
// Optional check of compare result string (useful when XmlCheck is not connected to test events)
std::string compareResult = TTB::TheXmlCheck()->GetCompareResult();
TTB_ACT_S(compareResult);
TTB_EXP("Difference detected\n"
"lhs: attrA: 1.2(path: /SomeNode/attrA, pos: 1)\n"
"rhs: attrA: 1.8(path: /SomeNode/attrA, pos: 1)");
/// [xmlcheck compare ignore attribute]
TTB_INFO("\nCompare A and B, ignore attribute attrA");
TTB::TheXmlCheck()->IgnoreAttribute("attrA");
equal = TTB::TheXmlCheck()->Compare(docA, docB);
TTB_CHECK_EQUAL(equal, false); // i.e. difference found
TTB_EXP("Difference detected");
TTB_EXP("lhs: 0.2(path: /SomeNode/SubNodeB/, pos: 42)");
TTB_EXP("rhs: 0.4(path: /SomeNode/SubNodeB/, pos: 42)");
/// [xmlcheck compare ignore attribute]
/// [xmlcheck compare ignore attribute and node]
TTB_INFO("\nCompare A and B, ignore attribute attrA and node SubNodeB");
TTB::TheXmlCheck()->IgnoreNode("SubNodeB");
equal = TTB::TheXmlCheck()->Compare(docA, docB);
TTB_CHECK_EQUAL(equal, true); // i.e. no difference found
/// [xmlcheck compare ignore attribute and node]
TTB_INFO("\nCompare A and C, ignore attribute attrA and node SubNodeB");
equal = TTB::TheXmlCheck()->Compare(docA, docC);
TTB_CHECK_EQUAL(equal, false);
TTB_EXP("Difference detected, rhs has more data");
TTB_EXP("rhs: UnexpectedNode (path: /SomeNode/UnexpectedNode, pos: 66)");
//-------------------------------------------------------------------------
}}
//-----------------------------------------------------------------------------
//! \brief Method to check if a value is close to another value
//! It is a common problem and solved over and over again, that doubles and floats
//! must not be compared directly. Here is a function that helps determining if
//! if a value is close to another.
//! T has to be of type float or double etc.
//! \param in_val1 first value that should be compared
//! \param in_val2 second value that should be compared
template<typename T>
bool IsCloseToEqual(T in_val1, T in_val2, T in_eps)
{
return (std::abs(in_val1 - in_val2) < in_eps);
}
//! \brief Method to check if a value is close to another value
//! It is a common problem and solved over and over again, that doubles and floats
//! must not be compared directly. Here is a function that helps determining if
//! if a value is close to another.
//! T has to be of type float or double etc.
template<typename T>
bool IsCloseToEqual(T in_val1, T in_val2)
{
T eps = 3.0f * std::numeric_limits<T>::epsilon();
return IsCloseToEqual(in_val1, in_val2, eps);
}
static double const gPRECISION_DOUBLE = 1.0e-8;
static float const gPRECISION_FLOAT = 1.0e-4f;
//! \brief Methods to check if a value is close to another value
//! Unlike the method IsCloseToEqual() it considers the absolute values of the
//! compared data.
//!
//! \param val1 first value that should be compared
//! \param val2 second value that should be compared
//! \param prec precision
inline bool IsApprox(double const val1, double const val2, double const prec = gPRECISION_DOUBLE)
{
return
std::abs(val1 - val2) <=
std::max(std::min(std::abs(val1), std::abs(val2)), 1.0) * prec;
}
inline bool IsApprox(float const val1, float const val2, float const prec = gPRECISION_FLOAT)
{
return
std::abs(val1 - val2) <=
std::max(std::min(std::abs(val1), std::abs(val2)), 1.0f) * prec;
}
inline bool IsSimilar(double const val1, double const val2, double const prec = gPRECISION_DOUBLE)
{
double sum = std::abs(val1) + std::abs(val2);
if (sum < 1000.0 * std::numeric_limits<double>::min())
return true;
return std::abs(val1 - val2) / sum <= prec;
}
inline bool IsSimilar(float const val1, float const val2, float const prec = gPRECISION_FLOAT)
{
float sum = std::abs(val1) + std::abs(val2);
if (sum < 1000.0 * std::numeric_limits<float>::min())
return true;
return std::abs(val1 - val2) / sum <= prec;
}
//-----------------------------------------------------------------------------
#define TTB_IS_CLOSE_TO_EQUAL(left,right) \
{ \
bool errorFound = !IsCloseToEqual(left,right); \
TestToolBox::TestEvents::Get()->DisplayCheckInfo( __FILE__,__LINE__, "IsCloseToEqual", errorFound, #left, ::TestToolBox::ToString(left), #right, ::TestToolBox::ToString(right), "~equal~", typeid(left).name()); \
}
#define TTB_IS_CLOSE_TO_EQUAL_EPS(left,right,eps) \
{ \
bool errorFound = !IsCloseToEqual(left,right,eps); \
TestToolBox::TestEvents::Get()->DisplayCheckInfo( __FILE__,__LINE__, "IsCloseToEqual", errorFound, #left, ::TestToolBox::ToString(left), #right, ::TestToolBox::ToString(right), "~equal~", std::string(typeid(left).name()) + ", eps=" + #eps); \
}
#define TTB_IS_APPROX(left,right) \
{ \
bool errorFound = !IsApprox(left,right); \
TestToolBox::TestEvents::Get()->DisplayCheckInfo( __FILE__,__LINE__, "IsApprox", errorFound, #left, ::TestToolBox::ToString(left), #right, ::TestToolBox::ToString(right), "~approx~", typeid(left).name()); \
}
#define TTB_IS_APPROX_PREC(left,right, prec) \
{ \
bool errorFound = !IsApprox(left,right,prec); \
TestToolBox::TestEvents::Get()->DisplayCheckInfo( __FILE__,__LINE__, "IsApprox", errorFound, #left, ::TestToolBox::ToString(left), #right, ::TestToolBox::ToString(right), "~approx~", std::string(typeid(left).name()) + ", prec=" + #prec); \
}
#define TTB_IS_SIMILAR(left,right) \
{ \
bool errorFound = !IsSimilar(left,right); \
TestToolBox::TestEvents::Get()->DisplayCheckInfo( __FILE__,__LINE__, "IsSimilar", errorFound, #left, ::TestToolBox::ToString(left), #right, ::TestToolBox::ToString(right), "~similar~", typeid(left).name()); \
}
#define TTB_IS_SIMILAR_PREC(left,right, prec) \
{ \
bool errorFound = !IsSimilar(left,right,prec); \
TestToolBox::TestEvents::Get()->DisplayCheckInfo( __FILE__,__LINE__, "IsSimilar", errorFound, #left, ::TestToolBox::ToString(left), #right, ::TestToolBox::ToString(right), "~similar~", std::string(typeid(left).name()) + ", prec=" + #prec); \
}
//-----------------------------------------------------------------------------
TTB_BOOST_TEST_CASE(CompareDouble)
{
double smallNum =1.234e-6;
double other = 1e-19;
TTB::TheTestEvents()->SetDetailedCheckLog(true); // log also successful checks
TTB::TheTestEvents()->SetDoublePrecision(4);
TTB_CHECK_EQUAL(smallNum, other);
if (TTB::TheEnvironment()->IsExistingCommandLineOption("-showErrors"))
{
TTB::TheTestEvents()->SetDoublePrecision(8);
TTB_CHECK_EQUAL(smallNum, other);
}
// Using project specific compare functions
{
float smallNum = 1.234567f;
float other = 1.234555f;
if (TTB::TheEnvironment()->IsExistingCommandLineOption("-showErrors"))
{
TTB_IS_CLOSE_TO_EQUAL(smallNum, other);
}
TTB_IS_CLOSE_TO_EQUAL_EPS(smallNum, other, 0.0004f);
TTB_IS_APPROX(smallNum, other);
TTB_IS_APPROX_PREC(smallNum, other, 0.001f);
TTB_IS_SIMILAR(smallNum, other);
TTB_IS_SIMILAR_PREC(smallNum, other, 0.001f);
}
}}
TTB_BOOST_TEST_CASE(CompareDoubleWithVariousMethods)
{
double smallNum = 1.234e-6;
double other = 1e-19;
TTB::TheTestEvents()->SetDetailedCheckLog(true); // log also successful checks
if (TTB::TheEnvironment()->IsExistingCommandLineOption("-showErrors"))
{
TTB_IS_CLOSE_TO_EQUAL(smallNum, other);
TTB_IS_APPROX(smallNum, other);
TTB_IS_SIMILAR(smallNum, other);
TTB_IS_SIMILAR_PREC(smallNum, other, 0.001);
}
TTB_IS_CLOSE_TO_EQUAL_EPS(smallNum, other, 0.0004);
TTB_IS_APPROX_PREC(smallNum, other, 0.001);
}}
TTB_BOOST_TEST_CASE(CompareFlloaWithVariousMethods)
{
float smallNum = 1.234567f;
float other = 1.234555f;
TTB::TheTestEvents()->SetDetailedCheckLog(true); // log also successful checks
if (TTB::TheEnvironment()->IsExistingCommandLineOption("-showErrors"))
{
TTB_IS_CLOSE_TO_EQUAL(smallNum, other);
TTB_IS_SIMILAR_PREC(smallNum, other, 0.000001f);
}
TTB_IS_CLOSE_TO_EQUAL_EPS(smallNum, other, 0.0004f);
TTB_IS_APPROX(smallNum, other);
TTB_IS_APPROX_PREC(smallNum, other, 0.001f);
TTB_IS_SIMILAR(smallNum, other);
TTB_IS_SIMILAR_PREC(smallNum, other, 0.001f);
}}