//============================================================================ // 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. // // Copyright 2014 Sandia Corporation. // Copyright 2014 UT-Battelle, LLC. // Copyright 2014 Los Alamos National Security. // // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, // the U.S. Government retains certain rights in this software. // // Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National // Laboratory (LANL), the U.S. Government retains certain rights in // this software. //============================================================================ #ifndef vtk_m_testing_Testing_h #define vtk_m_testing_Testing_h #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Try to enforce using the correct testing version. (Those that include the // control environment have more possible exceptions.) This is not guaranteed // to work. To make it more likely, place the Testing.h include last. #ifdef vtk_m_cont_Error_h #ifndef vtk_m_cont_testing_Testing_h #error Use vtkm::cont::testing::Testing instead of vtkm::testing::Testing. #else #define VTKM_TESTING_IN_CONT #endif #endif /// \def VTKM_TEST_ASSERT(condition, message) /// /// Asserts a condition for a test to pass. A passing condition is when \a /// condition resolves to true. If \a condition is false, then the test is /// aborted and failure is returned. #define VTKM_TEST_ASSERT(condition, message) \ ::vtkm::testing::Testing::Assert( \ condition, __FILE__, __LINE__, message, #condition) /// \def VTKM_TEST_FAIL(message) /// /// Causes a test to fail with the given \a message. #define VTKM_TEST_FAIL(message) \ throw ::vtkm::testing::Testing::TestFailure(__FILE__, __LINE__, message) namespace vtkm { namespace testing { // If you get an error about this class definition being incomplete, it means // that you tried to get the name of a type that is not specified. You can // either not use that type, not try to get the string name, or add it to the // list. template struct TypeName; #define VTK_M_BASIC_TYPE(type) \ template<> struct TypeName { \ static std::string Name() { return #type; } \ } \ VTK_M_BASIC_TYPE(vtkm::Float32); VTK_M_BASIC_TYPE(vtkm::Float64); VTK_M_BASIC_TYPE(vtkm::Int8); VTK_M_BASIC_TYPE(vtkm::UInt8); VTK_M_BASIC_TYPE(vtkm::Int16); VTK_M_BASIC_TYPE(vtkm::UInt16); VTK_M_BASIC_TYPE(vtkm::Int32); VTK_M_BASIC_TYPE(vtkm::UInt32); VTK_M_BASIC_TYPE(vtkm::Int64); VTK_M_BASIC_TYPE(vtkm::UInt64); #undef VTK_M_BASIC_TYPE template struct TypeName > { static std::string Name() { std::stringstream stream; stream << "vtkm::Vec< " << TypeName::Name() << ", " << Size << " >"; return stream.str(); } }; template struct TypeName > { static std::string Name() { std::stringstream stream; stream << "vtkm::Pair< " << TypeName::Name() << ", " << TypeName::Name() << " >"; return stream.str(); } }; namespace detail { template struct InternalTryCellShape { template void operator()(const FunctionType &function) const { this->PrintAndInvoke(function, typename vtkm::CellShapeIdToTag::valid()); InternalTryCellShape()(function); } private: template void PrintAndInvoke(const FunctionType &function, std::true_type) const { typedef typename vtkm::CellShapeIdToTag::Tag CellShapeTag; std::cout << "*** " << vtkm::GetCellShapeName(CellShapeTag()) << " ***************" << std::endl; function(CellShapeTag()); } template void PrintAndInvoke(const FunctionType &, std::false_type) const { // Not a valid cell shape. Do nothing. } }; template<> struct InternalTryCellShape { template void operator()(const FunctionType &) const { // Done processing cell sets. Do nothing and return. } }; } // namespace detail struct Testing { public: class TestFailure { public: VTKM_CONT TestFailure(const std::string &file, vtkm::Id line, const std::string &message) : File(file), Line(line), Message(message) { } VTKM_CONT TestFailure(const std::string &file, vtkm::Id line, const std::string &message, const std::string &condition) : File(file), Line(line) { this->Message.append(message); this->Message.append(" ("); this->Message.append(condition); this->Message.append(")"); } VTKM_CONT const std::string &GetFile() const { return this->File; } VTKM_CONT vtkm::Id GetLine() const { return this->Line; } VTKM_CONT const std::string &GetMessage() const { return this->Message; } private: std::string File; vtkm::Id Line; std::string Message; }; static VTKM_CONT void Assert(bool condition, const std::string &file, vtkm::Id line, const std::string &message, const std::string &conditionString) { if (condition) { // Do nothing. } else { throw TestFailure(file, line, message, conditionString); } } #ifndef VTKM_TESTING_IN_CONT /// Calls the test function \a function with no arguments. Catches any errors /// generated by VTKM_TEST_ASSERT or VTKM_TEST_FAIL, reports the error, and /// returns "1" (a failure status for a program's main). Returns "0" (a /// success status for a program's main). /// /// The intention is to implement a test's main function with this. For /// example, the implementation of UnitTestFoo might look something like /// this. /// /// \code /// #include /// /// namespace { /// /// void TestFoo() /// { /// // Do actual test, which checks in VTKM_TEST_ASSERT or VTKM_TEST_FAIL. /// } /// /// } // anonymous namespace /// /// int UnitTestFoo(int, char *[]) /// { /// return vtkm::testing::Testing::Run(TestFoo); /// } /// \endcode /// template static VTKM_CONT int Run(Func function) { try { function(); } catch (TestFailure error) { std::cout << "***** Test failed @ " << error.GetFile() << ":" << error.GetLine() << std::endl << error.GetMessage() << std::endl; return 1; } catch (std::exception error) { std::cout << "***** STL exception throw." << std::endl << error.what() << std::endl; } catch (...) { std::cout << "***** Unidentified exception thrown." << std::endl; return 1; } return 0; } #endif template struct InternalPrintTypeAndInvoke { InternalPrintTypeAndInvoke(FunctionType function) : Function(function) { } template void operator()(T t) const { std::cout << "*** " << vtkm::testing::TypeName::Name() << " ***************" << std::endl; this->Function(t); } private: FunctionType Function; }; /// Runs template \p function on all the types in the given list. If no type /// list is given, then an exemplar list of types is used. /// template static void TryTypes(const FunctionType &function, TypeList) { vtkm::ListForEach(InternalPrintTypeAndInvoke(function), TypeList()); } struct TypeListTagExemplarTypes : vtkm::ListTagBase > { }; template static void TryTypes(const FunctionType &function) { TryTypes(function, TypeListTagExemplarTypes()); } // Disabled: This very long list results is very long compile times. // /// Runs templated \p function on all the basic types defined in VTK-m. This // /// is helpful to test templated functions that should work on all types. If // /// the function is supposed to work on some subset of types, then use // /// \c TryTypes to restrict the call to some other list of types. // /// // template // static void TryAllTypes(const FunctionType &function) // { // TryTypes(function, vtkm::TypeListTagAll()); // } /// Runs templated \p function on all cell shapes defined in VTK-m. This is /// helpful to test templated functions that should work on all cell types. /// template static void TryAllCellShapes(const FunctionType &function) { detail::InternalTryCellShape<0>()(function); } }; } } // namespace vtkm::internal /// Helper function to test two quanitites for equality accounting for slight /// variance due to floating point numerical inaccuracies. /// template static inline VTKM_EXEC_CONT bool test_equal(VectorType1 vector1, VectorType2 vector2, vtkm::Float64 tolerance = 0.00001) { typedef typename vtkm::VecTraits Traits1; typedef typename vtkm::VecTraits Traits2; if (Traits1::GetNumberOfComponents(vector1) != Traits2::GetNumberOfComponents(vector2)) { return false; } for (vtkm::IdComponent component = 0; component < Traits1::GetNumberOfComponents(vector1); component++) { vtkm::Float64 value1 = vtkm::Float64(Traits1::GetComponent(vector1, component)); vtkm::Float64 value2 = vtkm::Float64(Traits2::GetComponent(vector2, component)); if (vtkm::Abs(value1-value2) <= tolerance) { continue; } // We are using a ratio to compare the relative tolerance of two numbers. // Using an ULP based comparison (comparing the bits as integers) might be // a better way to go, but this has been working pretty well so far. vtkm::Float64 ratio; if ((vtkm::Abs(value2) > tolerance) && (value2 != 0)) { ratio = value1 / value2; } else { // If we are here, it means that value2 is close to 0 but value1 is not. // These cannot be within tolerance, so just return false. return false; } if ((ratio > vtkm::Float64(1.0) - tolerance) && (ratio < vtkm::Float64(1.0) + tolerance)) { // This component is OK. The condition is checked in this way to // correctly handle non-finites that fail all comparisons. Thus, if a // non-finite is encountered, this condition will fail and false will be // returned. } else { return false; } } return true; } /// Special implementation of test_equal for strings, which don't fit a model /// of fixed length vectors of numbers. /// static inline VTKM_CONT bool test_equal(const std::string &string1, const std::string &string2) { return string1 == string2; } /// Special implementation of test_equal for Pairs, which are a bit different /// than a vector of numbers of the same type. /// template static inline VTKM_CONT bool test_equal(const vtkm::Pair &pair1, const vtkm::Pair &pair2, vtkm::Float64 tolerance = 0.0001) { return test_equal(pair1.first, pair2.first, tolerance) && test_equal(pair1.second, pair2.second, tolerance); } /// Special implementation of test_equal for Ranges. /// static inline VTKM_EXEC_CONT bool test_equal(const vtkm::Range &range1, const vtkm::Range &range2, vtkm::Float64 tolerance = 0.0001) { return (test_equal(range1.Min, range2.Min, tolerance) && test_equal(range1.Max, range2.Max, tolerance)); } /// Special implementation of test_equal for Bounds. /// static inline VTKM_EXEC_CONT bool test_equal(const vtkm::Bounds &bounds1, const vtkm::Bounds &bounds2, vtkm::Float64 tolerance = 0.0001) { return (test_equal(bounds1.X, bounds2.X, tolerance) && test_equal(bounds1.Y, bounds2.Y, tolerance) && test_equal(bounds1.Z, bounds2.Z, tolerance)); } template static inline VTKM_EXEC_CONT T TestValue(vtkm::Id index, T, vtkm::TypeTraitsIntegerTag) { return T(index*100); } template static inline VTKM_EXEC_CONT T TestValue(vtkm::Id index, T, vtkm::TypeTraitsRealTag) { return T(0.01*static_cast(index) + 1.001); } /// Many tests involve getting and setting values in some index-based structure /// (like an array). These tests also often involve trying many types. The /// overloaded TestValue function returns some unique value for an index for a /// given type. Different types might give different values. /// template static inline VTKM_EXEC_CONT T TestValue(vtkm::Id index, T) { return TestValue(index, T(), typename vtkm::TypeTraits::NumericTag()); } template static inline VTKM_EXEC_CONT vtkm::Vec TestValue(vtkm::Id index, vtkm::Vec) { vtkm::Vec value; for (vtkm::IdComponent i = 0; i < N; i++) { value[i] = T(TestValue(index, T()) + T(i + 1)); } return value; } static inline VTKM_CONT std::string TestValue(vtkm::Id index, std::string) { std::stringstream stream; stream << index; return stream.str(); } /// Verifies that the contents of the given array portal match the values /// returned by vtkm::testing::TestValue. /// template static inline VTKM_CONT void CheckPortal(const PortalType &portal) { typedef typename PortalType::ValueType ValueType; for (vtkm::Id index = 0; index < portal.GetNumberOfValues(); index++) { ValueType expectedValue = TestValue(index, ValueType()); ValueType foundValue = portal.Get(index); if (!test_equal(expectedValue, foundValue)) { std::stringstream message; message << "Got unexpected value in array." << std::endl << "Expected: " << expectedValue << ", Found: " << foundValue << std::endl; VTKM_TEST_FAIL(message.str().c_str()); } } } /// Sets all the values in a given array portal to be the values returned /// by vtkm::testing::TestValue. The ArrayPortal must be allocated first. /// template static inline VTKM_CONT void SetPortal(const PortalType &portal) { typedef typename PortalType::ValueType ValueType; for (vtkm::Id index = 0; index < portal.GetNumberOfValues(); index++) { portal.Set(index, TestValue(index, ValueType())); } } #endif //vtk_m_testing_Testing_h