* Fix CPUProcessor/optimizations on arm64 Arm64 has fma instructions that can lead to different results Break the manual computaion into multiple steps Signed-off-by: Mark Reid <mindmark@gmail.com> * Compare strings by float value Signed-off-by: Mark Reid <mindmark@gmail.com> * Enable macarm runners by default Signed-off-by: Mark Reid <mindmark@gmail.com> * Move mac arm runner to main workflow Signed-off-by: Mark Reid <mindmark@gmail.com> * fix rosetta typo Signed-off-by: Mark Reid <mindmark@gmail.com> * move StringFloatVecClose to testutils Signed-off-by: Mark Reid <mindmark@gmail.com> --------- Signed-off-by: Mark Reid <mindmark@gmail.com>
205 lines
5.4 KiB
C++
205 lines
5.4 KiB
C++
// SPDX-License-Identifier: BSD-3-Clause
|
|
// Copyright Contributors to the OpenColorIO Project.
|
|
|
|
|
|
#if defined(_WIN32) && !defined(NDEBUG)
|
|
#include <crtdbg.h>
|
|
#include <ctype.h>
|
|
#endif
|
|
|
|
#include <cstring>
|
|
|
|
#include "apputils/argparse.h"
|
|
#include "UnitTest.h"
|
|
#include "utils/NumberUtils.h"
|
|
#include "utils/StringUtils.h"
|
|
|
|
namespace OCIO = OCIO_NAMESPACE;
|
|
|
|
UnitTests & GetUnitTests()
|
|
{
|
|
static UnitTests oiio_unit_tests;
|
|
return oiio_unit_tests;
|
|
}
|
|
|
|
static bool StringVecToFloatVec(std::vector<float> &floatArray, const StringUtils::StringVec &lineParts)
|
|
{
|
|
floatArray.resize(lineParts.size());
|
|
|
|
for(unsigned int i=0; i<lineParts.size(); i++)
|
|
{
|
|
float x = NAN;
|
|
const char *str = lineParts[i].c_str();
|
|
const auto result = OCIO::NumberUtils::from_chars(str, str + lineParts[i].size(), x);
|
|
if (result.ec != std::errc())
|
|
{
|
|
return false;
|
|
}
|
|
floatArray[i] = x;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool StringFloatVecClose(std::string value, std::string expected, float eps)
|
|
{
|
|
std::vector<float> a;
|
|
std::vector<float> b;
|
|
if (StringVecToFloatVec(a, StringUtils::SplitByWhiteSpaces(value)) &&
|
|
StringVecToFloatVec(b, StringUtils::SplitByWhiteSpaces(expected)))
|
|
{
|
|
if (a.size() == b.size())
|
|
{
|
|
for(unsigned int i = 0; i < a.size(); i++)
|
|
{
|
|
if (std::abs(a[i] - b[i]) >= eps)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
int unit_test_failures{ 0 };
|
|
|
|
int UnitTestMain(int argc, const char ** argv)
|
|
{
|
|
|
|
#if !defined(NDEBUG) && defined(_WIN32)
|
|
// Disable the 'assert' dialog box in debug mode.
|
|
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);
|
|
#endif
|
|
|
|
bool printHelp = false;
|
|
bool stopOnFirstError = false;
|
|
|
|
// Note that empty strings mean to run all the unit tests.
|
|
std::string filter, utestGroupAllowed, utestNameAllowed;
|
|
|
|
ArgParse ap;
|
|
ap.options("\nCommand line arguments:\n",
|
|
"--help", &printHelp, "Print help message",
|
|
"--stop_on_error", &stopOnFirstError, "Stop on the first error",
|
|
"--run_only %s", &filter, "Run only some unit tests\n"
|
|
"\tex: --run_only \"FileRules/clone\"\n"
|
|
"\tex: --run_only FileRules i.e. \"FileRules/*\"\n"
|
|
"\tex: --run_only /clone i.e. \"*/clone\"\n",
|
|
nullptr);
|
|
|
|
if (ap.parse(argc, argv) < 0)
|
|
{
|
|
std::cerr << ap.geterror() << std::endl;
|
|
ap.usage();
|
|
return 1;
|
|
}
|
|
|
|
if (printHelp)
|
|
{
|
|
ap.usage();
|
|
return 1;
|
|
}
|
|
|
|
if (!filter.empty())
|
|
{
|
|
const std::vector<std::string> results = StringUtils::Split(filter, '/');
|
|
if (results.size() >= 1)
|
|
{
|
|
utestGroupAllowed = StringUtils::Lower(StringUtils::Trim(results[0]));
|
|
if (results.size() >= 2)
|
|
{
|
|
utestNameAllowed = StringUtils::Lower(StringUtils::Trim(results[1]));
|
|
|
|
if (results.size() >= 3)
|
|
{
|
|
std::cerr << "Invalid value for the argument '--run_only'." << std::endl;
|
|
ap.usage();
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int unit_test_failed = 0;
|
|
|
|
const size_t numTests = GetUnitTests().size();
|
|
for(size_t index = 0; index < numTests; ++index)
|
|
{
|
|
const std::string utestGroup = GetUnitTests()[index]->group;
|
|
const std::string utestName = GetUnitTests()[index]->name;
|
|
|
|
bool utestAllowed = true;
|
|
|
|
if (!utestGroupAllowed.empty() && StringUtils::Lower(utestGroup)!=utestGroupAllowed)
|
|
{
|
|
utestAllowed = false;
|
|
}
|
|
|
|
if (!utestNameAllowed.empty() && StringUtils::Lower(utestName)!=utestNameAllowed)
|
|
{
|
|
utestAllowed = false;
|
|
}
|
|
|
|
if (!utestAllowed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const int _tmp = unit_test_failures;
|
|
|
|
try
|
|
{
|
|
GetUnitTests()[index]->function();
|
|
}
|
|
catch(std::exception & ex)
|
|
{
|
|
std::cerr << "\nFAILED: " << ex.what() << "." << std::endl;
|
|
++unit_test_failures;
|
|
}
|
|
catch(...)
|
|
{
|
|
std::cerr << "\nFAILED: Unexpected error." << std::endl;
|
|
++unit_test_failures;
|
|
}
|
|
|
|
const bool passing = (_tmp == unit_test_failures);
|
|
if (!passing)
|
|
{
|
|
++unit_test_failed;
|
|
}
|
|
|
|
std::string name(utestGroup);
|
|
name += " / " + utestName;
|
|
|
|
constexpr size_t maxCharToDisplay = 59;
|
|
if (name.size() > maxCharToDisplay)
|
|
{
|
|
name.resize(maxCharToDisplay);
|
|
}
|
|
|
|
std::cerr << "[" << std::right << std::setw(4)
|
|
<< (index+1) << "/" << numTests << "] ["
|
|
<< std::left << std::setw(maxCharToDisplay+1)
|
|
<< name << "] - "
|
|
<< (passing ? "PASSED" : "FAILED")
|
|
<< std::endl;
|
|
|
|
if (stopOnFirstError && !passing)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
std::cerr << "\n\n" << unit_test_failed << " tests failed with "
|
|
<< unit_test_failures << " errors.\n\n";
|
|
|
|
GetUnitTests().clear();
|
|
|
|
|
|
return unit_test_failures;
|
|
}
|