#pragma once #include #include #include #include #include #include #include #include "meta.h" #include "util.h" #include #include #include #include #include #include #include // define some common lists of types typedef unittest::type_list ThirtyTwoBitTypes; typedef unittest::type_list SixtyFourBitTypes; typedef unittest::type_list IntegralTypes; typedef unittest::type_list SignedIntegralTypes; typedef unittest::type_list UnsignedIntegralTypes; typedef unittest::type_list ByteTypes; typedef unittest::type_list SmallIntegralTypes; typedef unittest::type_list LargeIntegralTypes; typedef unittest::type_list FloatingPointTypes; // A type that behaves as if it was a normal numeric type, // so it can be used in the same tests as "normal" numeric types. // NOTE: This is explicitly NOT proclaimed trivially reloctable. class custom_numeric { public: __host__ __device__ custom_numeric() { fill(0); } // Allow construction from any integral numeric. template ::value>::type> __host__ __device__ custom_numeric(const T& i) { fill(static_cast(i)); } __host__ __device__ custom_numeric(const custom_numeric & other) { fill(other.value[0]); } __host__ __device__ custom_numeric & operator=(int val) { fill(val); return *this; } __host__ __device__ custom_numeric & operator=(const custom_numeric & other) { fill(other.value[0]); return *this; } // cast to void * instead of bool to fool overload resolution // WTB C++11 explicit conversion operators __host__ __device__ operator void *() const { // static cast first to avoid MSVC warning C4312 return reinterpret_cast(static_cast(value[0])); } #define DEFINE_OPERATOR(op) \ __host__ __device__ \ custom_numeric & operator op() { \ fill(op value[0]); \ return *this; \ } \ __host__ __device__ \ custom_numeric operator op(int) const { \ custom_numeric ret(*this); \ op ret; \ return ret; \ } DEFINE_OPERATOR(++) DEFINE_OPERATOR(--) #undef DEFINE_OPERATOR #define DEFINE_OPERATOR(op) \ __host__ __device__ \ custom_numeric operator op () const \ { \ return custom_numeric(op value[0]); \ } DEFINE_OPERATOR(+) DEFINE_OPERATOR(-) DEFINE_OPERATOR(~) #undef DEFINE_OPERATOR #define DEFINE_OPERATOR(op) \ __host__ __device__ \ custom_numeric operator op (const custom_numeric & other) const \ { \ return custom_numeric(value[0] op other.value[0]); \ } DEFINE_OPERATOR(+) DEFINE_OPERATOR(-) DEFINE_OPERATOR(*) DEFINE_OPERATOR(/) DEFINE_OPERATOR(%) DEFINE_OPERATOR(<<) DEFINE_OPERATOR(>>) DEFINE_OPERATOR(&) DEFINE_OPERATOR(|) DEFINE_OPERATOR(^) #undef DEFINE_OPERATOR #define CONCAT(X, Y) X ## Y #define DEFINE_OPERATOR(op) \ __host__ __device__ \ custom_numeric & operator CONCAT(op, =) (const custom_numeric & other) \ { \ fill(value[0] op other.value[0]); \ return *this; \ } DEFINE_OPERATOR(+) DEFINE_OPERATOR(-) DEFINE_OPERATOR(*) DEFINE_OPERATOR(/) DEFINE_OPERATOR(%) DEFINE_OPERATOR(<<) DEFINE_OPERATOR(>>) DEFINE_OPERATOR(&) DEFINE_OPERATOR(|) DEFINE_OPERATOR(^) #undef DEFINE_OPERATOR #define DEFINE_OPERATOR(op) \ __host__ __device__ \ friend bool operator op (const custom_numeric & lhs, const custom_numeric & rhs) \ { \ return lhs.value[0] op rhs.value[0]; \ } DEFINE_OPERATOR(==) DEFINE_OPERATOR(!=) DEFINE_OPERATOR(<) DEFINE_OPERATOR(<=) DEFINE_OPERATOR(>) DEFINE_OPERATOR(>=) DEFINE_OPERATOR(&&) DEFINE_OPERATOR(||); #undef DEFINE_OPERATOR friend std::ostream & operator<<(std::ostream & os, const custom_numeric & val) { return os << "custom_numeric{" << val.value[0] << "}"; } private: int value[5]; __host__ __device__ void fill(int val) { for (int i = 0; i < 5; ++i) { value[i] = val; } } }; THRUST_NAMESPACE_BEGIN template <> struct numeric_limits : numeric_limits {}; namespace detail { // For random number generation template<> class integer_traits : public integer_traits_base {}; } // namespace detail THRUST_NAMESPACE_END typedef unittest::type_list NumericTypes; typedef unittest::type_list BuiltinNumericTypes; inline void chop_prefix(std::string& str, const std::string& prefix) { str.replace(str.find(prefix) == 0 ? 0 : str.size(), prefix.size(), ""); } inline std::string base_class_name(const std::string& name) { std::string result = name; // if the name begins with "struct ", chop it off chop_prefix(result, "struct "); // if the name begins with "class ", chop it off chop_prefix(result, "class "); const std::size_t first_lt = result.find_first_of("<"); if (first_lt < result.size()) // chop everything including and after first "<" return result.replace(first_lt, result.size(), ""); else return result; } enum TestStatus { Pass = 0, Failure = 1, KnownFailure = 2, Error = 3, UnknownException = 4}; typedef std::set ArgumentSet; typedef std::map ArgumentMap; std::vector get_test_sizes(void); void set_test_sizes(const std::string&); class UnitTest { public: std::string name; UnitTest() {} UnitTest(const char * name); virtual ~UnitTest() {} virtual void run() {} bool operator<(const UnitTest& u) const { return name < u.name; } }; class UnitTestDriver; class UnitTestDriver { typedef std::map TestMap; TestMap test_map; bool run_tests(std::vector& tests_to_run, const ArgumentMap& kwargs); protected: // executed immediately after each test // \param test The UnitTest of interest // \param concise Whether or not to suppress output // \return true if all is well; false if the tests must be immediately aborted virtual bool post_test_smoke_check(const UnitTest &test, bool concise); public: inline virtual ~UnitTestDriver() {}; void register_test(UnitTest * test); virtual bool run_tests(const ArgumentSet& args, const ArgumentMap& kwargs); void list_tests(void); static UnitTestDriver &s_driver(); }; // Macro to create a single unittest #define DECLARE_UNITTEST(TEST) \ class TEST##UnitTest : public UnitTest { \ public: \ TEST##UnitTest() : UnitTest(#TEST) {} \ void run(){ \ TEST(); \ } \ }; \ TEST##UnitTest TEST##Instance #define DECLARE_UNITTEST_WITH_NAME(TEST, NAME) \ class NAME##UnitTest : public UnitTest { \ public: \ NAME##UnitTest() : UnitTest(#NAME) {} \ void run(){ \ TEST(); \ } \ }; \ NAME##UnitTest NAME##Instance // Macro to create host and device versions of a // unit test for a bunch of data types #define DECLARE_VECTOR_UNITTEST(VTEST) \ void VTEST##Host(void) { \ VTEST< thrust::host_vector >(); \ VTEST< thrust::host_vector >(); \ VTEST< thrust::host_vector >(); \ VTEST< thrust::host_vector >(); \ VTEST< thrust::host_vector >(); \ /* MR vectors */ \ VTEST< thrust::host_vector > >(); \ } \ void VTEST##Device(void) { \ VTEST< thrust::device_vector >(); \ VTEST< thrust::device_vector >(); \ VTEST< thrust::device_vector >(); \ VTEST< thrust::device_vector >(); \ VTEST< thrust::device_vector >(); \ /* MR vectors */ \ VTEST< thrust::device_vector > >(); \ } \ void VTEST##Universal(void) { \ VTEST< thrust::universal_vector >(); \ VTEST< thrust::device_vector > >();\ } \ DECLARE_UNITTEST(VTEST##Host); \ DECLARE_UNITTEST(VTEST##Device); \ DECLARE_UNITTEST(VTEST##Universal); // Same as above, but only for integral types #define DECLARE_INTEGRAL_VECTOR_UNITTEST(VTEST) \ void VTEST##Host(void) { \ VTEST< thrust::host_vector >(); \ VTEST< thrust::host_vector >(); \ VTEST< thrust::host_vector >(); \ } \ void VTEST##Device(void) { \ VTEST< thrust::device_vector >(); \ VTEST< thrust::device_vector >(); \ VTEST< thrust::device_vector >(); \ } \ void VTEST##Universal(void) { \ VTEST< thrust::universal_vector >(); \ VTEST< thrust::device_vector > >();\ } \ DECLARE_UNITTEST(VTEST##Host); \ DECLARE_UNITTEST(VTEST##Device); \ DECLARE_UNITTEST(VTEST##Universal); // Macro to create instances of a test for several data types. #define DECLARE_GENERIC_UNITTEST(TEST) \ class TEST##UnitTest : public UnitTest { \ public: \ TEST##UnitTest() : UnitTest(#TEST) {} \ void run() \ { \ TEST(); \ TEST(); \ TEST(); \ TEST(); \ TEST(); \ TEST(); \ TEST(); \ } \ }; \ TEST##UnitTest TEST##Instance // Macro to create instances of a test for several array sizes. #define DECLARE_SIZED_UNITTEST(TEST) \ class TEST##UnitTest : public UnitTest { \ public: \ TEST##UnitTest() : UnitTest(#TEST) {} \ void run() \ { \ std::vector sizes = get_test_sizes(); \ for(size_t i = 0; i != sizes.size(); ++i) \ { \ TEST(sizes[i]); \ } \ } \ }; \ TEST##UnitTest TEST##Instance // Macro to create instances of a test for several data types and array sizes #define DECLARE_VARIABLE_UNITTEST(TEST) \ class TEST##UnitTest : public UnitTest { \ public: \ TEST##UnitTest() : UnitTest(#TEST) {} \ void run() \ { \ std::vector sizes = get_test_sizes(); \ for(size_t i = 0; i != sizes.size(); ++i) \ { \ TEST(sizes[i]); \ TEST(sizes[i]); \ TEST(sizes[i]); \ TEST(sizes[i]); \ TEST(sizes[i]); \ TEST(sizes[i]); \ TEST(sizes[i]); \ TEST(sizes[i]); \ } \ } \ }; \ TEST##UnitTest TEST##Instance #define DECLARE_INTEGRAL_VARIABLE_UNITTEST(TEST) \ class TEST##UnitTest : public UnitTest { \ public: \ TEST##UnitTest() : UnitTest(#TEST) {} \ void run() \ { \ std::vector sizes = get_test_sizes(); \ for(size_t i = 0; i != sizes.size(); ++i) \ { \ TEST(sizes[i]); \ TEST(sizes[i]); \ TEST(sizes[i]); \ TEST(sizes[i]); \ TEST(sizes[i]); \ TEST(sizes[i]); \ } \ } \ }; \ TEST##UnitTest TEST##Instance #define DECLARE_GENERIC_UNITTEST_WITH_TYPES_AND_NAME(TEST, TYPES, NAME) \ ::SimpleUnitTest NAME##_instance(#NAME) \ /**/ #define DECLARE_GENERIC_SIZED_UNITTEST_WITH_TYPES_AND_NAME(TEST, TYPES, NAME) \ ::VariableUnitTest NAME##_instance(#NAME) \ /**/ #define DECLARE_GENERIC_UNITTEST_WITH_TYPES(TEST, TYPES) \ ::SimpleUnitTest TEST##_instance(#TEST) \ /**/ #define DECLARE_GENERIC_SIZED_UNITTEST_WITH_TYPES(TEST, TYPES) \ ::VariableUnitTest TEST##_instance(#TEST) \ /**/ template