Skip to content

Commit

Permalink
Added support to Approx for strong typedefs of double - fixes #62
Browse files Browse the repository at this point in the history
- added is_constructible<> trait which for gcc/clang works as expected but for MSVC accepts only 1 argument for construction and versions of MSVC older than 2012 will always return false - so strong typedefs of double won't work with Approx there.
  • Loading branch information
onqtam committed Apr 7, 2017
1 parent 424234a commit 0ad07b0
Show file tree
Hide file tree
Showing 6 changed files with 590 additions and 133 deletions.
34 changes: 24 additions & 10 deletions doc/markdown/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ Planned features for future releases - order changes constantly...
### For 1.2:

- Value-Parameterized test cases - https://github.com/onqtam/doctest/issues/38
- decorators for test cases - like in boost test
- description
- disabled
- shouldFail
- mayFail
- alternative mechanism to tags
- time constraints?
- run X times (should also multiply with the global test run times)
- !!! and think about how these will be accessed and filtered from the command line
- crash handling: signals on UNIX platforms or structured exceptions on Windows (should also have DOCTEST_CONFIG_NO_SIGNAL_CATCHING) - look at [Using a Separate Signal Stack](https://www.gnu.org/software/libc/manual/html_node/Signal-Stack.html) - and what is a core dump?
- runtime performance
- lazily stringify expressions - only when needed
Expand All @@ -29,6 +38,7 @@ Planned features for future releases - order changes constantly...
- redo the compile time ones - also look into CATCH_CONFIG_FAST_COMPILE
- remove old benchmarks for doctest 1.0
- add runtime benchmarks
- rework the examples folder - so the test runner is compiled only a few times - CI builds take a ton of time!
- change docs a bit - mainly what is in the landing page (add link to overload)
- address the coverage issue... look at how this project does it: https://github.com/rollbear/trompeloeil
- resolve pull/60 (merge in dev, move cmake folder in scripts, maybe change/remove some of the 2 new SKIP options)
Expand All @@ -51,7 +61,7 @@ Planned features for future releases - order changes constantly...
- reporting running time of tests
- count a test case as failed if it exceeds X ms (but no force-killing!)
- killing a test that exceeds a time limit (will perhaps require threading or processes)
- matchers - should investigate what they are - look at google test and Catch
- matchers - should investigate what they are - look at google test/mock and Catch (also predicates and boost test)
- convolution support for the assertion macros (with a predicate)
- generators? - look at Catch - and investigate what they are
- look at property based testing
Expand All @@ -71,7 +81,11 @@ Planned features for future releases - order changes constantly...

### For 2.0:

- remove C++98 support - and update code like type lists to C++11
- remove C++98 support
- remove the config identifiers for C++11 features
- use variadic templates where appropriate
- update type lists to C++11
- update traits - use declval, etc.

### Things that are being considered but not part of the roadmap yet:

Expand All @@ -82,18 +96,18 @@ Planned features for future releases - order changes constantly...
- detect floating point exceptions
- checkpoint/passpoint - like in [boost test](http://www.boost.org/doc/libs/1_63_0/libs/test/doc/html/boost_test/test_output/test_tools_support_for_logging/checkpoints.html)
- log levels - like in [boost test](http://www.boost.org/doc/libs/1_63_0/libs/test/doc/html/boost_test/utf_reference/rt_param_reference/log_level.html)
- support for tags
- may fail tag
- invisible tag
- look at Catch - https://github.com/philsquared/Catch/blob/master/docs/test-cases-and-sections.md#special-tags
- marking a test to run X times (should also multiply with the global test run times)
- integrate static analysis on the CI: **msvc**, **clang**, **cppcheck**
- extend Approx for types that have operator double - see [here](https://github.com/philsquared/Catch/issues/652) and [here](https://github.com/philsquared/Catch/pull/658) and [here](https://github.com/philsquared/Catch/issues/873) and [here](https://github.com/philsquared/Catch/commit/0354d50278d725d52084601300eb955cee6756d8)
- option to list files in which there are test cases who match the current filters
- option to list test suites and test cases in a tree view
- decorators for test cases - like in boost test - like "description" - alternative (and broader) mechanism to tags
- queries for the current test case - name (and probably decorators)
- thread safety - asserts/subcases/captures should be safe to be used by multiple threads simultaneously
- support for running tests in parallel in multiple threads
- death tests - as in [google test](https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#death-tests)
- setup / teardown support
- global setup / teardown - can be currently achieved by providing a custom main function
- per test suite
- perhaps for fixtures in addition to the constructor / destructor - since throwing in the destructor might terminate the program
- or just ignore all of this this - it would require globals or classes and inheritance - and we already have subcases
- doctest in a GUI environment? with no console? APIs for attaching a console? querying if there is one? [investigate...](https://github.com/philsquared/Catch/blob/master/docs/configuration.md#stdout)
- ability to specify ASC/DESC for the order option
- command line error handling/reporting
Expand All @@ -105,12 +119,12 @@ Planned features for future releases - order changes constantly...
- also look into similar Xcode integration - https://github.com/philsquared/Catch/pull/454
- the set holding all registered tests should use a specialized allocator to minimize program startup time
- ability to provide a temp folder that is cleared between each test case
- rework the examples folder - so the test runner is compiled only a few times - CI builds take a ton of time!
- make the _MESSAGE assert macros work with variadic arguments - and maybe write the ones for binary/unary/fast asserts as well

### Things that are very unlikely to enter the roadmap:

- stop using underscores for the begining of identifiers - the anonymous variables - against the standard...
- templated fixture test cases
- test with missed warning flags for GCC - look into https://github.com/Barro/compiler-warnings
- utf8???
- handle ```wchar``` strings???
Expand Down
247 changes: 242 additions & 5 deletions doctest/doctest.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wstrict-overflow"
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#pragma GCC diagnostic ignored "-Wctor-dtor-privacy"
#pragma GCC diagnostic ignored "-Wmissing-declarations"
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
#pragma GCC diagnostic ignored "-Winline"
Expand Down Expand Up @@ -514,6 +515,220 @@ namespace detail
} // namespace static_assert_impl
#endif // DOCTEST_CONFIG_WITH_STATIC_ASSERT

namespace traits
{
template <typename T>
struct remove_const
{ typedef T type; };

template <typename T>
struct remove_const<const T>
{ typedef T type; };

template <typename T>
struct remove_volatile
{ typedef T type; };

template <typename T>
struct remove_volatile<volatile T>
{ typedef T type; };

template <typename T>
struct remove_cv
{ typedef typename remove_volatile<typename remove_const<T>::type>::type type; };

template <typename T>
struct is_pointer_helper
{ static const bool value = false; };

template <typename T>
struct is_pointer_helper<T*>
{ static const bool value = true; };

template <typename T>
struct is_pointer
{ static const bool value = is_pointer_helper<typename remove_cv<T>::type>::value; };

template <bool CONDITION, typename TYPE = void>
struct enable_if
{};

template <typename TYPE>
struct enable_if<true, TYPE>
{ typedef TYPE type; };

template <typename T>
struct remove_reference
{ typedef T type; };

template <typename T>
struct remove_reference<T&>
{ typedef T type; };

template <typename T, typename AT_1 = void, typename AT_2 = void, typename AT_3 = void,
typename AT_4 = void>
class is_constructible_impl
{
private:
template <typename C_T, typename C_AT_1, typename C_AT_2, typename C_AT_3,
typename C_AT_4>
static bool
test(typename enable_if<
sizeof(C_T) ==
sizeof(C_T(static_cast<C_AT_1>(
*static_cast<typename remove_reference<C_AT_1>::type*>(0)),
static_cast<C_AT_2>(
*static_cast<typename remove_reference<C_AT_2>::type*>(0)),
static_cast<C_AT_3>(
*static_cast<typename remove_reference<C_AT_3>::type*>(0)),
static_cast<C_AT_4>(
*static_cast<typename remove_reference<C_AT_4>::type*>(
0))))>::type*);

template <typename, typename, typename, typename, typename>
static int test(...);

public:
static const bool value = (sizeof(test<T, AT_1, AT_2, AT_3, AT_4>(0)) == sizeof(bool));
};

template <typename T, typename AT_1, typename AT_2, typename AT_3>
class is_constructible_impl<T, AT_1, AT_2, AT_3, void>
{
private:
template <typename C_T, typename C_AT_1, typename C_AT_2, typename C_AT_3>
static bool test(typename enable_if<
sizeof(C_T) ==
sizeof(C_T(static_cast<C_AT_1>(*static_cast<typename remove_reference<
C_AT_1>::type*>(0)),
static_cast<C_AT_2>(*static_cast<typename remove_reference<
C_AT_2>::type*>(0)),
static_cast<C_AT_3>(*static_cast<typename remove_reference<
C_AT_3>::type*>(0))))>::type*);

template <typename, typename, typename, typename>
static int test(...);

public:
static const bool value = (sizeof(test<T, AT_1, AT_2, AT_3>(0)) == sizeof(bool));
};

template <typename T, typename AT_1, typename AT_2>
class is_constructible_impl<T, AT_1, AT_2, void, void>
{
private:
template <typename C_T, typename C_AT_1, typename C_AT_2>
static bool test(typename enable_if<
sizeof(C_T) ==
sizeof(C_T(static_cast<C_AT_1>(*static_cast<typename remove_reference<
C_AT_1>::type*>(0)),
static_cast<C_AT_2>(*static_cast<typename remove_reference<
C_AT_2>::type*>(0))))>::type*);

template <typename, typename, typename>
static int test(...);

public:
static const bool value = (sizeof(test<T, AT_1, AT_2>(0)) == sizeof(bool));
};

template <typename T, typename AT_1>
class is_constructible_impl<T, AT_1, void, void, void>
{
private:
template <typename C_T, typename C_AT_1>
static bool test(typename enable_if<
sizeof(C_T) ==
sizeof(C_T(static_cast<C_AT_1>(
*static_cast<typename remove_reference<C_AT_1>::type*>(
0))))>::type*);

template <typename, typename>
static int test(...);

public:
static const bool value = (sizeof(test<T, AT_1>(0)) == sizeof(bool));
};

template <typename T>
class is_constructible_impl<T, void, void, void, void>
{
private:
template <typename C_T>
static C_T testFun(C_T);

template <typename C_T>
static bool test(typename enable_if<sizeof(C_T) == sizeof(testFun(C_T()))>::type*);

template <typename>
static int test(...);

public:
static const bool value = (sizeof(test<T>(0)) == sizeof(bool));
};

template <typename T, typename AT_1 = void, typename AT_2 = void, typename AT_3 = void,
typename AT_4 = void>
class is_constructible_impl_ptr
{
public:
static const bool value = false;
};

template <typename T, typename AT_1>
class is_constructible_impl_ptr<
T, AT_1, typename enable_if<is_pointer<typename remove_reference<T>::type>::value,
void>::type,
void, void>
{
private:
template <typename C_T>
static bool test(C_T);

template <typename>
static int test(...);

public:
static const bool value = (sizeof(test<T>(static_cast<AT_1>(0))) == sizeof(bool));
};

template <typename T>
class is_constructible_impl_ptr<T, void, void, void, void>
{
public:
static const bool value = true;
};

// is_constructible<> taken from here: http://stackoverflow.com/a/40443701/3162383
// for GCC/Clang gives the same results as std::is_constructible<> - see here: https://wandbox.org/permlink/bNWr7Ii2fuz4Vf7A
// MSVC support:
// - only 1 argument
// - for versions before 2012 read the CAUTION comment below
// currently intended for use only in the Approx() helper for strong typedefs of double
#ifndef _MSC_VER
template <typename T, typename AT_1 = void, typename AT_2 = void, typename AT_3 = void,
typename AT_4 = void>
class is_constructible
{
public:
static const bool value =
(is_pointer<typename remove_reference<T>::type>::value ?
is_constructible_impl_ptr<T, AT_1, AT_2, AT_3, AT_4>::value :
is_constructible_impl<T, AT_1, AT_2, AT_3, AT_4>::value);
};
#elif defined(_MSC_VER) && (_MSC_VER >= 1700)
template <typename T, typename AT_1>
struct is_constructible
{ static const bool value = __is_constructible(T, AT_1); };
#elif defined(_MSC_VER)
// !!! USE WITH CAUTION !!!
// will always return false - unable to implement this for versions of MSVC older than 2012 for now...
template <typename T, typename AT_1>
struct is_constructible
{ static const bool value = false; };
#endif // _MSC_VER
} // namespace traits

template <typename T>
struct deferred_false
{ static const bool value = false; };
Expand Down Expand Up @@ -755,14 +970,22 @@ class DOCTEST_INTERFACE Approx
, m_scale(other.m_scale)
, m_value(other.m_value) {}

Approx operator()(double value) {
Approx operator()(double value) const {
Approx approx(value);
approx.epsilon(m_epsilon);
approx.scale(m_scale);
return approx;
}

template <typename T>
explicit Approx(typename detail::traits::enable_if<
detail::traits::is_constructible<double, T>::value, T>::type value) {
*this = Approx(static_cast<double>(value));
}

// clang-format off
// overloads for double - the first one is necessary as it is in the implementation part of doctest
// as for the others - keeping them for potentially faster compile times
DOCTEST_INTERFACE friend bool operator==(double lhs, Approx const& rhs);
friend bool operator==(Approx const& lhs, double rhs) { return operator==(rhs, lhs); }
friend bool operator!=(double lhs, Approx const& rhs) { return !operator==(lhs, rhs); }
Expand All @@ -775,6 +998,23 @@ class DOCTEST_INTERFACE Approx
friend bool operator< (Approx const& lhs, double rhs) { return lhs.m_value < rhs && lhs != rhs; }
friend bool operator> (double lhs, Approx const& rhs) { return lhs > rhs.m_value && lhs != rhs; }
friend bool operator> (Approx const& lhs, double rhs) { return lhs.m_value > rhs && lhs != rhs; }

#define DOCTEST_APPROX_PREFIX \
template <typename T> friend typename detail::traits::enable_if<detail::traits::is_constructible<double, T>::value, bool>::type

DOCTEST_APPROX_PREFIX operator==(const T& lhs, const Approx& rhs) { return operator==(double(lhs), rhs); }
DOCTEST_APPROX_PREFIX operator==(const Approx& lhs, const T& rhs) { return operator==(rhs, lhs); }
DOCTEST_APPROX_PREFIX operator!=(const T& lhs, const Approx& rhs) { return !operator==(lhs, rhs); }
DOCTEST_APPROX_PREFIX operator!=(const Approx& lhs, const T& rhs) { return !operator==(rhs, lhs); }
DOCTEST_APPROX_PREFIX operator<=(const T& lhs, const Approx& rhs) { return double(lhs) < rhs.m_value || lhs == rhs; }
DOCTEST_APPROX_PREFIX operator<=(const Approx& lhs, const T& rhs) { return lhs.m_value < double(rhs) || lhs == rhs; }
DOCTEST_APPROX_PREFIX operator>=(const T& lhs, const Approx& rhs) { return double(lhs) > rhs.m_value || lhs == rhs; }
DOCTEST_APPROX_PREFIX operator>=(const Approx& lhs, const T& rhs) { return lhs.m_value > double(rhs) || lhs == rhs; }
DOCTEST_APPROX_PREFIX operator< (const T& lhs, const Approx& rhs) { return double(lhs) < rhs.m_value && lhs != rhs; }
DOCTEST_APPROX_PREFIX operator< (const Approx& lhs, const T& rhs) { return lhs.m_value < double(rhs) && lhs != rhs; }
DOCTEST_APPROX_PREFIX operator> (const T& lhs, const Approx& rhs) { return double(lhs) > rhs.m_value && lhs != rhs; }
DOCTEST_APPROX_PREFIX operator> (const Approx& lhs, const T& rhs) { return lhs.m_value > double(rhs) && lhs != rhs; }
#undef DOCTEST_APPROX_PREFIX
// clang-format on

Approx& epsilon(double newEpsilon) {
Expand Down Expand Up @@ -934,9 +1174,6 @@ namespace detail
template<> struct not_char_pointer<const char*> { enum { value = false }; };

template<class T> struct can_use_op : not_char_pointer<typename decay_array<T>::type> {};

template<bool, class = void> struct enable_if {};
template<class T> struct enable_if<true, T> { typedef T type; };
// clang-format on

struct TestFailureException
Expand Down Expand Up @@ -1066,7 +1303,7 @@ namespace detail
#ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
#define DOCTEST_COMPARISON_RETURN_TYPE bool
#else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
#define DOCTEST_COMPARISON_RETURN_TYPE typename enable_if<can_use_op<L>::value || can_use_op<R>::value, bool>::type
#define DOCTEST_COMPARISON_RETURN_TYPE typename traits::enable_if<can_use_op<L>::value || can_use_op<R>::value, bool>::type
inline bool eq(const char* lhs, const char* rhs) { return String(lhs) == String(rhs); }
inline bool ne(const char* lhs, const char* rhs) { return String(lhs) != String(rhs); }
inline bool lt(const char* lhs, const char* rhs) { return String(lhs) < String(rhs); }
Expand Down
Loading

0 comments on commit 0ad07b0

Please sign in to comment.