#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "meta.h" #include "util.h" // define some common lists of types using ThirtyTwoBitTypes = unittest::type_list; using SixtyFourBitTypes = unittest::type_list; using IntegralTypes = unittest::type_list< char, signed char, unsigned char, short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long>; using SignedIntegralTypes = unittest::type_list; using UnsignedIntegralTypes = unittest::type_list; using ByteTypes = unittest::type_list; using SmallIntegralTypes = unittest::type_list; using LargeIntegralTypes = unittest::type_list; using FloatingPointTypes = unittest::type_list; // 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: _CCCL_HOST_DEVICE constexpr custom_numeric() { fill(0); } // Allow construction from any integral numeric. template >> _CCCL_HOST_DEVICE constexpr custom_numeric(const T& i) { fill(static_cast(i)); } _CCCL_HOST_DEVICE constexpr custom_numeric(const custom_numeric& other) { fill(other.value[0]); } _CCCL_HOST_DEVICE constexpr custom_numeric& operator=(int val) { fill(val); return *this; } _CCCL_HOST_DEVICE constexpr custom_numeric& operator=(const custom_numeric& other) { if (this != &other) { fill(other.value[0]); } return *this; } // cast to void * instead of bool to fool overload resolution // WTB C++11 explicit conversion operators _CCCL_HOST_DEVICE operator void*() const { // static cast first to avoid MSVC warning C4312 return reinterpret_cast(static_cast(value[0])); // NOLINT(performance-no-int-to-ptr) } #define DEFINE_OPERATOR(op) \ _CCCL_HOST_DEVICE constexpr custom_numeric& operator op() \ { \ fill(op value[0]); \ return *this; \ } \ _CCCL_HOST_DEVICE constexpr 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) \ _CCCL_HOST_DEVICE constexpr custom_numeric operator op() const \ { \ return custom_numeric(op value[0]); \ } DEFINE_OPERATOR(+) DEFINE_OPERATOR(-) DEFINE_OPERATOR(~) #undef DEFINE_OPERATOR #define DEFINE_OPERATOR(op) \ _CCCL_HOST_DEVICE constexpr 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) \ _CCCL_HOST_DEVICE constexpr 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) \ _CCCL_HOST_DEVICE friend constexpr 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] = {0}; _CCCL_HOST_DEVICE constexpr void fill(int val) { for (auto& v : value) { v = val; } } }; namespace std { template <> struct numeric_limits : numeric_limits {}; } // namespace std _CCCL_BEGIN_NAMESPACE_CUDA_STD template <> struct numeric_limits : numeric_limits {}; _CCCL_END_NAMESPACE_CUDA_STD using NumericTypes = unittest::type_list< char, signed char, unsigned char, short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long, float, double, custom_numeric>; using BuiltinNumericTypes = unittest::type_list< char, signed char, unsigned char, short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long, float, double>; 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 }; using ArgumentSet = std::set; using ArgumentMap = std::map; // clang-format off inline constexpr size_t standard_test_sizes[] = { 0, 1, 2, 3, 4, 5, 8, 10, 13, 16, 17, 19, 27, 30, 31, 32, 33, 35, 42, 53, 58, 63, 64, 65, 72, 97, 100, 127, 128, 129, 142, 183, 192, 201, 240, 255, 256, 257, 302, 511, 512, 513, 687, 900, 1023, 1024, 1025, 1565, 1786, 1973, 2047, 2048, 2049, 3050, 4095, 4096, 4097, 5030, 7791, 10000, 10027, 12345, 16384, 17354, 26255, 32768, 43718, 65533, 65536, 65539, 123456, 131072, 731588, 1048575, 1048576, 3398570, 9760840, (1 << 24) - 1, (1 << 24), (1 << 24) + 1, (1 << 25) - 1, (1 << 25), (1 << 25) + 1, (1 << 26) - 1, 1 << 26, (1 << 26) + 1, (1 << 27) - 1, (1 << 27) }; // clang-format on inline constexpr size_t tiny_threshold = 1 << 5; // 32 inline constexpr size_t small_threshold = 1 << 8; // 256 inline constexpr size_t medium_threshold = 1 << 12; // 4K inline constexpr size_t default_threshold = 1 << 16; // 64K inline constexpr size_t large_threshold = 1 << 20; // 1M inline constexpr size_t huge_threshold = 1 << 24; // 16M inline constexpr size_t epic_threshold = 1 << 26; // 64M inline constexpr size_t max_threshold = (std::numeric_limits::max)(); inline std::vector test_sizes = [] { std::vector v; for (size_t s : standard_test_sizes) { if (s <= default_threshold) { v.push_back(s); } } return v; }(); inline const std::vector& get_test_sizes() { return test_sizes; } void set_test_sizes(const std::string&); class UnitTest { public: std::string name; UnitTest() = default; UnitTest(const char* name); virtual ~UnitTest() = default; virtual void run() {} bool operator<(const UnitTest& u) const { return name < u.name; } }; class UnitTestDriver; class UnitTestDriver { using TestMap = std::map; 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() = default; void register_test(UnitTest* test); virtual bool run_tests(const ArgumentSet& args, const ArgumentMap& kwargs); void list_tests(); 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() \ { \ VTEST>(); \ VTEST>(); \ VTEST>(); \ VTEST>(); \ VTEST>(); \ /* MR vectors */ \ VTEST>>(); \ } \ void VTEST##Device() \ { \ VTEST>(); \ VTEST>(); \ VTEST>(); \ VTEST>(); \ VTEST>(); \ /* MR vectors */ \ VTEST>>(); \ } \ void VTEST##Universal() \ { \ VTEST>(); \ VTEST>(); \ } \ 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() \ { \ VTEST>(); \ VTEST>(); \ VTEST>(); \ } \ void VTEST##Device() \ { \ VTEST>(); \ VTEST>(); \ VTEST>(); \ } \ void VTEST##Universal() \ { \ VTEST>(); \ VTEST>(); \ } \ 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() \ { \ const 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() \ { \ const 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() \ { \ const 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