vtk-m/vtkm/rendering/testing/Testing.h
2021-03-24 22:57:32 +00:00

209 lines
7.8 KiB
C++

//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#ifndef vtk_m_rendering_testing_Testing_h
#define vtk_m_rendering_testing_Testing_h
#include <vtkm/cont/DataSet.h>
#include <vtkm/cont/Error.h>
#include <vtkm/cont/Logging.h>
#include <vtkm/cont/RuntimeDeviceTracker.h>
#include <vtkm/cont/testing/Testing.h>
#include <vtkm/filter/ImageDifference.h>
#include <vtkm/internal/Configure.h>
#include <vtkm/io/FileUtils.h>
#include <vtkm/io/ImageUtils.h>
#include <fstream>
#include <sstream>
#include <vector>
/// \brief Tests multiple image files against a provided view pointer for differences
///
/// This function tests multiple files provided via fileNames against the rendered
/// canvas generated by the provided view using the ImageDifference filter. If one
/// of the provided images is within the error threshold for the image difference
/// this function will return true. Otherwise the view is too different from the images
/// and this will return false with corresponding error messages.
///
/// This function will generate an image if the provided file is missing. If a file is
/// missing the image will be generated for that file and the test will continue.
///
template <typename ViewType>
inline TestEqualResult test_equal_images(
const std::shared_ptr<ViewType> view,
const std::vector<std::string>& fileNames,
const vtkm::IdComponent& averageRadius = 0,
const vtkm::IdComponent& pixelShiftRadius = 0,
const vtkm::FloatDefault& allowedPixelErrorRatio = 0.00025f,
const vtkm::FloatDefault& threshold = 0.05f,
const bool& writeDiff = true,
const bool& returnOnPass = true)
{
vtkm::cont::ScopedRuntimeDeviceTracker runtime(vtkm::cont::DeviceAdapterTagAny{});
TestEqualResult testResults;
if (fileNames.empty())
{
testResults.PushMessage("No valid image file names were provided");
return testResults;
}
view->Paint();
view->GetCanvas().RefreshColorBuffer();
const std::string testImageName = vtkm::cont::testing::Testing::WriteDirPath(
vtkm::io::PrefixStringToFilename(fileNames[0], "test-"));
vtkm::io::WriteImageFile(view->GetCanvas().GetDataSet(), testImageName, "color");
for (const auto& fileName : fileNames)
{
VTKM_LOG_S(vtkm::cont::LogLevel::Info, "testing image file: " << fileName);
TestEqualResult imageResult;
vtkm::cont::DataSet imageDataSet;
try
{
const std::string testImagePath = vtkm::cont::testing::Testing::RegressionImagePath(fileName);
imageDataSet = vtkm::io::ReadImageFile(testImagePath, "baseline-image");
}
catch (const vtkm::cont::ErrorExecution& error)
{
VTKM_LOG_S(vtkm::cont::LogLevel::Error, error.GetMessage());
imageResult.PushMessage(error.GetMessage());
const std::string outputImagePath = vtkm::cont::testing::Testing::WriteDirPath(fileName);
vtkm::io::WriteImageFile(view->GetCanvas().GetDataSet(), outputImagePath, "color");
imageResult.PushMessage("File '" + fileName +
"' did not exist but has been generated here: " + outputImagePath);
testResults.PushMessage(imageResult.GetMergedMessage());
continue;
}
catch (const vtkm::cont::ErrorBadValue& error)
{
VTKM_LOG_S(vtkm::cont::LogLevel::Error, error.GetMessage());
imageResult.PushMessage(error.GetMessage());
imageResult.PushMessage("Unsupported file type for image: " + fileName);
testResults.PushMessage(imageResult.GetMergedMessage());
continue;
}
imageDataSet.AddPointField("generated-image", view->GetCanvas().GetColorBuffer());
vtkm::filter::ImageDifference filter;
filter.SetPrimaryField("baseline-image");
filter.SetSecondaryField("generated-image");
filter.SetAverageRadius(averageRadius);
filter.SetPixelShiftRadius(pixelShiftRadius);
filter.SetAllowedPixelErrorRatio(allowedPixelErrorRatio);
filter.SetPixelDiffThreshold(threshold);
auto resultDataSet = filter.Execute(imageDataSet);
if (!filter.GetImageDiffWithinThreshold())
{
imageResult.PushMessage("Image Difference was not within the expected threshold for: " +
fileName);
}
if (writeDiff && resultDataSet.HasPointField("image-diff"))
{
const std::string diffName = vtkm::cont::testing::Testing::WriteDirPath(
vtkm::io::PrefixStringToFilename(fileName, "diff-"));
vtkm::io::WriteImageFile(resultDataSet, diffName, "image-diff");
}
if (imageResult && returnOnPass)
{
VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Test passed for image " << fileName);
if (!testResults)
{
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
"Other image errors: " << testResults.GetMergedMessage());
}
return imageResult;
}
testResults.PushMessage(imageResult.GetMergedMessage());
}
VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Test Results: " << testResults.GetMergedMessage());
return testResults;
}
template <typename ViewType>
inline TestEqualResult test_equal_images(
const std::shared_ptr<ViewType> view,
const std::string& fileName,
const vtkm::IdComponent& averageRadius = 0,
const vtkm::IdComponent& pixelShiftRadius = 0,
const vtkm::FloatDefault& allowedPixelErrorRatio = 0.00025f,
const vtkm::FloatDefault& threshold = 0.05f,
const bool& writeDiff = true)
{
std::vector<std::string> fileNames{ fileName };
return test_equal_images(
view, fileNames, averageRadius, pixelShiftRadius, allowedPixelErrorRatio, threshold, writeDiff);
}
/// \brief Tests multiple images in the format `fileName#.png`
///
/// Using the provided fileName, it splits the extension and prefix into two
/// components and searches through the regression image file path directory
/// for all matching file names with a number specifier starting at 0 incrementing
/// by one.
///
/// For example, if a file `foo.png` is provied, this function will first look
/// for a file foo0.png. If it exists, it will then look for foo1.png and so on
/// until it cannot find a file with a specific number.
///
/// test_equal_images will then be called on the vector of valid fileNames
///
template <typename ViewType>
inline TestEqualResult test_equal_images_matching_name(
const std::shared_ptr<ViewType> view,
const std::string& fileName,
const vtkm::IdComponent& averageRadius = 0,
const vtkm::IdComponent& pixelShiftRadius = 0,
const vtkm::FloatDefault& allowedPixelErrorRatio = 0.00025f,
const vtkm::FloatDefault& threshold = 0.05f,
const bool& writeDiff = true,
const bool& returnOnPass = true)
{
std::vector<std::string> fileNames;
auto found = fileName.rfind(".");
auto prefix = fileName.substr(0, found);
auto suffix = fileName.substr(found, fileName.length());
for (int i = 0;; i++)
{
std::ostringstream fileNameStream;
fileNameStream << prefix << i << suffix;
std::ifstream check(
vtkm::cont::testing::Testing::RegressionImagePath(fileNameStream.str()).c_str());
if (!check.good())
{
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
"Stopped filename search at: " << fileNameStream.str() << ", beginning testing");
break;
}
fileNames.emplace_back(fileNameStream.str());
}
return test_equal_images(view,
fileNames,
averageRadius,
pixelShiftRadius,
allowedPixelErrorRatio,
threshold,
writeDiff,
returnOnPass);
}
#endif // vtk_m_rendering_testing_Testing_h