diff --git a/include/criterion/theories.h b/include/criterion/theories.h index f23c223..be9bf8b 100644 --- a/include/criterion/theories.h +++ b/include/criterion/theories.h @@ -24,9 +24,22 @@ #ifndef CRITERION_THEORIES_H_ # define CRITERION_THEORIES_H_ -# include +# ifdef __cplusplus +# include +using std::size_t; +# else +# include +# endif + # include "criterion.h" +# ifdef __cplusplus +template +constexpr size_t criterion_va_num__(const T &...) { + return sizeof...(T); +}; +# endif + CR_BEGIN_C_API struct criterion_theory_context; @@ -43,11 +56,19 @@ CR_API void cr_theory_call(struct criterion_theory_context *ctx, void (*fnptr)(v # define TheoryDataPoints(Category, Name) \ static struct criterion_datapoints IDENTIFIER_(Category, Name, dps)[] +# ifdef __cplusplus +# define CR_TH_VA_NUM(Type, ...) criterion_va_num__(__VA_ARGS__) +# define CR_TH_TEMP_ARRAY(Type, ...) []() { static Type arr[] = { __VA_ARGS__ }; return &arr; }() +# else +# define CR_TH_VA_NUM(Type, ...) sizeof ((Type[]) { __VA_ARGS__ }) / sizeof (Type) +# define CR_TH_TEMP_ARRAY(Type, ...) &(Type[]) { __VA_ARGS__ } +# endif + # define DataPoints(Type, ...) { \ sizeof (Type), \ - sizeof ((Type[]) { __VA_ARGS__ }) / sizeof (Type), \ + CR_EXPAND(CR_TH_VA_NUM(Type, __VA_ARGS__)), \ #Type, \ - &(Type[]) { __VA_ARGS__ }, \ + CR_EXPAND(CR_TH_TEMP_ARRAY(Type, __VA_ARGS__)), \ } struct criterion_datapoints { diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 5ab40fb..efbef5e 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -30,6 +30,7 @@ set(SAMPLES description.cc other-crashes.cc simple.cc + theories.cc ) set(SCRIPTS diff --git a/samples/theories.cc b/samples/theories.cc new file mode 100644 index 0000000..62f8b91 --- /dev/null +++ b/samples/theories.cc @@ -0,0 +1,112 @@ +#ifdef _MSC_VER +#pragma warning(disable : 4090) +#endif + +#include +#ifdef __cplusplus +# include +#else +# include +#endif + +# define INT_DATAPOINTS DataPoints(int, 0, 1, 2, -1, -2, INT_MAX, INT_MIN) + +// Let's test the multiplicative properties of 32-bit integers: + +int bad_mul(int a, int b) { + return a * b; +} + +int bad_div(int a, int b) { + return a / b; +} + +TheoryDataPoints(algebra, bad_divide_is_inverse_of_multiply) = { + INT_DATAPOINTS, + INT_DATAPOINTS, +}; + +Theory((int a, int b), algebra, bad_divide_is_inverse_of_multiply) { + cr_assume(b != 0); + cr_assert_eq(a, bad_div(bad_mul(a, b), b)); +} + +// The above implementation of mul & div fails the test because of overflows, +// let's try again: + +long long good_mul(long long a, long long b) { + return a * b; +} + +long long good_div(long long a, long long b) { + return a / b; +} + +TheoryDataPoints(algebra, good_divide_is_inverse_of_multiply) = { + INT_DATAPOINTS, + INT_DATAPOINTS, +}; + +Theory((int a, int b), algebra, good_divide_is_inverse_of_multiply) { + cr_assume(b != 0); + cr_assert_eq(a, good_div(good_mul(a, b), b)); +} + +// For triangulation + +Test(algebra, multiplication_by_integer) { + cr_assert_eq(10, good_mul(5, 2)); +} + +// Another property test + +TheoryDataPoints(algebra, zero_is_absorbing) = { + INT_DATAPOINTS, + INT_DATAPOINTS, +}; + +Theory((int a, int b), algebra, zero_is_absorbing) { + cr_assume(a == 0 || b == 0); + cr_assert_eq(0, good_mul(a, b)); +} + +// Testing for various parameters + +struct my_object { + int foo; +}; + +struct my_object o = {42}; + +char test_str[] = {'t', 'e', 's', 't', '\0'}; + +TheoryDataPoints(theory, misc) = { + DataPoints(char, 'a'), + DataPoints(bool, true), + DataPoints(short, 1), + DataPoints(int, 1), + DataPoints(long, 1), + DataPoints(long long, 1), + DataPoints(float, 3.14f), + DataPoints(double, 3.14), + DataPoints(char *, test_str), + DataPoints(const char *, "other test"), + DataPoints(struct my_object *, &o), +}; + +Theory((char c, bool b, short s, int i, long l, long long ll, float f, double d, char *str, const char *cstr, struct my_object *obj), theory, misc) { + cr_assert(b); + cr_assert_eq(c, 'a'); + cr_assert_eq(s, 1); + cr_assert_eq(i, 1); + cr_assert_eq(l, 1); + cr_assert_eq(ll, 1); + cr_assert_eq(f, 3.14f); + cr_assert_eq(d, 3.14); + cr_assert_strings_eq(str, "test"); + cr_assert_strings_eq(cstr, "other test"); + cr_assert_eq(obj->foo, 42); + + // abort to see the formatted string of all parameters + cr_abort_test(NULL); +}