diff options
Diffstat (limited to 'libcxx/test/support')
-rw-r--r-- | libcxx/test/support/count_new.hpp | 34 | ||||
-rw-r--r-- | libcxx/test/support/filesystem_dynamic_test_helper.py | 85 | ||||
-rw-r--r-- | libcxx/test/support/filesystem_test_helper.hpp | 370 | ||||
-rw-r--r-- | libcxx/test/support/min_allocator.h | 54 | ||||
-rw-r--r-- | libcxx/test/support/rapid-cxx-test.hpp | 847 | ||||
-rw-r--r-- | libcxx/test/support/test_convertible.hpp | 42 | ||||
-rw-r--r-- | libcxx/test/support/test_convertible_header.pass.cpp | 68 | ||||
-rw-r--r-- | libcxx/test/support/test_macros.h | 17 |
8 files changed, 1517 insertions, 0 deletions
diff --git a/libcxx/test/support/count_new.hpp b/libcxx/test/support/count_new.hpp index 8f66c5057d1..4a1a78d42c3 100644 --- a/libcxx/test/support/count_new.hpp +++ b/libcxx/test/support/count_new.hpp @@ -130,6 +130,11 @@ public: return disable_checking || n != new_called; } + bool checkNewCalledGreaterThan(int n) const + { + return disable_checking || new_called > n; + } + bool checkDeleteCalledEq(int n) const { return disable_checking || n == delete_called; @@ -253,4 +258,33 @@ private: DisableAllocationGuard& operator=(DisableAllocationGuard const&); }; + +struct RequireAllocationGuard { + explicit RequireAllocationGuard(std::size_t RequireAtLeast = 1) + : m_req_alloc(RequireAtLeast), + m_new_count_on_init(globalMemCounter.new_called), + m_outstanding_new_on_init(globalMemCounter.outstanding_new), + m_exactly(false) + { + } + + void requireAtLeast(std::size_t N) { m_req_alloc = N; m_exactly = false; } + void requireExactly(std::size_t N) { m_req_alloc = N; m_exactly = true; } + + ~RequireAllocationGuard() { + assert(globalMemCounter.checkOutstandingNewEq(m_outstanding_new_on_init)); + std::size_t Expect = m_new_count_on_init + m_req_alloc; + assert(globalMemCounter.checkNewCalledEq(Expect) || + (!m_exactly && globalMemCounter.checkNewCalledGreaterThan(Expect))); + } + +private: + std::size_t m_req_alloc; + const std::size_t m_new_count_on_init; + const std::size_t m_outstanding_new_on_init; + bool m_exactly; + RequireAllocationGuard(RequireAllocationGuard const&); + RequireAllocationGuard& operator=(RequireAllocationGuard const&); +}; + #endif /* COUNT_NEW_HPP */ diff --git a/libcxx/test/support/filesystem_dynamic_test_helper.py b/libcxx/test/support/filesystem_dynamic_test_helper.py new file mode 100644 index 00000000000..1f48c952798 --- /dev/null +++ b/libcxx/test/support/filesystem_dynamic_test_helper.py @@ -0,0 +1,85 @@ +import sys +import os +import stat + +# Ensure that this is being run on a specific platform +assert sys.platform.startswith('linux') or sys.platform.startswith('darwin') \ + or sys.platform.startswith('cygwin') or sys.platform.startswith('freebsd') + +def env_path(): + ep = os.environ.get('LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT') + assert ep is not None + ep = os.path.realpath(ep) + assert os.path.isdir(ep) + return ep + +env_path_global = env_path() + +# Make sure we don't try and write outside of env_path. +# All paths used should be sanitized +def sanitize(p): + p = os.path.realpath(p) + if os.path.commonprefix([env_path_global, p]): + return p + assert False + +""" +Some of the tests restrict permissions to induce failures. +Before we delete the test enviroment, we have to walk it and re-raise the +permissions. +""" +def clean_recursive(root_p): + if not os.path.islink(root_p): + os.chmod(root_p, 0o777) + for ent in os.listdir(root_p): + p = os.path.join(root_p, ent) + if os.path.islink(p) or not os.path.isdir(p): + os.remove(p) + else: + assert os.path.isdir(p) + clean_recursive(p) + os.rmdir(p) + + +def init_test_directory(root_p): + root_p = sanitize(root_p) + assert not os.path.exists(root_p) + os.makedirs(root_p) + + +def destroy_test_directory(root_p): + root_p = sanitize(root_p) + clean_recursive(root_p) + os.rmdir(root_p) + + +def create_file(fname, size): + with open(sanitize(fname), 'w') as f: + f.write('c' * size) + + +def create_dir(dname): + os.mkdir(sanitize(dname)) + + +def create_symlink(source, link): + os.symlink(sanitize(source), sanitize(link)) + + +def create_hardlink(source, link): + os.link(sanitize(source), sanitize(link)) + + +def create_fifo(source): + os.mkfifo(sanitize(source)) + + +def create_socket(source): + mode = 0600|stat.S_IFSOCK + os.mknod(sanitize(source), mode) + + +if __name__ == '__main__': + command = " ".join(sys.argv[1:]) + eval(command) + sys.exit(0) diff --git a/libcxx/test/support/filesystem_test_helper.hpp b/libcxx/test/support/filesystem_test_helper.hpp new file mode 100644 index 00000000000..54345905963 --- /dev/null +++ b/libcxx/test/support/filesystem_test_helper.hpp @@ -0,0 +1,370 @@ +#ifndef FILESYSTEM_TEST_HELPER_HPP +#define FILESYSTEM_TEST_HELPER_HPP + +#include <experimental/filesystem> +#include <cassert> +#include <cstdio> // for printf +#include <string> +#include <fstream> +#include <random> + +namespace fs = std::experimental::filesystem; + +// static test helpers + +#ifndef LIBCXX_FILESYSTEM_STATIC_TEST_ROOT +#warning "STATIC TESTS DISABLED" +#else // LIBCXX_FILESYSTEM_STATIC_TEST_ROOT + +namespace StaticEnv { + +inline fs::path makePath(fs::path const& p) { + static const fs::path env_path = LIBCXX_FILESYSTEM_STATIC_TEST_ROOT; + return env_path / p; +} + +static const fs::path Root = LIBCXX_FILESYSTEM_STATIC_TEST_ROOT; + +static const fs::path TestFileList[] = { + makePath("empty_file"), + makePath("non_empty_file"), + makePath("dir1/file1"), + makePath("dir1/file2") +}; +const std::size_t TestFileListSize = sizeof(TestFileList) / sizeof(fs::path); + +static const fs::path TestDirList[] = { + makePath("dir1"), + makePath("dir1/dir2"), + makePath("dir1/dir2/dir3") +}; +const std::size_t TestDirListSize = sizeof(TestDirList) / sizeof(fs::path); + +static const fs::path File = TestFileList[0]; +static const fs::path Dir = TestDirList[0]; +static const fs::path Dir2 = TestDirList[1]; +static const fs::path Dir3 = TestDirList[2]; +static const fs::path SymlinkToFile = makePath("symlink_to_empty_file"); +static const fs::path SymlinkToDir = makePath("symlink_to_dir"); +static const fs::path BadSymlink = makePath("bad_symlink"); +static const fs::path DNE = makePath("DNE"); +static const fs::path EmptyFile = TestFileList[0]; +static const fs::path NonEmptyFile = TestFileList[1]; +static const fs::path CharFile = "/dev/null"; // Hopefully this exists + +static const fs::path DirIterationList[] = { + makePath("dir1/dir2"), + makePath("dir1/file1"), + makePath("dir1/file2") +}; +const std::size_t DirIterationListSize = sizeof(DirIterationList) + / sizeof(fs::path); + +static const fs::path DirIterationListDepth1[] = { + makePath("dir1/dir2/afile3"), + makePath("dir1/dir2/dir3"), + makePath("dir1/dir2/symlink_to_dir3"), + makePath("dir1/dir2/file4"), +}; + +static const fs::path RecDirIterationList[] = { + makePath("dir1/dir2"), + makePath("dir1/file1"), + makePath("dir1/file2"), + makePath("dir1/dir2/afile3"), + makePath("dir1/dir2/dir3"), + makePath("dir1/dir2/symlink_to_dir3"), + makePath("dir1/dir2/file4"), + makePath("dir1/dir2/dir3/file5") +}; + +static const fs::path RecDirFollowSymlinksIterationList[] = { + makePath("dir1/dir2"), + makePath("dir1/file1"), + makePath("dir1/file2"), + makePath("dir1/dir2/afile3"), + makePath("dir1/dir2/dir3"), + makePath("dir1/dir2/file4"), + makePath("dir1/dir2/dir3/file5"), + makePath("dir1/dir2/symlink_to_dir3"), + makePath("dir1/dir2/symlink_to_dir3/file5"), +}; + +} // namespace StaticEnv + +#endif // LIBCXX_FILESYSTEM_STATIC_TEST_ROOT + +#ifndef LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT +#warning LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT must be defined +#else // LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT + +#ifndef LIBCXX_FILESYSTEM_DYNAMIC_TEST_HELPER +#error LIBCXX_FILESYSTEM_DYNAMIC_TEST_HELPER must be defined +#endif + +struct scoped_test_env +{ + scoped_test_env() : test_root(random_env_path()) + { fs_helper_run(fs_make_cmd("init_test_directory", test_root)); } + + ~scoped_test_env() + { fs_helper_run(fs_make_cmd("destroy_test_directory", test_root)); } + + scoped_test_env(scoped_test_env const &) = delete; + scoped_test_env & operator=(scoped_test_env const &) = delete; + + fs::path make_env_path(std::string p) { return sanitize_path(p); } + + std::string sanitize_path(std::string raw) { + assert(raw.find("..") == std::string::npos); + std::string const& root = test_root.native(); + if (root.compare(0, root.size(), raw, 0, root.size()) != 0) { + assert(raw.front() != '\\'); + fs::path tmp(test_root); + tmp /= raw; + return std::move(const_cast<std::string&>(tmp.native())); + } + return raw; + } + + std::string create_file(std::string filename, std::size_t size = 0) { + filename = sanitize_path(std::move(filename)); + std::string out_str(size, 'a'); + { + std::ofstream out(filename.c_str()); + out << out_str; + } + return filename; + } + + std::string create_dir(std::string filename) { + filename = sanitize_path(std::move(filename)); + fs_helper_run(fs_make_cmd("create_dir", filename)); + return filename; + } + + std::string create_symlink(std::string source, std::string to) { + source = sanitize_path(std::move(source)); + to = sanitize_path(std::move(to)); + fs_helper_run(fs_make_cmd("create_symlink", source, to)); + return to; + } + + std::string create_hardlink(std::string source, std::string to) { + source = sanitize_path(std::move(source)); + to = sanitize_path(std::move(to)); + fs_helper_run(fs_make_cmd("create_hardlink", source, to)); + return to; + } + + std::string create_fifo(std::string file) { + file = sanitize_path(std::move(file)); + fs_helper_run(fs_make_cmd("create_fifo", file)); + return file; + } + + // OS X and FreeBSD doesn't support socket files so we shouldn't even + // allow tests to call this unguarded. +#if !defined(__FreeBSD__) && !defined(__APPLE__) + std::string create_socket(std::string file) { + file = sanitize_path(std::move(file)); + fs_helper_run(fs_make_cmd("create_socket", file)); + return file; + } +#endif + + fs::path const test_root; + +private: + static char to_hex(int ch) { + return ch < 10 ? static_cast<char>('0' + ch) + : static_cast<char>('a' + (ch - 10)); + } + + static char random_hex_char() { + static std::mt19937 rd { std::random_device{}() }; + static std::uniform_int_distribution<int> mrand{0, 15}; + return to_hex( mrand(rd) ); + } + + static std::string unique_path_suffix() { + std::string model = "test.%%%%%%"; + for (auto & ch : model) { + if (ch == '%') ch = random_hex_char(); + } + return model; + } + + // This could potentially introduce a filesystem race with other tests + // running at the same time, but oh well, it's just test code. + static inline fs::path random_env_path() { + static const char* env_path = LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT; + fs::path p = fs::path(env_path) / unique_path_suffix(); + assert(p.parent_path() == env_path); + return p; + } + + static inline std::string make_arg(std::string const& arg) { + return "'" + arg + "'"; + } + + static inline std::string make_arg(std::size_t arg) { + return std::to_string(arg); + } + + template <class T> + static inline std::string + fs_make_cmd(std::string const& cmd_name, T const& arg) { + return cmd_name + "(" + make_arg(arg) + ")"; + } + + template <class T, class U> + static inline std::string + fs_make_cmd(std::string const& cmd_name, T const& arg1, U const& arg2) { + return cmd_name + "(" + make_arg(arg1) + ", " + make_arg(arg2) + ")"; + } + + static inline void fs_helper_run(std::string const& raw_cmd) { + // check that the fs test root in the enviroment matches what we were + // compiled with. + static bool checked = checkDynamicTestRoot(); + std::string cmd = LIBCXX_FILESYSTEM_DYNAMIC_TEST_HELPER; + cmd += " \"" + raw_cmd + "\""; + int ret = std::system(cmd.c_str()); + assert(ret == 0); + } + + static bool checkDynamicTestRoot() { + char* fs_root = std::getenv("LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT"); + if (!fs_root) { + std::printf("ERROR: LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT must be a defined " + "environment variable when running the test.\n"); + std::abort(); + } + if (std::string(fs_root) != LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT) { + std::printf("ERROR: LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT enviroment variable" + " must have the same value as when the test was compiled.\n"); + std::printf(" Current Value: '%s'\n", fs_root); + std::printf(" Expected Value: '%s'\n", LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT); + std::abort(); + } + return true; + } + +}; + +#endif // LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT + +// Misc test types + +#define CONCAT2(LHS, RHS) LHS##RHS +#define CONCAT(LHS, RHS) CONCAT2(LHS, RHS) +#define MKSTR(Str) {Str, CONCAT(L, Str), CONCAT(u, Str), CONCAT(U, Str)} + +struct MultiStringType { + const char* s; + const wchar_t* w; + const char16_t* u16; + const char32_t* u32; + + operator const char* () const { return s; } + operator const wchar_t* () const { return w; } + operator const char16_t* () const { return u16; } + operator const char32_t* () const { return u32; } +}; + +const MultiStringType PathList[] = { + MKSTR(""), + MKSTR(" "), + MKSTR("//"), + MKSTR("."), + MKSTR(".."), + MKSTR("foo"), + MKSTR("/"), + MKSTR("/foo"), + MKSTR("foo/"), + MKSTR("/foo/"), + MKSTR("foo/bar"), + MKSTR("/foo/bar"), + MKSTR("//net"), + MKSTR("//net/foo"), + MKSTR("///foo///"), + MKSTR("///foo///bar"), + MKSTR("/."), + MKSTR("./"), + MKSTR("/.."), + MKSTR("../"), + MKSTR("foo/."), + MKSTR("foo/.."), + MKSTR("foo/./"), + MKSTR("foo/./bar"), + MKSTR("foo/../"), + MKSTR("foo/../bar"), + MKSTR("c:"), + MKSTR("c:/"), + MKSTR("c:foo"), + MKSTR("c:/foo"), + MKSTR("c:foo/"), + MKSTR("c:/foo/"), + MKSTR("c:/foo/bar"), + MKSTR("prn:"), + MKSTR("c:\\"), + MKSTR("c:\\foo"), + MKSTR("c:foo\\"), + MKSTR("c:\\foo\\"), + MKSTR("c:\\foo/"), + MKSTR("c:/foo\\bar"), + MKSTR("//"), + MKSTR("/finally/we/need/one/really/really/really/really/really/really/really/long/string") +}; +const unsigned PathListSize = sizeof(PathList) / sizeof(MultiStringType); + +template <class Iter> +Iter IterEnd(Iter B) { + using VT = typename std::iterator_traits<Iter>::value_type; + for (; *B != VT{}; ++B) + ; + return B; +} + +template <class CharT> +const CharT* StrEnd(CharT const* P) { + return IterEnd(P); +} + +template <class CharT> +std::size_t StrLen(CharT const* P) { + return StrEnd(P) - P; +} + +// Testing the allocation behavior of the code_cvt functions requires +// *knowing* that the allocation was not done by "path::__str_". +// This hack forces path to allocate enough memory. +inline void PathReserve(fs::path& p, std::size_t N) { + auto const& native_ref = p.native(); + const_cast<std::string&>(native_ref).reserve(N); +} + +template <class Iter1, class Iter2> +bool checkCollectionsEqual( + Iter1 start1, Iter1 const end1 + , Iter2 start2, Iter2 const end2 + ) +{ + while (start1 != end1 && start2 != end2) { + if (*start1 != *start2) { + return false; + } + ++start1; ++start2; + } + return (start1 == end1 && start2 == end2); +} + +// We often need to test that the error_code was cleared if no error occurs +// this function returns a error_code which is set to an error that will +// never be returned by the filesystem functions. +inline std::error_code GetTestEC() { + return std::make_error_code(std::errc::address_family_not_supported); +} + +#endif /* FILESYSTEM_TEST_HELPER_HPP */ diff --git a/libcxx/test/support/min_allocator.h b/libcxx/test/support/min_allocator.h index 68cde7917a2..701159e0481 100644 --- a/libcxx/test/support/min_allocator.h +++ b/libcxx/test/support/min_allocator.h @@ -11,6 +11,9 @@ #define MIN_ALLOCATOR_H #include <cstddef> +#include <cstdlib> +#include <cstddef> +#include <cassert> #include "test_macros.h" @@ -39,6 +42,57 @@ public: friend bool operator!=(bare_allocator x, bare_allocator y) {return !(x == y);} }; +struct malloc_allocator_base { + static size_t alloc_count; + static size_t dealloc_count; + static bool disable_default_constructor; + + static size_t outstanding_alloc() { + assert(alloc_count >= dealloc_count); + return (alloc_count - dealloc_count); + } + + static void reset() { + assert(outstanding_alloc() == 0); + disable_default_constructor = false; + alloc_count = 0; + dealloc_count = 0; + } +}; + + +size_t malloc_allocator_base::alloc_count = 0; +size_t malloc_allocator_base::dealloc_count = 0; +bool malloc_allocator_base::disable_default_constructor = false; + + +template <class T> +class malloc_allocator : public malloc_allocator_base +{ +public: + typedef T value_type; + + malloc_allocator() TEST_NOEXCEPT { assert(!disable_default_constructor); } + + template <class U> + malloc_allocator(malloc_allocator<U>) TEST_NOEXCEPT {} + + T* allocate(std::size_t n) + { + ++alloc_count; + return static_cast<T*>(std::malloc(n*sizeof(T))); + } + + void deallocate(T* p, std::size_t) + { + ++dealloc_count; + std::free(static_cast<void*>(p)); + } + + friend bool operator==(malloc_allocator, malloc_allocator) {return true;} + friend bool operator!=(malloc_allocator x, malloc_allocator y) {return !(x == y);} +}; + #if TEST_STD_VER >= 11 diff --git a/libcxx/test/support/rapid-cxx-test.hpp b/libcxx/test/support/rapid-cxx-test.hpp new file mode 100644 index 00000000000..a25bda53109 --- /dev/null +++ b/libcxx/test/support/rapid-cxx-test.hpp @@ -0,0 +1,847 @@ +#ifndef RAPID_CXX_TEST_HPP +#define RAPID_CXX_TEST_HPP + +# include <cstddef> +# include <cstdlib> +# include <cstdio> +# include <cstring> +# include <cassert> + +#include "test_macros.h" + +#if !defined(RAPID_CXX_TEST_NO_SYSTEM_HEADER) || !defined(__GNUC__) +#pragma GCC system_header +#endif + +# define RAPID_CXX_TEST_PP_CAT(x, y) RAPID_CXX_TEST_PP_CAT_2(x, y) +# define RAPID_CXX_TEST_PP_CAT_2(x, y) x##y + +# define RAPID_CXX_TEST_PP_STR(...) RAPID_CXX_TEST_PP_STR_2(__VA_ARGS__) +# define RAPID_CXX_TEST_PP_STR_2(...) #__VA_ARGS__ + +# if defined(__GNUC__) +# define TEST_FUNC_NAME() __PRETTY_FUNCTION__ +# define RAPID_CXX_TEST_UNUSED __attribute__((unused)) +# else +# define TEST_FUNC_NAME() __func__ +# define RAPID_CXX_TEST_UNUSED +# endif + +//////////////////////////////////////////////////////////////////////////////// +// TEST_SUITE +//////////////////////////////////////////////////////////////////////////////// +# define TEST_SUITE(Name) \ +namespace Name \ +{ \ + inline ::rapid_cxx_test::test_suite & get_test_suite() \ + { \ + static ::rapid_cxx_test::test_suite m_suite(#Name); \ + return m_suite; \ + } \ + \ + inline int unit_test_main(int, char**) \ + { \ + ::rapid_cxx_test::test_runner runner(get_test_suite()); \ + return runner.run(); \ + } \ +} \ +int main(int argc, char **argv) \ +{ \ + return Name::unit_test_main(argc, argv); \ +} \ +namespace Name \ +{ /* namespace closed in TEST_SUITE_END */ +# + +//////////////////////////////////////////////////////////////////////////////// +// TEST_SUITE_END +//////////////////////////////////////////////////////////////////////////////// +# define TEST_SUITE_END() \ +} /* namespace opened in TEST_SUITE(...) */ +# + +//////////////////////////////////////////////////////////////////////////////// +// TEST_CASE +//////////////////////////////////////////////////////////////////////////////// + +# if !defined(__clang__) +# +# define TEST_CASE(Name) \ + void Name(); \ + static void RAPID_CXX_TEST_PP_CAT(Name, _invoker)() \ + { \ + Name(); \ + } \ + static ::rapid_cxx_test::registrar \ + RAPID_CXX_TEST_PP_CAT(rapid_cxx_test_registrar_, Name)( \ + get_test_suite() \ + , ::rapid_cxx_test::test_case(__FILE__, #Name, __LINE__, & RAPID_CXX_TEST_PP_CAT(Name, _invoker)) \ + ); \ + void Name() +# +# else /* __clang__ */ +# +# define TEST_CASE(Name) \ + void Name(); \ + static void RAPID_CXX_TEST_PP_CAT(Name, _invoker)() \ + { \ + Name(); \ + } \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") \ + static ::rapid_cxx_test::registrar \ + RAPID_CXX_TEST_PP_CAT(rapid_cxx_test_registrar_, Name)( \ + get_test_suite() \ + , ::rapid_cxx_test::test_case(__FILE__, #Name, __LINE__, & RAPID_CXX_TEST_PP_CAT(Name, _invoker)) \ + ); \ + _Pragma("clang diagnostic pop") \ + void Name() +# +# endif /* !defined(__clang__) */ + + +# define TEST_SET_CHECKPOINT() ::rapid_cxx_test::set_checkpoint(__FILE__, TEST_FUNC_NAME(), __LINE__) + +#define RAPID_CXX_TEST_OUTCOME() + +//////////////////////////////////////////////////////////////////////////////// +// TEST_UNSUPPORTED +//////////////////////////////////////////////////////////////////////////////// +# define TEST_UNSUPPORTED() \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::unsupported, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "", "" \ + ); \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + return; \ + } while (false) +# + + +//////////////////////////////////////////////////////////////////////////////// +// BASIC ASSERTIONS +//////////////////////////////////////////////////////////////////////////////// +# define TEST_WARN(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_WARN(" #__VA_ARGS__ ")", "" \ + ); \ + if (not (__VA_ARGS__)) { \ + m_f.type = ::rapid_cxx_test::failure_type::warn; \ + } \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + } while (false) +# + +# define TEST_CHECK(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_CHECK(" #__VA_ARGS__ ")", "" \ + ); \ + if (not (__VA_ARGS__)) { \ + m_f.type = ::rapid_cxx_test::failure_type::check; \ + } \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + } while (false) +# + +# define TEST_REQUIRE(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_REQUIRE(" #__VA_ARGS__ ")", "" \ + ); \ + if (not (__VA_ARGS__)) { \ + m_f.type = ::rapid_cxx_test::failure_type::require; \ + } \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ + return; \ + } \ + } while (false) +# + +# define TEST_ASSERT(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_ASSERT(" #__VA_ARGS__ ")", "" \ + ); \ + if (not (__VA_ARGS__)) { \ + m_f.type = ::rapid_cxx_test::failure_type::assert; \ + } \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ + std::abort(); \ + } \ + } while (false) +# + +//////////////////////////////////////////////////////////////////////////////// +// TEST_CHECK_NO_THROW / TEST_CHECK_THROW +//////////////////////////////////////////////////////////////////////////////// +#ifndef TEST_HAS_NO_EXCEPTIONS + +# define TEST_CHECK_NO_THROW(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_CHECK_NO_THROW(" #__VA_ARGS__ ")", "" \ + ); \ + try { \ + (static_cast<void>(__VA_ARGS__)); \ + } catch (...) { \ + m_f.type = ::rapid_cxx_test::failure_type::check; \ + } \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + } while (false) +# + +# define TEST_CHECK_THROW(Except, ...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_CHECK_THROW(" #Except "," #__VA_ARGS__ ")", "" \ + ); \ + try { \ + (static_cast<void>(__VA_ARGS__)); \ + m_f.type = ::rapid_cxx_test::failure_type::check; \ + } catch (Except const &) {} \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + } while (false) +# + +#else // TEST_HAS_NO_EXCEPTIONS + +# define TEST_CHECK_NO_THROW(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_CHECK_NO_THROW(" #__VA_ARGS__ ")", "" \ + ); \ + (static_cast<void>(__VA_ARGS__)); \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + } while (false) +# + +#define TEST_CHECK_THROW(Except, ...) ((void)0) + +#endif // TEST_HAS_NO_EXCEPTIONS + + +//////////////////////////////////////////////////////////////////////////////// +// TEST_REQUIRE_NO_THROW / TEST_REQUIRE_THROWs +//////////////////////////////////////////////////////////////////////////////// +#ifndef TEST_HAS_NO_EXCEPTIONS + +# define TEST_REQUIRE_NO_THROW(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_REQUIRE_NO_THROW(" #__VA_ARGS__ ")", "" \ + ); \ + try { \ + (static_cast<void>(__VA_ARGS__)); \ + } catch (...) { \ + m_f.type = ::rapid_cxx_test::failure_type::require; \ + } \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ + return; \ + } \ + } while (false) +# + +# define TEST_REQUIRE_THROW(Except, ...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_REQUIRE_THROW(" #Except "," #__VA_ARGS__ ")", "" \ + ); \ + try { \ + (static_cast<void>(__VA_ARGS__)); \ + m_f.type = ::rapid_cxx_test::failure_type::require; \ + } catch (Except const &) {} \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ + return; \ + } \ + } while (false) +# + +#else // TEST_HAS_NO_EXCEPTIONS + +# define TEST_REQUIRE_NO_THROW(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_REQUIRE_NO_THROW(" #__VA_ARGS__ ")", "" \ + ); \ + (static_cast<void>(__VA_ARGS__)); \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + } while (false) +# + +#define TEST_REQUIRE_THROW(Except, ...) ((void)0) + +#endif // TEST_HAS_NO_EXCEPTIONS + +//////////////////////////////////////////////////////////////////////////////// +// TEST_ASSERT_NO_THROW / TEST_ASSERT_THROW +//////////////////////////////////////////////////////////////////////////////// +#ifndef TEST_HAS_NO_EXCEPTIONS + +# define TEST_ASSERT_NO_THROW(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_ASSERT_NO_THROW(" #__VA_ARGS__ ")", "" \ + ); \ + try { \ + (static_cast<void>(__VA_ARGS__)); \ + } catch (...) { \ + m_f.type = ::rapid_cxx_test::failure_type::assert; \ + } \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ + std::abort(); \ + } \ + } while (false) +# + +# define TEST_ASSERT_THROW(Except, ...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_ASSERT_THROW(" #Except "," #__VA_ARGS__ ")", "" \ + ); \ + try { \ + (static_cast<void>(__VA_ARGS__)); \ + m_f.type = ::rapid_cxx_test::failure_type::assert; \ + } catch (Except const &) {} \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ + std::abort(); \ + } \ + } while (false) +# + +#else // TEST_HAS_NO_EXCEPTIONS + +# define TEST_ASSERT_NO_THROW(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_ASSERT_NO_THROW(" #__VA_ARGS__ ")", "" \ + ); \ + (static_cast<void>(__VA_ARGS__)); \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + } while (false) +# + +#define TEST_ASSERT_THROW(Except, ...) ((void)0) + +#endif // TEST_HAS_NO_EXCEPTIONS + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// + +# define TEST_WARN_EQUAL_COLLECTIONS(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_WARN_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", "" \ + ); \ + if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \ + m_f.type = ::rapid_cxx_test::failure_type::warn; \ + } \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + } while (false) +# + +# define TEST_CHECK_EQUAL_COLLECTIONS(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_CHECK_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", "" \ + ); \ + if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \ + m_f.type = ::rapid_cxx_test::failure_type::check; \ + } \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + } while (false) +# + +# define TEST_REQUIRE_EQUAL_COLLECTIONS(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_REQUIRE_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", "" \ + ); \ + if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \ + m_f.type = ::rapid_cxx_test::failure_type::require; \ + } \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ + return; \ + } \ + } while (false) +# + +# define TEST_ASSERT_EQUAL_COLLECTIONS(...) \ + do { \ + TEST_SET_CHECKPOINT(); \ + ::rapid_cxx_test::test_outcome m_f( \ + ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ + , "TEST_ASSERT_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", "" \ + ); \ + if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \ + m_f.type = ::rapid_cxx_test::failure_type::assert; \ + } \ + ::rapid_cxx_test::get_reporter().report(m_f); \ + if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ + ::std::abort(); \ + } \ + } while (false) +# + +namespace rapid_cxx_test +{ + typedef void (*invoker_t)(); + + //////////////////////////////////////////////////////////////////////////// + struct test_case + { + test_case() + : file(""), func(""), line(0), invoke(NULL) + {} + + test_case(const char* file1, const char* func1, std::size_t line1, + invoker_t invoke1) + : file(file1), func(func1), line(line1), invoke(invoke1) + {} + + const char *file; + const char *func; + std::size_t line; + invoker_t invoke; + }; + + //////////////////////////////////////////////////////////////////////////// + struct failure_type + { + enum enum_type { + none, + unsupported, + warn, + check, + require, + assert, + uncaught_exception + }; + }; + + typedef failure_type::enum_type failure_type_t; + + //////////////////////////////////////////////////////////////////////////// + struct test_outcome + { + test_outcome() + : type(failure_type::none), + file(""), func(""), line(0), + expression(""), message("") + {} + + test_outcome(failure_type_t type1, const char* file1, const char* func1, + std::size_t line1, const char* expression1, + const char* message1) + : type(type1), file(file1), func(func1), line(line1), + expression(expression1), message(message1) + { + trim_func_string(); + } + + failure_type_t type; + const char *file; + const char *func; + std::size_t line; + const char *expression; + const char *message; + + private: + void trim_file_string() { + const char* f_start = file; + const char* prev_start = f_start; + const char* last_start = f_start; + char last; + while ((last = *f_start) != '\0') { + ++f_start; + if (last == '/' && *f_start) { + prev_start = last_start; + last_start = f_start; + } + } + file = prev_start; + } + void trim_func_string() { + const char* void_loc = ::strstr(func, "void "); + if (void_loc == func) { + func += strlen("void "); + } + const char* namespace_loc = ::strstr(func, "::"); + if (namespace_loc) { + func = namespace_loc + 2; + } + } + }; + + //////////////////////////////////////////////////////////////////////////// + struct checkpoint + { + const char *file; + const char *func; + std::size_t line; + }; + + namespace detail + { + inline checkpoint & global_checkpoint() + { + static checkpoint cp = {"", "", 0}; + return cp; + } + } + + //////////////////////////////////////////////////////////////////////////// + inline void set_checkpoint(const char* file, const char* func, std::size_t line) + { + checkpoint& cp = detail::global_checkpoint(); + cp.file = file; + cp.func = func; + cp.line = line; + } + + //////////////////////////////////////////////////////////////////////////// + inline checkpoint const & get_checkpoint() + { + return detail::global_checkpoint(); + } + + //////////////////////////////////////////////////////////////////////////// + class test_suite + { + public: + typedef test_case const* iterator; + typedef iterator const_iterator; + + public: + test_suite(const char *xname) + : m_name(xname), m_tests(), m_size(0) + { + assert(xname); + } + + public: + const char *name() const { return m_name; } + + std::size_t size() const { return m_size; } + + test_case const & operator[](std::size_t i) const + { + assert(i < m_size); + return m_tests[i]; + } + + const_iterator begin() const + { return m_tests; } + + const_iterator end() const + { + return m_tests + m_size; + } + + public: + std::size_t register_test(test_case tc) + { + static std::size_t test_case_max = sizeof(m_tests) / sizeof(test_case); + assert(m_size < test_case_max); + m_tests[m_size] = tc; + return m_size++; + } + + private: + test_suite(test_suite const &); + test_suite & operator=(test_suite const &); + + private: + const char* m_name; + // Since fast compile times in a priority, we use simple containers + // with hard limits. + test_case m_tests[256]; + std::size_t m_size; + }; + + //////////////////////////////////////////////////////////////////////////// + class registrar + { + public: + registrar(test_suite & st, test_case tc) + { + st.register_test(tc); + } + }; + + //////////////////////////////////////////////////////////////////////////// + class test_reporter + { + public: + test_reporter() + : m_testcases(0), m_testcase_failures(0), m_unsupported(0), + m_assertions(0), m_warning_failures(0), m_check_failures(0), + m_require_failures(0), m_uncaught_exceptions(0), m_failure() + { + } + + void test_case_begin() + { + ++m_testcases; + clear_failure(); + } + + void test_case_end() + { + if (m_failure.type != failure_type::none + && m_failure.type != failure_type::unsupported) { + ++m_testcase_failures; + } + } + +# if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wswitch-default" +# endif + // Each assertion and failure is reported through this function. + void report(test_outcome o) + { + ++m_assertions; + switch (o.type) + { + case failure_type::none: + break; + case failure_type::unsupported: + ++m_unsupported; + m_failure = o; + break; + case failure_type::warn: + ++m_warning_failures; + report_error(o); + break; + case failure_type::check: + ++m_check_failures; + report_error(o); + m_failure = o; + break; + case failure_type::require: + ++m_require_failures; + report_error(o); + m_failure = o; + break; + case failure_type::assert: + report_error(o); + break; + case failure_type::uncaught_exception: + ++m_uncaught_exceptions; + std::fprintf(stderr + , "Test case FAILED with uncaught exception:\n" + " last checkpoint near %s::%lu %s\n\n" + , o.file, o.line, o.func + ); + m_failure = o; + break; + } + } +# if defined(__GNUC__) +# pragma GCC diagnostic pop +# endif + + test_outcome current_failure() const + { + return m_failure; + } + + void clear_failure() + { + m_failure.type = failure_type::none; + m_failure.file = ""; + m_failure.func = ""; + m_failure.line = 0; + m_failure.expression = ""; + m_failure.message = ""; + } + + std::size_t test_case_count() const + { return m_testcases; } + + std::size_t test_case_failure_count() const + { return m_testcase_failures; } + + std::size_t unsupported_count() const + { return m_unsupported; } + + std::size_t assertion_count() const + { return m_assertions; } + + std::size_t warning_failure_count() const + { return m_warning_failures; } + + std::size_t check_failure_count() const + { return m_check_failures; } + + std::size_t require_failure_count() const + { return m_require_failures; } + + std::size_t failure_count() const + { return m_check_failures + m_require_failures + m_uncaught_exceptions; } + + // Print a summary of what was run and the outcome. + void print_summary(const char* suitename) const + { + FILE* out = failure_count() ? stderr : stdout; + std::size_t testcases_run = m_testcases - m_unsupported; + std::fprintf(out, "Summary for testsuite %s:\n", suitename); + std::fprintf(out, " %lu of %lu test cases passed.\n", testcases_run - m_testcase_failures, testcases_run); + std::fprintf(out, " %lu of %lu assertions passed.\n", m_assertions - (m_warning_failures + m_check_failures + m_require_failures), m_assertions); + std::fprintf(out, " %lu unsupported test case%s.\n", m_unsupported, (m_unsupported != 1 ? "s" : "")); + } + + private: + test_reporter(test_reporter const &); + test_reporter const & operator=(test_reporter const &); + + void report_error(test_outcome o) const + { + std::fprintf(stderr, "In %s:%lu Assertion %s failed.\n in file: %s\n %s\n" + , o.func, o.line, o.expression, o.file, o.message ? o.message : "" + ); + } + + private: + // counts of testcases, failed testcases, and unsupported testcases. + std::size_t m_testcases; + std::size_t m_testcase_failures; + std::size_t m_unsupported; + + // counts of assertions and assertion failures. + std::size_t m_assertions; + std::size_t m_warning_failures; + std::size_t m_check_failures; + std::size_t m_require_failures; + std::size_t m_uncaught_exceptions; + + // The last failure. This is cleared between testcases. + test_outcome m_failure; + }; + + //////////////////////////////////////////////////////////////////////////// + inline test_reporter & get_reporter() + { + static test_reporter o; + return o; + } + + //////////////////////////////////////////////////////////////////////////// + class test_runner + { + public: + test_runner(test_suite & ts) + : m_ts(ts) + {} + + public: + int run() + { + // for each testcase + for (test_suite::const_iterator b = m_ts.begin(), e = m_ts.end(); + b != e; ++b) + { + test_case const& tc = *b; + set_checkpoint(tc.file, tc.func, tc.line); + get_reporter().test_case_begin(); +#ifndef TEST_HAS_NO_EXCEPTIONS + try { +#endif + tc.invoke(); +#ifndef TEST_HAS_NO_EXCEPTIONS + } catch (...) { + test_outcome o; + o.type = failure_type::uncaught_exception; + o.file = get_checkpoint().file; + o.func = get_checkpoint().func; + o.line = get_checkpoint().line; + o.expression = ""; + o.message = ""; + get_reporter().report(o); + } +#endif + get_reporter().test_case_end(); + } + auto exit_code = get_reporter().failure_count() ? EXIT_FAILURE : EXIT_SUCCESS; + if (exit_code == EXIT_FAILURE) + get_reporter().print_summary(m_ts.name()); + return exit_code; + } + + private: + test_runner(test_runner const &); + test_runner operator=(test_runner const &); + + test_suite & m_ts; + }; + + namespace detail + { + template <class Iter1, class Iter2> + bool check_equal_collections_impl( + Iter1 start1, Iter1 const end1 + , Iter2 start2, Iter2 const end2 + ) + { + while (start1 != end1 && start2 != end2) { + if (*start1 != *start2) { + return false; + } + ++start1; ++start2; + } + return (start1 == end1 && start2 == end2); + } + } // namespace detail + +} // namespace rapid_cxx_test + + +# if defined(__GNUC__) +# pragma GCC diagnostic pop +# endif + +#endif /* RAPID_CXX_TEST_HPP */ diff --git a/libcxx/test/support/test_convertible.hpp b/libcxx/test/support/test_convertible.hpp new file mode 100644 index 00000000000..787cef6568f --- /dev/null +++ b/libcxx/test/support/test_convertible.hpp @@ -0,0 +1,42 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SUPPORT_TEST_CONVERTIBLE_HPP +#define SUPPORT_TEST_CONVERTIBLE_HPP + +// "test_convertible<Tp, Args...>()" is a metafunction used to check if 'Tp' +// is implicitly convertible from 'Args...' for any number of arguments, +// Unlike 'std::is_convertible' which only allows checking for single argument +// conversions. + +#include <type_traits> + +#include "test_macros.h" + +#if TEST_STD_VER < 11 +#error test_convertible.hpp requires C++11 or newer +#endif + +namespace detail { + template <class Tp> void eat_type(Tp); + + template <class Tp, class ...Args> + constexpr auto test_convertible_imp(int) + -> decltype(eat_type<Tp>({std::declval<Args>()...}), true) + { return true; } + + template <class Tp, class ...Args> + constexpr auto test_convertible_imp(long) -> bool { return false; } +} + +template <class Tp, class ...Args> +constexpr bool test_convertible() +{ return detail::test_convertible_imp<Tp, Args...>(0); } + +#endif // SUPPORT_TEST_CONVERTIBLE_HPP
\ No newline at end of file diff --git a/libcxx/test/support/test_convertible_header.pass.cpp b/libcxx/test/support/test_convertible_header.pass.cpp new file mode 100644 index 00000000000..a56b84b4739 --- /dev/null +++ b/libcxx/test/support/test_convertible_header.pass.cpp @@ -0,0 +1,68 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// "support/test_convertible.hpp" + +#include "test_convertible.hpp" + +struct ImplicitDefault { + ImplicitDefault() {} +}; +static_assert(test_convertible<ImplicitDefault>(), "Must be convertible"); + +struct ExplicitDefault { + explicit ExplicitDefault() {} +}; +static_assert(!test_convertible<ExplicitDefault>(), "Must not be convertible"); + +struct ImplicitInt { + ImplicitInt(int) {} +}; +static_assert(test_convertible<ImplicitInt, int>(), "Must be convertible"); + +struct ExplicitInt { + explicit ExplicitInt(int) {} +}; +static_assert(!test_convertible<ExplicitInt, int>(), "Must not be convertible"); + +struct ImplicitCopy { + ImplicitCopy(ImplicitCopy const&) {} +}; +static_assert(test_convertible<ImplicitCopy, ImplicitCopy>(), "Must be convertible"); + +struct ExplicitCopy { + explicit ExplicitCopy(ExplicitCopy const&) {} +}; +static_assert(!test_convertible<ExplicitCopy, ExplicitCopy>(), "Must not be convertible"); + +struct ImplicitMove { + ImplicitMove(ImplicitMove&&) {} +}; +static_assert(test_convertible<ImplicitMove, ImplicitMove>(), "Must be convertible"); + +struct ExplicitMove { + explicit ExplicitMove(ExplicitMove&&) {} +}; +static_assert(!test_convertible<ExplicitMove, ExplicitMove>(), "Must not be convertible"); + +struct ImplicitArgs { + ImplicitArgs(int, int, int) {} +}; +static_assert(test_convertible<ImplicitArgs, int, int, int>(), "Must be convertible"); + +struct ExplicitArgs { + explicit ExplicitArgs(int, int, int) {} +}; +static_assert(!test_convertible<ExplicitArgs, int, int, int>(), "Must not be convertible"); + +int main() { + // Nothing to do +} diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h index 037edbcb989..597c85f2f2c 100644 --- a/libcxx/test/support/test_macros.h +++ b/libcxx/test/support/test_macros.h @@ -93,4 +93,21 @@ #define LIBCPP_STATIC_ASSERT(...) ((void)0) #endif +#define ASSERT_NOEXCEPT(...) \ + static_assert(noexcept(__VA_ARGS__), "Operation must be noexcept") + +#define ASSERT_NOT_NOEXCEPT(...) \ + static_assert(!noexcept(__VA_ARGS__), "Operation must NOT be noexcept") + +namespace test_macros_detail { +template <class T, class U> +struct is_same { enum { value = 0};} ; +template <class T> +struct is_same<T, T> { enum {value = 1}; }; +} // namespace test_macros_detail + +#define ASSERT_SAME_TYPE(...) \ + static_assert(test_macros_detail::is_same<__VA_ARGS__>::value, \ + "Types differ uexpectedly") + #endif // SUPPORT_TEST_MACROS_HPP |