Newer
Older
utest.h / utest.h
@Neil Henning Neil Henning on 30 Dec 2015 16 KB Apparently linux needs that?
// The latest version of this library is available on GitHub;
//   https://github.com/sheredom/utest.h

// This is free and unencumbered software released into the public domain.
//
// Anyone is free to copy, modify, publish, use, compile, sell, or
// distribute this software, either in source code form or as a compiled
// binary, for any purpose, commercial or non-commercial, and by any
// means.
//
// In jurisdictions that recognize copyright laws, the author or authors
// of this software dedicate any and all copyright interest in the
// software to the public domain. We make this dedication for the benefit
// of the public at large and to the detriment of our heirs and
// successors. We intend this dedication to be an overt act of
// relinquishment in perpetuity of all present and future rights to this
// software under copyright law.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// For more information, please refer to <http://unlicense.org/>

#ifndef SHEREDOM_UTEST_H_INCLUDED
#define SHEREDOM_UTEST_H_INCLUDED

#ifdef _MSC_VER
#pragma warning(push, 1)
#endif

#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if defined(_MSC_VER)
#pragma warning(pop)
#endif

#if defined(_MSC_VER)
#if defined(_M_IX86)
#define _X86_
#endif

#if defined(_M_AMD64)
#define _AMD64_
#endif

#pragma warning(push, 1)
#include <windef.h>
#include <winbase.h>
#pragma warning(pop)

#elif defined(__linux__)

// slightly obscure include here - we need to include glibc's features.h, but
// we don't want to just include a header that might not be defined for other
// c libraries like musl. Instead we include limits.h, which we know on all
// glibc distributions includes features.h
#include <limits.h>

#if defined(__GLIBC__) && defined(__GLIBC_MINOR__)
#include <time.h>

#if ((2 < __GLIBC__) || ((2 == __GLIBC__) && (17 <= __GLIBC_MINOR__)))
// glibc is version 2.17 or above, so we can just use clock_gettime
#define UTEST_USE_CLOCKGETTIME
#else // ((2 < __GLIBC__) || ((2 == __GLIBC__) && (17 <= __GLIBC_MINOR__)))
#include <unistd.h>
#include <sys/syscall.h>
#endif // ((2 < __GLIBC__) || ((2 == __GLIBC__) && (17 <= __GLIBC_MINOR__)))
#endif // defined(__GLIBC__) && defined(__GLIBC_MINOR__)

#elif defined(__APPLE__)
#include <mach/mach_time.h>
#endif

#if defined(_MSC_VER)
#define UTEST_PRId64 "I64d"
#define UTEST_PRIu64 "I64u"
#define UTEST_INLINE __forceinline

#pragma section(".CRT$XCU", read)
#define UTEST_INITIALIZER(f)                                                   \
  static void __cdecl f(void);                                                 \
  __declspec(allocate(".CRT$XCU")) void(__cdecl * f##_)(void) = f;             \
  static void __cdecl f(void)
#else
#define __STDC_FORMAT_MACROS 1
#include <inttypes.h>

#define UTEST_PRId64 PRId64
#define UTEST_PRIu64 PRIu64
#define UTEST_INLINE inline

#define UTEST_INITIALIZER(f)                                                   \
  static void f(void) __attribute__((constructor));                            \
  static void f(void)
#endif

#if defined(__cplusplus)
#define UTEST_CAST(type, x) static_cast<type>(x)
#define UTEST_PTR_CAST(type, x) reinterpret_cast<type>(x)
#define UTEST_EXTERN extern "C"
#else
#define UTEST_CAST(type, x) ((type)x)
#define UTEST_PTR_CAST(type, x) ((type)x)
#define UTEST_EXTERN extern
#endif

static UTEST_INLINE int64_t utest_ns(void) {
#ifdef _MSC_VER
  LARGE_INTEGER counter;
  LARGE_INTEGER frequency;
  QueryPerformanceCounter(&counter);
  QueryPerformanceFrequency(&frequency);
  return UTEST_CAST(int64_t,
                    (counter.QuadPart * 1000000000) / frequency.QuadPart);
#elif defined(__linux)
  struct timespec ts;
  const clockid_t cid = CLOCK_REALTIME;
#if defined(UTEST_USE_CLOCKGETTIME)
  clock_gettime(cid, &ts);
#else
  syscall(SYS_clock_gettime, cid, &ts);
#endif
  return UTEST_CAST(int64_t, ts.tv_sec) * 1000 * 1000 * 1000 + ts.tv_nsec;
#elif __APPLE__
  return UTEST_CAST(int64_t, mach_absolute_time());
#endif
}

typedef void (*utest_testcase_t)(int *);

struct utest_state_s {
  utest_testcase_t *testcases;
  const char **testcase_names;
  size_t testcases_length;
  const char *filter;
};

#if defined(_MSC_VER)
#define UTEST_WEAK __forceinline
#else
#define UTEST_WEAK __attribute__((weak))
#endif

#if defined(__cplusplus)
// if we are using c++ we can use overloaded methods (its in the language)
#define UTEST_OVERLOADABLE
#elif defined(__clang__)
// otherwise, if we are using clang with c we can use the overloadable attribute
#define UTEST_OVERLOADABLE __attribute__((overloadable))
#endif

#if defined(UTEST_OVERLOADABLE)
UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(float f);
UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(float f) {
  printf("%f", f);
}

UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(double d);
UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(double d) {
  printf("%f", d);
}

UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long double d);
UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long double d) {
  printf("%Lf", d);
}

UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(int i);
UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(int i) {
  printf("%d", i);
}

UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(unsigned int i);
UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(unsigned int i) {
  printf("%u", i);
}

UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long int i);
UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long int i) {
  printf("%ld", i);
}

UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long unsigned int i);
UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long unsigned int i) {
  printf("%lu", i);
}

// long long is a c++11 extension
// TODO: grok for c++11 version here
#if !defined(__cplusplus)
UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long int i);
UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long int i) {
  printf("%lld", i);
}

UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long unsigned int i);
UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long unsigned int i) {
  printf("%llu", i);
}
#endif
#else
// we don't have the ability to print the values we got, so we create a macro to
// tell our users we can't do anything fancy
#define utest_type_printer(...) printf("undef")
#endif

#define UTEST_ASSERT(x, y, cond)                                               \
  if (!((x)cond(y))) {                                                         \
    printf("%s:%u: Failure\n", __FILE__, __LINE__);                            \
    printf("  Expected : "); utest_type_printer(x); printf("\n");              \
    printf("    Actual : "); utest_type_printer(y); printf("\n");              \
    *utest_result = 1;                                                         \
    return;                                                                    \
  }

#define ASSERT_TRUE(x)                                                         \
  if (!(x)) {                                                                  \
    printf("%s:%u: Failure\n", __FILE__, __LINE__);                            \
    printf("  Expected : true\n");                                             \
    printf("    Actual : %s\n", (x) ? "true" : "false");                       \
    *utest_result = 1;                                                         \
    return;                                                                    \
  }

#define ASSERT_FALSE(x)                                                        \
  if (x) {                                                                     \
    printf("%s:%u: Failure\n", __FILE__, __LINE__);                            \
    printf("  Expected : false\n");                                            \
    printf("    Actual : %s\n", (x) ? "true" : "false");                       \
    *utest_result = 1;                                                         \
    return;                                                                    \
  }

#define ASSERT_EQ(x, y) UTEST_ASSERT(x, y, == )
#define ASSERT_NE(x, y) UTEST_ASSERT(x, y, != )
#define ASSERT_LT(x, y) UTEST_ASSERT(x, y, < )
#define ASSERT_LE(x, y) UTEST_ASSERT(x, y, <= )
#define ASSERT_GT(x, y) UTEST_ASSERT(x, y, > )
#define ASSERT_GE(x, y) UTEST_ASSERT(x, y, >= )

#define ASSERT_STREQ(x, y)                                                     \
  if (0 != strcmp(x, y)) {                                                     \
    printf("%s:%u: Failure\n", __FILE__, __LINE__);                            \
    printf("  Expected : \"%s\"\n", x);                                        \
    printf("    Actual : \"%s\"\n", y);                                        \
    *utest_result = 1;                                                         \
    return;                                                                    \
  }

#define ASSERT_STRNE(x, y)                                                     \
  if (0 == strcmp(x, y)) {                                                     \
    printf("%s:%u: Failure\n", __FILE__, __LINE__);                            \
    printf("  Expected : \"%s\"\n", x);                                        \
    printf("    Actual : \"%s\"\n", y);                                        \
    *utest_result = 1;                                                         \
    return;                                                                    \
  }

#define UTEST_EXPECT(x, y, cond)                                               \
  if (!((x)cond(y))) {                                                         \
    printf("%s:%u: Failure\n", __FILE__, __LINE__);                            \
    *utest_result = 1;                                                         \
  }

#define EXPECT_TRUE(x)                                                         \
  if (!(x)) {                                                                  \
    printf("%s:%u: Failure\n", __FILE__, __LINE__);                            \
    printf("  Expected : true\n");                                             \
    printf("    Actual : %s\n", (x) ? "true" : "false");                       \
    *utest_result = 1;                                                         \
  }

#define EXPECT_FALSE(x)                                                        \
  if (x) {                                                                     \
    printf("%s:%u: Failure\n", __FILE__, __LINE__);                            \
    printf("  Expected : false\n");                                            \
    printf("    Actual : %s\n", (x) ? "true" : "false");                       \
    *utest_result = 1;                                                         \
  }

#define EXPECT_EQ(x, y) UTEST_EXPECT(x, y, == )
#define EXPECT_NE(x, y) UTEST_EXPECT(x, y, != )
#define EXPECT_LT(x, y) UTEST_EXPECT(x, y, < )
#define EXPECT_LE(x, y) UTEST_EXPECT(x, y, <= )
#define EXPECT_GT(x, y) UTEST_EXPECT(x, y, > )
#define EXPECT_GE(x, y) UTEST_EXPECT(x, y, >= )

