Files
Mark Reid 50e7d5863d Fix CPU unit test failures on recent macOS ARM platforms (#1950)
* 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>
2024-03-20 00:52:15 -04:00

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;
}