summaryrefslogtreecommitdiffstats
path: root/libcxx/test/support
diff options
context:
space:
mode:
Diffstat (limited to 'libcxx/test/support')
-rw-r--r--libcxx/test/support/count_new.hpp34
-rw-r--r--libcxx/test/support/filesystem_dynamic_test_helper.py85
-rw-r--r--libcxx/test/support/filesystem_test_helper.hpp370
-rw-r--r--libcxx/test/support/min_allocator.h54
-rw-r--r--libcxx/test/support/rapid-cxx-test.hpp847
-rw-r--r--libcxx/test/support/test_convertible.hpp42
-rw-r--r--libcxx/test/support/test_convertible_header.pass.cpp68
-rw-r--r--libcxx/test/support/test_macros.h17
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
OpenPOWER on IntegriCloud