#define EXPECT_STREQ(x, y)                                                     \
  if (0 != strcmp(x, y)) {                                                     \
    printf("%s:%u: Failure\n", __FILE__, __LINE__);                            \
    printf("  Expected : \"%s\"\n", x);                                        \
    printf("    Actual : \"%s\"\n", y);                                        \
    *utest_result = 1;                                                         \
  }

#define EXPECT_STRNE(x, y)                                                     \
  if (0 == strcmp(x, y)) {                                                     \
    printf("%s:%u: Failure\n", __FILE__, __LINE__);                            \
    printf("  Expected : \"%s\"\n", x);                                        \
    printf("    Actual : \"%s\"\n", y);                                        \
    *utest_result = 1;                                                         \
  }

#define TESTCASE(set, name)                                                    \
  static void utest_run_##set##_##name(int *utest_result);                     \
  UTEST_INITIALIZER(utest_register_##set##_##name) {                           \
    const size_t index = utest_state.testcases_length++;                       \
    utest_state.testcases = UTEST_PTR_CAST(                                    \
        utest_testcase_t *,                                                    \
        realloc(UTEST_PTR_CAST(void *, utest_state.testcases),                 \
                sizeof(utest_testcase_t) * utest_state.testcases_length));     \
    utest_state.testcases[index] = &utest_run_##set##_##name;                  \
    utest_state.testcase_names = UTEST_PTR_CAST(                               \
        const char **,                                                         \
        realloc(UTEST_PTR_CAST(void *, utest_state.testcase_names),            \
                sizeof(char *) * utest_state.testcases_length));               \
    utest_state.testcase_names[index] = #set "." #name;                        \
  }                                                                            \
  void utest_run_##set##_##name(int *utest_result)

// extern to the global state utest needs to execute
UTEST_EXTERN struct utest_state_s utest_state;

UTEST_WEAK int utest_main(int argc, const char* const argv[]);
UTEST_WEAK int utest_main(int argc, const char* const argv[]) {
  size_t failed = 0;
  size_t index = 0;
  size_t *failed_testcases = 0;
  size_t failed_testcases_length = 0;

  (void)argc; (void)argv;
  printf("\033[32m[==========]\033[0m Running %" UTEST_PRIu64 " test cases.\n",
         UTEST_CAST(uint64_t, utest_state.testcases_length));
  for (index = 0; index < utest_state.testcases_length; index++) {
    int result = 0;
    int64_t ns = 0;
    printf("\033[32m[ RUN      ]\033[0m %s\n",
           utest_state.testcase_names[index]);
    ns = utest_ns();
    utest_state.testcases[index](&result);
    ns = utest_ns() - ns;
    if (0 != result) {
      const size_t failed_testcase_index = failed_testcases_length++;
      failed_testcases = UTEST_PTR_CAST(size_t*, realloc(UTEST_PTR_CAST(void *,
        failed_testcases), sizeof(size_t) * failed_testcases_length));
      failed_testcases[failed_testcase_index] = index;
      failed++;
      printf("\033[31m[  FAILED  ]\033[0m %s (%" UTEST_PRId64 "ns)\n",
             utest_state.testcase_names[index], ns);
    } else {
      printf("\033[32m[       OK ]\033[0m %s (%" UTEST_PRId64 "ns)\n",
             utest_state.testcase_names[index], ns);
    }
  }
  printf("\033[32m[==========]\033[0m %" UTEST_PRIu64 " test cases ran.\n",
         UTEST_CAST(uint64_t, utest_state.testcases_length));
  printf("\033[32m[  PASSED  ]\033[0m %" UTEST_PRIu64 " tests.\n",
         UTEST_CAST(uint64_t, utest_state.testcases_length - failed));
  if (0 != failed) {
    printf("\033[31m[  FAILED  ]\033[0m %" UTEST_PRIu64 " tests, listed below:\n",
           UTEST_CAST(uint64_t, failed));
    for (index = 0; index < failed_testcases_length; index++) {
      printf("\033[31m[  FAILED  ]\033[0m %s\n",
             utest_state.testcase_names[failed_testcases[index]]);
    }
  }
  free(UTEST_PTR_CAST(void *, failed_testcases));
  free(UTEST_PTR_CAST(void *, utest_state.testcases));
  free(UTEST_PTR_CAST(void *, utest_state.testcase_names));
  return UTEST_CAST(int, failed);
}

// we need, in exactly one source file, define the global struct that will hold
// the data we need to run utest. This macro allows the user to declare the
// data without having to use the UTEST_MAIN macro, thus allowing them to write
// their own main() function.
#define UTEST_STATE() struct utest_state_s utest_state

// define a main() function to call into utest.h and start executing tests! A
// user can optionally not use this macro, and instead define their own main()
// function and manually call utest_main. The user must, in exactly one source
// file, use the UTEST_STATE macro to declare a global struct variable that
// utest requires.
#define UTEST_MAIN()                                                           \
  UTEST_STATE();                                                               \
  int main(int argc, const char* const argv[]) {                               \
    return utest_main(argc, argv);                                             \
  }

#endif // SHEREDOM_UTEST_H_INCLUDED