diff options
| -rw-r--r-- | .clang-format | 199 | ||||
| -rw-r--r-- | .clangd | 9 | ||||
| -rw-r--r-- | .gitignore | 30 | ||||
| -rw-r--r-- | LICENSE | 19 | ||||
| -rw-r--r-- | Makefile.in | 75 | ||||
| -rw-r--r-- | README.md | 87 | ||||
| -rwxr-xr-x | configure | 137 | ||||
| -rwxr-xr-x | impl | bin | 0 -> 33560 bytes | |||
| -rw-r--r-- | include/impl/base.h | 14 | ||||
| -rw-r--r-- | src/base.c | 13 | ||||
| -rw-r--r-- | src/main.c | 23 | ||||
| -rw-r--r-- | tests/test_main.c | 19 | ||||
| -rw-r--r-- | thirdparty/utest/utest.h | 1726 | ||||
| -rwxr-xr-x | tools/gentarball.sh | 41 |
14 files changed, 2392 insertions, 0 deletions
diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..efe9f12 --- /dev/null +++ b/.clang-format @@ -0,0 +1,199 @@ +# Basic .clang-format +--- +BasedOnStyle: WebKit +AlignAfterOpenBracket: DontAlign +AlignConsecutiveMacros: AcrossEmptyLines +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: false +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: InlineOnly +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: TopLevelDefinitions +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +BinPackArguments: true +BinPackParameters: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: WebKit +BreakBeforeTernaryOperators: false +# TODO: BreakStringLiterals can cause very strange formatting so turn it off? +BreakStringLiterals: false +# Prefer: +# some_var = function(arg1, +# arg2) +# over: +# some_var = +# function(arg1, arg2) +PenaltyBreakAssignment: 100 +# Prefer: +# some_long_function(arg1, arg2 +# arg3) +# over: +# some_long_function( +# arg1, arg2, arg3) +PenaltyBreakBeforeFirstCallParameter: 100 +CompactNamespaces: true +DerivePointerAlignment: false +DisableFormat: false +ForEachMacros: + - ARB_ARRFOREACH + - ARB_ARRFOREACH_REVWCOND + - ARB_ARRFOREACH_REVERSE + - ARB_FOREACH + - ARB_FOREACH_FROM + - ARB_FOREACH_SAFE + - ARB_FOREACH_REVERSE + - ARB_FOREACH_REVERSE_FROM + - ARB_FOREACH_REVERSE_SAFE + - BIT_FOREACH_ISCLR + - BIT_FOREACH_ISSET + - CPU_FOREACH + - CPU_FOREACH_ISCLR + - CPU_FOREACH_ISSET + - FOREACH_THREAD_IN_PROC + - FOREACH_PROC_IN_SYSTEM + - FOREACH_PRISON_CHILD + - FOREACH_PRISON_DESCENDANT + - FOREACH_PRISON_DESCENDANT_LOCKED + - FOREACH_PRISON_DESCENDANT_LOCKED_LEVEL + - MNT_VNODE_FOREACH_ALL + - MNT_VNODE_FOREACH_ACTIVE + - RB_FOREACH + - RB_FOREACH_FROM + - RB_FOREACH_SAFE + - RB_FOREACH_REVERSE + - RB_FOREACH_REVERSE_FROM + - RB_FOREACH_REVERSE_SAFE + - SLIST_FOREACH + - SLIST_FOREACH_FROM + - SLIST_FOREACH_FROM_SAFE + - SLIST_FOREACH_SAFE + - SLIST_FOREACH_PREVPTR + - SPLAY_FOREACH + - LIST_FOREACH + - LIST_FOREACH_FROM + - LIST_FOREACH_FROM_SAFE + - LIST_FOREACH_SAFE + - STAILQ_FOREACH + - STAILQ_FOREACH_FROM + - STAILQ_FOREACH_FROM_SAFE + - STAILQ_FOREACH_SAFE + - TAILQ_FOREACH + - TAILQ_FOREACH_FROM + - TAILQ_FOREACH_FROM_SAFE + - TAILQ_FOREACH_REVERSE + - TAILQ_FOREACH_REVERSE_FROM + - TAILQ_FOREACH_REVERSE_FROM_SAFE + - TAILQ_FOREACH_REVERSE_SAFE + - TAILQ_FOREACH_SAFE + - VM_MAP_ENTRY_FOREACH + - VM_PAGE_DUMP_FOREACH +SpaceBeforeParens: ControlStatementsExceptForEachMacros +IndentCaseLabels: false +IndentPPDirectives: None +Language: Cpp +NamespaceIndentation: None +PointerAlignment: Right +ContinuationIndentWidth: 4 +IndentWidth: 8 +TabWidth: 8 +ColumnLimit: 80 +UseTab: Always +SpaceAfterCStyleCast: false +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^\"opt_.*\.h\"' + Priority: 1 + SortPriority: 10 + - Regex: '^<sys/cdefs\.h>' + Priority: 2 + SortPriority: 20 + - Regex: '^<sys/types\.h>' + Priority: 2 + SortPriority: 21 + - Regex: '^<sys/param\.h>' + Priority: 2 + SortPriority: 22 + - Regex: '^<sys/systm\.h>' + Priority: 2 + SortPriority: 23 + - Regex: '^<sys.*/' + Priority: 2 + SortPriority: 24 + - Regex: '^<vm/vm\.h>' + Priority: 3 + SortPriority: 30 + - Regex: '^<vm/' + Priority: 3 + SortPriority: 31 + - Regex: '^<machine/' + Priority: 4 + SortPriority: 40 + - Regex: '^<(x86|amd64|i386|xen)/' + Priority: 5 + SortPriority: 50 + - Regex: '^<dev/' + Priority: 6 + SortPriority: 60 + - Regex: '^<net.*/' + Priority: 7 + SortPriority: 70 + - Regex: '^<protocols/' + Priority: 7 + SortPriority: 71 + - Regex: '^<(fs|nfs(|client|server)|ufs)/' + Priority: 8 + SortPriority: 80 + - Regex: '^<[^/].*' + Priority: 9 + SortPriority: 90 + - Regex: '^\".*\"' + Priority: 10 + SortPriority: 100 +# LLVM's header include ordering style is almost the exact opposite of ours. +# Unfortunately, they have hard-coded their preferences into clang-format. +# Clobbering this regular expression to avoid matching prevents non-system +# headers from being forcibly moved to the top of the include list. +# http://llvm.org/docs/CodingStandards.html#include-style +IncludeIsMainRegex: 'BLAH_DONT_MATCH_ANYTHING' +SortIncludes: true +KeepEmptyLinesAtTheStartOfBlocks: false +TypenameMacros: + - ARB_ELMTYPE + - ARB_HEAD + - ARB8_HEAD + - ARB16_HEAD + - ARB32_HEAD + - ARB_ENTRY + - ARB8_ENTRY + - ARB16_ENTRY + - ARB32_ENTRY + - LIST_CLASS_ENTRY + - LIST_CLASS_HEAD + - LIST_ENTRY + - LIST_HEAD + - QUEUE_TYPEOF + - RB_ENTRY + - RB_HEAD + - SLIST_CLASS_HEAD + - SLIST_CLASS_ENTRY + - SLIST_HEAD + - SLIST_ENTRY + - SMR_POINTER + - SPLAY_ENTRY + - SPLAY_HEAD + - STAILQ_CLASS_ENTRY + - STAILQ_CLASS_HEAD + - STAILQ_ENTRY + - STAILQ_HEAD + - TAILQ_CLASS_ENTRY + - TAILQ_CLASS_HEAD + - TAILQ_ENTRY + - TAILQ_HEAD @@ -0,0 +1,9 @@ +# clangd configuration +CompileFlags: + Add: + - -Wall + - -Wextra + +Diagnostics: + UnusedIncludes: Strict + MissingIncludes: Strict diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fe6031c --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +# Build artifacts +*.o +*.a +*.so +*.dylib +*.dll +*.exe + +# Build directories +build/ +debug/ +release/ +dist/ + +# Generated files +config.h +Makefile + +# Editor files +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS files +.DS_Store +Thumbs.db +compile_commands.json +.cache @@ -0,0 +1,19 @@ +Copyright (c) 2026, impl authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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 OR COPYRIGHT HOLDERS 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. diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..898090d --- /dev/null +++ b/Makefile.in @@ -0,0 +1,75 @@ +# Makefile for impl +VERSION = @VERSION@ + +# Environment and values saved by the configure script +CC = @CC@ +CCOM = @CCOM@ +AR = @AR@ +RANLIB = @RANLIB@ +RM = @RM@ +INSTALL = @INSTALL@ +OPTIMISE = @OPTIMISE@ +PREFIX = @PREFIX@ +SRCDIR = @SRCDIR@ + +# Find all .c files in src/ recursively +SRCS := $(shell find $(SRCDIR)/src -name '*.c' -type f) +# Generate object file names (strip SRCDIR/src/ prefix and change .c to .o) +OBJS := $(patsubst $(SRCDIR)/src/%.c,%.o,$(SRCS)) + +# VPATH for finding source files +VPATH = $(SRCDIR)/src:$(shell find $(SRCDIR)/src -type d 2>/dev/null | tr '\n' ':') + +# Compiler flags +COPTFLAGS_gcc_debug = -O0 -g3 -Wall -Wextra +COPTFLAGS_gcc_release = -O2 -DNDEBUG +COPTFLAGS_clang_debug = -O0 -g3 -Wall -Wextra +COPTFLAGS_clang_release = -O2 -DNDEBUG +COPTFLAGS = $(COPTFLAGS_$(CCOM)_$(OPTIMISE)) + +CSTDFLAGS_gcc = -std=c11 -pedantic +CSTDFLAGS_clang = -std=c11 -pedantic +CSTDFLAGS = $(CSTDFLAGS_$(CCOM)) + +CFLAGS = $(CSTDFLAGS) $(COPTFLAGS) -I$(SRCDIR)/include -I$(SRCDIR)/thirdparty/utest -I. -DHAVE_CONFIG_H=1 +LDFLAGS = + +# Targets +BINARY = impl + +.PHONY: all clean install check + +all: $(BINARY) + +# Create subdirectories for object files if needed +$(BINARY): $(OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) + +# Suffix rule for compiling (uses VPATH to find .c files) +.SUFFIXES: .c .o + +.c.o: + @test -d $(dir $@) || mkdir -p $(dir $@) + $(CC) $(CFLAGS) -c -o $@ $< + +clean: + $(RM) -f $(BINARY) $(OBJS) + $(RM) -f test_main test_main.o + find . -type f -name '*.o' -delete 2>/dev/null || true + +install: $(BINARY) + $(INSTALL) -d $(DESTDIR)$(PREFIX)/bin + $(INSTALL) -m 755 $(BINARY) $(DESTDIR)$(PREFIX)/bin/ + +# Test target - links with all objects except main.o +TEST_OBJS := $(filter-out main.o,$(OBJS)) + +check: test_main + ./test_main + +test_main: test_main.o $(TEST_OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ + +test_main.o: $(SRCDIR)/tests/test_main.c $(SRCDIR)/include/impl/base.h + $(CC) $(CFLAGS) -c -o $@ $< + diff --git a/README.md b/README.md new file mode 100644 index 0000000..646d783 --- /dev/null +++ b/README.md @@ -0,0 +1,87 @@ +# IMPL + +Version 0.1.0 + +## Description + +A C project created with the gcli-style build system. + +## Building + +### Dependencies + +Required: +- C11 compiler (gcc or clang) +- make + +### Build Instructions + +```bash +./configure +make +``` + +### Debug Build + +```bash +./configure --debug +make +``` + +### Out-of-tree builds (optional) + +You can also build in a separate directory: + +```bash +mkdir build +cd build +../configure +make +``` + +### Installation + +```bash +sudo make install +``` + +Default prefix is `/usr/local`. To change: + +```bash +./configure --prefix=/usr +``` + +## Testing + +```bash +make check +``` + +## IDE Support + +### clangd / LSP + +To generate `compile_commands.json` for clangd, use one of these methods: + +**Option 1: Using compiledb (recommended)** +```bash +pip install compiledb +compiledb make +``` + +**Option 2: Using bear** +```bash +bear -- make +``` + +If the file is empty, try a clean build: +```bash +make clean +bear -- make +``` + +## Usage + +```bash +./impl +``` diff --git a/configure b/configure new file mode 100755 index 0000000..0608b83 --- /dev/null +++ b/configure @@ -0,0 +1,137 @@ +#!/usr/bin/env sh +# +# Configure script for impl +# + +CONFIGURE_CMD_ARGS="${*}" + +PACKAGE_VERSION="0.1.0" +PACKAGE_DATE="$(date +%Y-%m-%d)" +PACKAGE_STRING="impl $PACKAGE_VERSION" +PACKAGE_BUGREPORT="ethan@gweithio.com" +PACKAGE_URL="https://sixfourtwelve.com/repos/impl.git" + +find_program() { + varname=$1 + shift + + printf "Checking for $varname ..." >&2 + for x in $*; do + if command -v $x >/dev/null 2>&1 && [ -x $(command -v $x) ]; then + binary="$(command -v $x)" + printf " $binary\n" >&2 + echo "${binary}" + return 0 + fi + done + printf " not found\n" >&2 + return 1 +} + +die() { + printf "%s\n" "${*}" + exit 1 +} + +# Default values +PREFIX="/usr/local" +OPTIMISE="release" +CC="" +AR="ar" +RANLIB="ranlib" +RM="rm" +INSTALL="install" + +# Parse arguments +while [ $# -gt 0 ]; do + case "$1" in + --prefix=*) + PREFIX="${1#--prefix=}" + ;; + --prefix) + shift + PREFIX="$1" + ;; + --debug) + OPTIMISE="debug" + ;; + --release) + OPTIMISE="release" + ;; + --help) + cat <<EOF +Configure script for impl + +Options: + --prefix=PREFIX Installation prefix (default: /usr/local) + --debug Build with debug symbols, no optimization + --release Build with optimizations (default) + --help Show this help message + +Environment variables: + CC C compiler + AR Archiver + RANLIB ranlib + CFLAGS Additional C flags + LDFLAGS Additional linker flags +EOF + exit 0 + ;; + *) + die "Unknown option: $1" + ;; + esac + shift +done + +# Find required tools +if [ -z "$CC" ]; then + CC=$(find_program CC cc clang gcc) || die "No C compiler found" +fi + +# Use basename only for better tool compatibility (e.g., bear) +CC=$(basename "$CC") + +# Detect compiler type +if $CC --version 2>&1 | grep -i clang >/dev/null; then + CCOM="clang" +elif $CC --version 2>&1 | grep -i gcc >/dev/null; then + CCOM="gcc" +else + CCOM="unknown" +fi + +printf "Compiler: %s (%s)\n" "$CC" "$CCOM" + +# Generate config.h +cat > config.h <<EOF_CONFIG +#ifndef CONFIG_H +#define CONFIG_H + +#define PACKAGE_VERSION "$PACKAGE_VERSION" +#define PACKAGE_STRING "$PACKAGE_STRING" +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +#define PACKAGE_URL "$PACKAGE_URL" + +#endif /* CONFIG_H */ +EOF_CONFIG + +# Determine source directory +SRCDIR="$(dirname "$0")" +SRCDIR="$(cd "$SRCDIR" && pwd)" + +# Generate Makefile from Makefile.in +sed \ + -e "s|@PREFIX@|$PREFIX|g" \ + -e "s|@CC@|$CC|g" \ + -e "s|@CCOM@|$CCOM|g" \ + -e "s|@AR@|$AR|g" \ + -e "s|@RANLIB@|$RANLIB|g" \ + -e "s|@RM@|$RM|g" \ + -e "s|@INSTALL@|$INSTALL|g" \ + -e "s|@OPTIMISE@|$OPTIMISE|g" \ + -e "s|@VERSION@|$PACKAGE_VERSION|g" \ + -e "s|@SRCDIR@|$SRCDIR|g" \ + < "$SRCDIR/Makefile.in" > Makefile + +echo "Configuration complete. Run 'make' to build." Binary files differdiff --git a/include/impl/base.h b/include/impl/base.h new file mode 100644 index 0000000..1cc3986 --- /dev/null +++ b/include/impl/base.h @@ -0,0 +1,14 @@ +#ifndef IMPL_BASE_H +#define IMPL_BASE_H + +/** + * Initialize impl + */ +void impl_init(void); + +/** + * Cleanup impl resources + */ +void impl_cleanup(void); + +#endif /* IMPL_BASE_H */ diff --git a/src/base.c b/src/base.c new file mode 100644 index 0000000..08c6568 --- /dev/null +++ b/src/base.c @@ -0,0 +1,13 @@ +#include "impl/base.h" + +void +impl_init(void) +{ + /* Initialize resources here */ +} + +void +impl_cleanup(void) +{ + /* Cleanup resources here */ +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..f724a95 --- /dev/null +++ b/src/main.c @@ -0,0 +1,23 @@ +#include <stdio.h> +#include <stdlib.h> + +#include "config.h" +#include "impl/base.h" + +int +main(int argc, char **argv) +{ + (void)argc; + (void)argv; + + printf("%s version %s\n", PACKAGE_STRING, PACKAGE_VERSION); + + /* Call a function from base.h */ + impl_init(); + + printf("Hello from impl!\n"); + + impl_cleanup(); + + return 0; +} diff --git a/tests/test_main.c b/tests/test_main.c new file mode 100644 index 0000000..1d1861f --- /dev/null +++ b/tests/test_main.c @@ -0,0 +1,19 @@ +#include "impl/base.h" +#include "utest.h" + +UTEST(impl, init_cleanup) +{ + impl_init(); + impl_cleanup(); + ASSERT_TRUE(1); +} + +UTEST(impl, basic_test) +{ + ASSERT_EQ(1, 1); + ASSERT_NE(1, 0); + ASSERT_TRUE(1); + ASSERT_FALSE(0); +} + +UTEST_MAIN() diff --git a/thirdparty/utest/utest.h b/thirdparty/utest/utest.h new file mode 100644 index 0000000..6516e7c --- /dev/null +++ b/thirdparty/utest/utest.h @@ -0,0 +1,1726 @@ +/* + 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 +/* + Disable warning about not inlining 'inline' functions. +*/ +#pragma warning(disable : 4710) + +/* + Disable warning about inlining functions that are not marked 'inline'. +*/ +#pragma warning(disable : 4711) + +/* + Disable warning for alignment padding added +*/ +#pragma warning(disable : 4820) + +#if _MSC_VER > 1900 +/* + Disable warning about preprocessor macros not being defined in MSVC headers. +*/ +#pragma warning(disable : 4668) + +/* + Disable warning about no function prototype given in MSVC headers. +*/ +#pragma warning(disable : 4255) + +/* + Disable warning about pointer or reference to potentially throwing function. +*/ +#pragma warning(disable : 5039) + +/* + Disable warning about macro expansion producing 'defined' has undefined + behavior. +*/ +#pragma warning(disable : 5105) +#endif + +#if _MSC_VER > 1930 +/* + Disable warning about 'const' variable is not used. +*/ +#pragma warning(disable : 5264) +#endif + +#pragma warning(push, 1) +#endif + +#if defined(_MSC_VER) && (_MSC_VER < 1920) +typedef __int64 utest_int64_t; +typedef unsigned __int64 utest_uint64_t; +typedef unsigned __int32 utest_uint32_t; +#else +#include <stdint.h> +typedef int64_t utest_int64_t; +typedef uint64_t utest_uint64_t; +typedef uint32_t utest_uint32_t; +#endif + +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#if defined(__cplusplus) +#if defined(_MSC_VER) && !defined(_CPPUNWIND) +/* We're on MSVC and the compiler is compiling without exception support! */ +#elif !defined(_MSC_VER) && !defined(__EXCEPTIONS) +/* We're on a GCC/Clang compiler that doesn't have exception support! */ +#else +#define UTEST_HAS_EXCEPTIONS 1 +#endif +#endif + +#if defined(UTEST_HAS_EXCEPTIONS) +#include <stdexcept> +#endif + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#if defined(__cplusplus) +#define UTEST_C_FUNC extern "C" +#else +#define UTEST_C_FUNC +#endif + +#define UTEST_TEST_PASSED (0) +#define UTEST_TEST_FAILURE (1) +#define UTEST_TEST_SKIPPED (2) + +#if defined(__TINYC__) +#define UTEST_ATTRIBUTE(a) __attribute((a)) +#else +#define UTEST_ATTRIBUTE(a) __attribute__((a)) +#endif + +#if defined(_MSC_VER) || defined(__MINGW64__) || defined(__MINGW32__) + +#if defined(__MINGW64__) || defined(__MINGW32__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#endif + +#if defined(_WINDOWS_) || defined(_WINDOWS_H) +typedef LARGE_INTEGER utest_large_integer; +#else +// use old QueryPerformanceCounter definitions (not sure is this needed in some +// edge cases or not) on Win7 with VS2015 these extern declaration cause "second +// C linkage of overloaded function not allowed" error +typedef union { + struct { + unsigned long LowPart; + long HighPart; + } DUMMYSTRUCTNAME; + struct { + unsigned long LowPart; + long HighPart; + } u; + utest_int64_t QuadPart; +} utest_large_integer; + +UTEST_C_FUNC __declspec(dllimport) int __stdcall QueryPerformanceCounter( + utest_large_integer *); +UTEST_C_FUNC __declspec(dllimport) int __stdcall QueryPerformanceFrequency( + utest_large_integer *); + +#if defined(__MINGW64__) || defined(__MINGW32__) +#pragma GCC diagnostic pop +#endif +#endif + +#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \ + defined(__NetBSD__) || defined(__DragonFly__) || defined(__sun__) || \ + defined(__HAIKU__) +/* + 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 +#include <sys/syscall.h> +#include <unistd.h> +#endif +#else // Other libc implementations +#include <time.h> +#define UTEST_USE_CLOCKGETTIME +#endif + +#elif defined(__APPLE__) +#include <time.h> +#endif + +#if defined(_MSC_VER) && (_MSC_VER < 1920) +#define UTEST_PRId64 "I64d" +#define UTEST_PRIu64 "I64u" +#else +#include <inttypes.h> + +#define UTEST_PRId64 PRId64 +#define UTEST_PRIu64 PRIu64 +#endif + +#if defined(__cplusplus) +#define UTEST_INLINE inline + +#if defined(__clang__) +#define UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") + +#define UTEST_INITIALIZER_END_DISABLE_WARNINGS _Pragma("clang diagnostic pop") +#else +#define UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS +#define UTEST_INITIALIZER_END_DISABLE_WARNINGS +#endif + +#define UTEST_INITIALIZER(f) \ + struct f##_cpp_struct { \ + f##_cpp_struct(); \ + }; \ + UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS static f##_cpp_struct \ + f##_cpp_global UTEST_INITIALIZER_END_DISABLE_WARNINGS; \ + f##_cpp_struct::f##_cpp_struct() +#elif defined(_MSC_VER) +#define UTEST_INLINE __forceinline + +#if defined(_WIN64) +#define UTEST_SYMBOL_PREFIX +#else +#define UTEST_SYMBOL_PREFIX "_" +#endif + +#if defined(__clang__) +#define UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wmissing-variable-declarations\"") + +#define UTEST_INITIALIZER_END_DISABLE_WARNINGS _Pragma("clang diagnostic pop") +#else +#define UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS +#define UTEST_INITIALIZER_END_DISABLE_WARNINGS +#endif + +#pragma section(".CRT$XCU", read) +#define UTEST_INITIALIZER(f) \ + static void __cdecl f(void); \ + UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS \ + __pragma(comment(linker, "/include:" UTEST_SYMBOL_PREFIX #f "_")) \ + UTEST_C_FUNC \ + __declspec(allocate(".CRT$XCU")) void(__cdecl * f##_)(void) = f; \ + UTEST_INITIALIZER_END_DISABLE_WARNINGS \ + static void __cdecl f(void) +#else +#if defined(__linux__) +#if defined(__clang__) +#if __has_warning("-Wreserved-id-macro") +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif +#endif + +#define __STDC_FORMAT_MACROS 1 + +#if defined(__clang__) +#if __has_warning("-Wreserved-id-macro") +#pragma clang diagnostic pop +#endif +#endif +#endif + +#define UTEST_INLINE inline + +#define UTEST_INITIALIZER(f) \ + static void f(void) UTEST_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" +#define UTEST_NULL NULL +#else +#define UTEST_CAST(type, x) ((type)(x)) +#define UTEST_PTR_CAST(type, x) ((type)(x)) +#define UTEST_EXTERN extern +#define UTEST_NULL 0 +#endif + +#ifdef _MSC_VER +/* + io.h contains definitions for some structures with natural padding. This is + uninteresting, but for some reason MSVC's behaviour is to warn about + including this system header. That *is* interesting +*/ +#pragma warning(disable : 4820) +#pragma warning(push, 1) +#include <io.h> +#pragma warning(pop) +#define UTEST_COLOUR_OUTPUT() (_isatty(_fileno(stdout))) +#else +#if defined(__EMSCRIPTEN__) +#include <emscripten/html5.h> +#define UTEST_COLOUR_OUTPUT() false +#else +#include <unistd.h> +#define UTEST_COLOUR_OUTPUT() (isatty(STDOUT_FILENO)) +#endif +#endif + +static UTEST_INLINE void *utest_realloc(void *const pointer, size_t new_size) { + void *const new_pointer = realloc(pointer, new_size); + + if (UTEST_NULL == new_pointer) { + free(pointer); + } + + return new_pointer; +} + +// Prevent 64-bit integer overflow when computing a timestamp by using a trick +// from Sokol: +// https://github.com/floooh/sokol/blob/189843bf4f86969ca4cc4b6d94e793a37c5128a7/sokol_time.h#L204 +static UTEST_INLINE utest_int64_t utest_mul_div(const utest_int64_t value, + const utest_int64_t numer, + const utest_int64_t denom) { + const utest_int64_t q = value / denom; + const utest_int64_t r = value % denom; + return q * numer + r * numer / denom; +} + +static UTEST_INLINE utest_int64_t utest_ns(void) { +#if defined(_MSC_VER) || defined(__MINGW64__) || defined(__MINGW32__) + utest_large_integer counter; + utest_large_integer frequency; + QueryPerformanceCounter(&counter); + QueryPerformanceFrequency(&frequency); + return utest_mul_div(counter.QuadPart, 1000000000, frequency.QuadPart); +#elif defined(__linux__) && defined(__STRICT_ANSI__) + return utest_mul_div(clock(), 1000000000, CLOCKS_PER_SEC); +#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \ + defined(__NetBSD__) || defined(__DragonFly__) || defined(__sun__) || \ + defined(__HAIKU__) + struct timespec ts; +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ + !defined(__HAIKU__) + timespec_get(&ts, TIME_UTC); +#else + const clockid_t cid = CLOCK_REALTIME; +#if defined(UTEST_USE_CLOCKGETTIME) + clock_gettime(cid, &ts); +#else + syscall(SYS_clock_gettime, cid, &ts); +#endif +#endif + return UTEST_CAST(utest_int64_t, ts.tv_sec) * 1000 * 1000 * 1000 + ts.tv_nsec; +#elif __APPLE__ + return UTEST_CAST(utest_int64_t, clock_gettime_nsec_np(CLOCK_UPTIME_RAW)); +#elif __EMSCRIPTEN__ + return emscripten_performance_now() * 1000000.0; +#else +#error Unsupported platform! +#endif +} + +typedef void (*utest_testcase_t)(int *, size_t); + +struct utest_test_state_s { + utest_testcase_t func; + size_t index; + char *name; +}; + +struct utest_state_s { + struct utest_test_state_s *tests; + size_t tests_length; + FILE *output; +}; + +/* extern to the global state utest needs to execute */ +UTEST_EXTERN struct utest_state_s utest_state; + +#if defined(_MSC_VER) +#define UTEST_WEAK __forceinline +#elif defined(__MINGW32__) || defined(__MINGW64__) +#define UTEST_WEAK static UTEST_ATTRIBUTE(used) +#elif defined(__clang__) || defined(__GNUC__) || defined(__TINYC__) +#define UTEST_WEAK UTEST_ATTRIBUTE(weak) +#else +#error Non clang, non gcc, non MSVC, non tcc compiler found! +#endif + +#if defined(_MSC_VER) +#define UTEST_UNUSED +#else +#define UTEST_UNUSED UTEST_ATTRIBUTE(unused) +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wvariadic-macros" +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif +#define UTEST_PRINTF(...) \ + if (utest_state.output) { \ + fprintf(utest_state.output, __VA_ARGS__); \ + } \ + printf(__VA_ARGS__) +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wvariadic-macros" +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif + +#ifdef _MSC_VER +#define UTEST_SNPRINTF(BUFFER, N, ...) _snprintf_s(BUFFER, N, N, __VA_ARGS__) +#else +#define UTEST_SNPRINTF(...) snprintf(__VA_ARGS__) +#endif + +#ifdef __clang__ +#pragma clang diagnostic pop +#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 - use the overloadable attribute */ +#define UTEST_OVERLOADABLE UTEST_ATTRIBUTE(overloadable) +#endif + +#if defined(__cplusplus) && (__cplusplus >= 201103L) + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif + +#include <type_traits> + +template <typename T, bool is_enum = std::is_enum<T>::value> +struct utest_type_deducer final { + static void _(const T t); +}; + +template <> struct utest_type_deducer<char, false> { + static void _(const char c) { + if (std::is_signed<decltype(c)>::value) { + UTEST_PRINTF("%d", static_cast<int>(c)); + } else { + UTEST_PRINTF("%u", static_cast<unsigned int>(c)); + } + } +}; +template <> struct utest_type_deducer<signed char, false> { + static void _(const signed char c) { + UTEST_PRINTF("%d", static_cast<int>(c)); + } +}; + +template <> struct utest_type_deducer<unsigned char, false> { + static void _(const unsigned char c) { + UTEST_PRINTF("%u", static_cast<unsigned int>(c)); + } +}; + +template <> struct utest_type_deducer<short, false> { + static void _(const short s) { UTEST_PRINTF("%d", static_cast<int>(s)); } +}; + +template <> struct utest_type_deducer<unsigned short, false> { + static void _(const unsigned short s) { + UTEST_PRINTF("%u", static_cast<unsigned>(s)); + } +}; + +template <> struct utest_type_deducer<float, false> { + static void _(const float f) { UTEST_PRINTF("%f", static_cast<double>(f)); } +}; + +template <> struct utest_type_deducer<double, false> { + static void _(const double d) { UTEST_PRINTF("%f", d); } +}; + +template <> struct utest_type_deducer<long double, false> { + static void _(const long double d) { +#if defined(__MINGW32__) || defined(__MINGW64__) + /* MINGW is weird - doesn't like LF at all?! */ + UTEST_PRINTF("%f", (double)d); +#else + UTEST_PRINTF("%Lf", d); +#endif + } +}; + +template <> struct utest_type_deducer<int, false> { + static void _(const int i) { UTEST_PRINTF("%d", i); } +}; + +template <> struct utest_type_deducer<unsigned int, false> { + static void _(const unsigned int i) { UTEST_PRINTF("%u", i); } +}; + +template <> struct utest_type_deducer<long, false> { + static void _(const long i) { UTEST_PRINTF("%ld", i); } +}; + +template <> struct utest_type_deducer<unsigned long, false> { + static void _(const unsigned long i) { UTEST_PRINTF("%lu", i); } +}; + +template <> struct utest_type_deducer<long long, false> { + static void _(const long long i) { UTEST_PRINTF("%lld", i); } +}; + +template <> struct utest_type_deducer<unsigned long long, false> { + static void _(const unsigned long long i) { UTEST_PRINTF("%llu", i); } +}; + +template <> struct utest_type_deducer<bool, false> { + static void _(const bool i) { UTEST_PRINTF(i ? "true" : "false"); } +}; + +template <typename T> struct utest_type_deducer<const T *, false> { + static void _(const T *t) { + UTEST_PRINTF("%p", static_cast<const void *>(t)); + } +}; + +template <typename T> struct utest_type_deducer<T *, false> { + static void _(T *t) { UTEST_PRINTF("%p", static_cast<void *>(t)); } +}; + +template <typename T> struct utest_type_deducer<T, true> { + static void _(const T t) { + UTEST_PRINTF("%llu", static_cast<unsigned long long>(t)); + } +}; + +// default printer for all other objects (specialize for custom printing) +template <typename T> struct utest_type_deducer<T, false> { + static void _(const T& t) { + UTEST_PRINTF("(object %p)", static_cast<const void*>(&t)); + } +}; + +template <> struct utest_type_deducer<std::nullptr_t, false> { + static void _(std::nullptr_t t) { + UTEST_PRINTF("%p", static_cast<void *>(t)); + } +}; + +template <typename T> +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(const T& t) { + utest_type_deducer<T>::_(t); +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#elif defined(UTEST_OVERLOADABLE) + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(signed char c); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(signed char c) { + UTEST_PRINTF("%d", UTEST_CAST(int, c)); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(unsigned char c); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(unsigned char c) { + UTEST_PRINTF("%u", UTEST_CAST(unsigned int, c)); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(float f); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(float f) { + UTEST_PRINTF("%f", UTEST_CAST(double, f)); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(double d); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(double d) { + UTEST_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) { +#if defined(__MINGW32__) || defined(__MINGW64__) + /* MINGW is weird - doesn't like LF at all?! */ + UTEST_PRINTF("%f", (double)d); +#else + UTEST_PRINTF("%Lf", d); +#endif +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(int i); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(int i) { + UTEST_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) { + UTEST_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) { + UTEST_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) { + UTEST_PRINTF("%lu", i); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(const void *p); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(const void *p) { + UTEST_PRINTF("%p", p); +} + +/* + long long is a c++11 extension +*/ +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) || \ + defined(__cplusplus) && (__cplusplus >= 201103L) || \ + (defined(__MINGW32__) || defined(__MINGW64__)) + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long int i); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long int i) { + UTEST_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) { + UTEST_PRINTF("%llu", i); +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#endif +#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ + !(defined(__MINGW32__) || defined(__MINGW64__)) || \ + defined(__TINYC__) +#define utest_type_printer(val) \ + UTEST_PRINTF( \ + _Generic((val), \ + signed char: "%d", \ + unsigned char: "%u", \ + short: "%d", \ + unsigned short: "%u", \ + int: "%d", \ + long: "%ld", \ + long long: "%lld", \ + unsigned: "%u", \ + unsigned long: "%lu", \ + unsigned long long: "%llu", \ + float: "%f", \ + double: "%f", \ + long double: "%Lf", \ + default: _Generic((val - val), ptrdiff_t: "%p", default: "undef")), \ + (val)) +#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(...) UTEST_PRINTF("undef") +#endif + +#if defined(_MSC_VER) +#define UTEST_SURPRESS_WARNING_BEGIN \ + __pragma(warning(push)) __pragma(warning(disable : 4127)) \ + __pragma(warning(disable : 4571)) __pragma(warning(disable : 4130)) +#define UTEST_SURPRESS_WARNING_END __pragma(warning(pop)) +#else +#define UTEST_SURPRESS_WARNING_BEGIN +#define UTEST_SURPRESS_WARNING_END +#endif + +#if defined(__cplusplus) && (__cplusplus >= 201103L) +#define UTEST_AUTO(x) const auto& +#elif !defined(__cplusplus) + +#if defined(__clang__) +/* clang-format off */ +/* had to disable clang-format here because it malforms the pragmas */ +#define UTEST_AUTO(x) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wgnu-auto-type\"") __auto_type \ + _Pragma("clang diagnostic pop") +/* clang-format on */ +#else +#define UTEST_AUTO(x) __typeof__(x + 0) +#endif + +#else +#define UTEST_AUTO(x) typeof(x + 0) +#endif + +#if defined(__clang__) +#define UTEST_STRNCMP(x, y, size) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wdisabled-macro-expansion\"") \ + strncmp(x, y, size) _Pragma("clang diagnostic pop") +#else +#define UTEST_STRNCMP(x, y, size) strncmp(x, y, size) +#endif + +#if defined(_MSC_VER) +#define UTEST_STRNCPY(x, y, size) strcpy_s(x, size, y) +#elif !defined(__clang__) && defined(__GNUC__) +static UTEST_INLINE char * +utest_strncpy_gcc(char *const dst, const char *const src, const size_t size) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-overflow" + return strncpy(dst, src, size); +#pragma GCC diagnostic pop +} + +#define UTEST_STRNCPY(x, y, size) utest_strncpy_gcc(x, y, size) +#else +#define UTEST_STRNCPY(x, y, size) strncpy(x, y, size) +#endif + +#define UTEST_SKIP(msg) \ + do { \ + UTEST_PRINTF(" Skipped : '%s'\n", (msg)); \ + *utest_result = UTEST_TEST_SKIPPED; \ + return; \ + } while (0) + +#if defined(__clang__) +#define UTEST_COND(x, y, cond, msg, is_assert) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wlanguage-extension-token\"") \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat-pedantic\"") \ + _Pragma("clang diagnostic ignored \"-Wfloat-equal\"") \ + UTEST_AUTO(x) xEval = (x); \ + UTEST_AUTO(y) yEval = (y); \ + if (!((xEval)cond(yEval))) { \ + const char *const xAsString = #x; \ + const char *const yAsString = #y; \ + _Pragma("clang diagnostic pop") \ + UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : ("); \ + UTEST_PRINTF("%s) " #cond " (%s", xAsString, yAsString); \ + UTEST_PRINTF(")\n"); \ + UTEST_PRINTF(" Actual : "); \ + utest_type_printer(xEval); \ + UTEST_PRINTF(" vs "); \ + utest_type_printer(yEval); \ + UTEST_PRINTF("\n"); \ + if (strlen(msg) > 0) { \ + UTEST_PRINTF(" Message : %s\n", msg); \ + } \ + *utest_result = UTEST_TEST_FAILURE; \ + if (is_assert) { \ + return; \ + } \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END +#elif defined(__GNUC__) || defined(__TINYC__) +#define UTEST_COND(x, y, cond, msg, is_assert) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + UTEST_AUTO(x) xEval = (x); \ + UTEST_AUTO(y) yEval = (y); \ + if (!((xEval)cond(yEval))) { \ + const char *const xAsString = #x; \ + const char *const yAsString = #y; \ + UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : ("); \ + UTEST_PRINTF("%s) " #cond " (%s", xAsString, yAsString); \ + UTEST_PRINTF(")\n"); \ + UTEST_PRINTF(" Actual : "); \ + utest_type_printer(xEval); \ + UTEST_PRINTF(" vs "); \ + utest_type_printer(yEval); \ + UTEST_PRINTF("\n"); \ + if (strlen(msg) > 0) { \ + UTEST_PRINTF(" Message : %s\n", msg); \ + } \ + *utest_result = UTEST_TEST_FAILURE; \ + if (is_assert) { \ + return; \ + } \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END +#else +#define UTEST_COND(x, y, cond, msg, is_assert) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + if (!((x)cond(y))) { \ + UTEST_PRINTF("%s:%i: Failure (Expected " #cond " Actual)", __FILE__, \ + __LINE__); \ + if (strlen(msg) > 0) { \ + UTEST_PRINTF(" Message : %s", msg); \ + } \ + UTEST_PRINTF("\n"); \ + *utest_result = UTEST_TEST_FAILURE; \ + if (is_assert) { \ + return; \ + } \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END +#endif + +#define EXPECT_EQ(x, y) UTEST_COND(x, y, ==, "", 0) +#define EXPECT_EQ_MSG(x, y, msg) UTEST_COND(x, y, ==, msg, 0) +#define ASSERT_EQ(x, y) UTEST_COND(x, y, ==, "", 1) +#define ASSERT_EQ_MSG(x, y, msg) UTEST_COND(x, y, ==, msg, 1) + +#define EXPECT_NE(x, y) UTEST_COND(x, y, !=, "", 0) +#define EXPECT_NE_MSG(x, y, msg) UTEST_COND(x, y, !=, msg, 0) +#define ASSERT_NE(x, y) UTEST_COND(x, y, !=, "", 1) +#define ASSERT_NE_MSG(x, y, msg) UTEST_COND(x, y, !=, msg, 1) + +#define EXPECT_LT(x, y) UTEST_COND(x, y, <, "", 0) +#define EXPECT_LT_MSG(x, y, msg) UTEST_COND(x, y, <, msg, 0) +#define ASSERT_LT(x, y) UTEST_COND(x, y, <, "", 1) +#define ASSERT_LT_MSG(x, y, msg) UTEST_COND(x, y, <, msg, 1) + +#define EXPECT_LE(x, y) UTEST_COND(x, y, <=, "", 0) +#define EXPECT_LE_MSG(x, y, msg) UTEST_COND(x, y, <=, msg, 0) +#define ASSERT_LE(x, y) UTEST_COND(x, y, <=, "", 1) +#define ASSERT_LE_MSG(x, y, msg) UTEST_COND(x, y, <=, msg, 1) + +#define EXPECT_GT(x, y) UTEST_COND(x, y, >, "", 0) +#define EXPECT_GT_MSG(x, y, msg) UTEST_COND(x, y, >, msg, 0) +#define ASSERT_GT(x, y) UTEST_COND(x, y, >, "", 1) +#define ASSERT_GT_MSG(x, y, msg) UTEST_COND(x, y, >, msg, 1) + +#define EXPECT_GE(x, y) UTEST_COND(x, y, >=, "", 0) +#define EXPECT_GE_MSG(x, y, msg) UTEST_COND(x, y, >=, msg, 0) +#define ASSERT_GE(x, y) UTEST_COND(x, y, >=, "", 1) +#define ASSERT_GE_MSG(x, y, msg) UTEST_COND(x, y, >=, msg, 1) + +#define UTEST_TRUE(x, msg, is_assert) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + const int xEval = !!(x); \ + if (!(xEval)) { \ + UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : true\n"); \ + UTEST_PRINTF(" Actual : %s\n", (xEval) ? "true" : "false"); \ + if (strlen(msg) > 0) { \ + UTEST_PRINTF(" Message : %s\n", msg); \ + } \ + *utest_result = UTEST_TEST_FAILURE; \ + if (is_assert) { \ + return; \ + } \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define EXPECT_TRUE(x) UTEST_TRUE(x, "", 0) +#define EXPECT_TRUE_MSG(x, msg) UTEST_TRUE(x, msg, 0) +#define ASSERT_TRUE(x) UTEST_TRUE(x, "", 1) +#define ASSERT_TRUE_MSG(x, msg) UTEST_TRUE(x, msg, 1) + +#define UTEST_FALSE(x, msg, is_assert) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + const int xEval = !!(x); \ + if (xEval) { \ + UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : false\n"); \ + UTEST_PRINTF(" Actual : %s\n", (xEval) ? "true" : "false"); \ + if (strlen(msg) > 0) { \ + UTEST_PRINTF(" Message : %s\n", msg); \ + } \ + *utest_result = UTEST_TEST_FAILURE; \ + if (is_assert) { \ + return; \ + } \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define EXPECT_FALSE(x) UTEST_FALSE(x, "", 0) +#define EXPECT_FALSE_MSG(x, msg) UTEST_FALSE(x, msg, 0) +#define ASSERT_FALSE(x) UTEST_FALSE(x, "", 1) +#define ASSERT_FALSE_MSG(x, msg) UTEST_FALSE(x, msg, 1) + +#define UTEST_STREQ(x, y, msg, is_assert) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + const char *xEval = (x); \ + const char *yEval = (y); \ + if (UTEST_NULL == xEval || UTEST_NULL == yEval || \ + 0 != strcmp(xEval, yEval)) { \ + UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : \"%s\"\n", xEval); \ + UTEST_PRINTF(" Actual : \"%s\"\n", yEval); \ + if (strlen(msg) > 0) { \ + UTEST_PRINTF(" Message : %s\n", msg); \ + } \ + *utest_result = UTEST_TEST_FAILURE; \ + if (is_assert) { \ + return; \ + } \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define EXPECT_STREQ(x, y) UTEST_STREQ(x, y, "", 0) +#define EXPECT_STREQ_MSG(x, y, msg) UTEST_STREQ(x, y, msg, 0) +#define ASSERT_STREQ(x, y) UTEST_STREQ(x, y, "", 1) +#define ASSERT_STREQ_MSG(x, y, msg) UTEST_STREQ(x, y, msg, 1) + +#define UTEST_STRNE(x, y, msg, is_assert) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + const char *xEval = (x); \ + const char *yEval = (y); \ + if (UTEST_NULL == xEval || UTEST_NULL == yEval || \ + 0 == strcmp(xEval, yEval)) { \ + UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : \"%s\"\n", xEval); \ + UTEST_PRINTF(" Actual : \"%s\"\n", yEval); \ + if (strlen(msg) > 0) { \ + UTEST_PRINTF(" Message : %s\n", msg); \ + } \ + *utest_result = UTEST_TEST_FAILURE; \ + if (is_assert) { \ + return; \ + } \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define EXPECT_STRNE(x, y) UTEST_STRNE(x, y, "", 0) +#define EXPECT_STRNE_MSG(x, y, msg) UTEST_STRNE(x, y, msg, 0) +#define ASSERT_STRNE(x, y) UTEST_STRNE(x, y, "", 1) +#define ASSERT_STRNE_MSG(x, y, msg) UTEST_STRNE(x, y, msg, 1) + +#define UTEST_STRNEQ(x, y, n, msg, is_assert) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + const char *xEval = (x); \ + const char *yEval = (y); \ + const size_t nEval = UTEST_CAST(size_t, n); \ + if (UTEST_NULL == xEval || UTEST_NULL == yEval || \ + 0 != UTEST_STRNCMP(xEval, yEval, nEval)) { \ + UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : \"%.*s\"\n", UTEST_CAST(int, nEval), xEval); \ + UTEST_PRINTF(" Actual : \"%.*s\"\n", UTEST_CAST(int, nEval), yEval); \ + if (strlen(msg) > 0) { \ + UTEST_PRINTF(" Message : %s\n", msg); \ + } \ + *utest_result = UTEST_TEST_FAILURE; \ + if (is_assert) { \ + return; \ + } \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define EXPECT_STRNEQ(x, y, n) UTEST_STRNEQ(x, y, n, "", 0) +#define EXPECT_STRNEQ_MSG(x, y, n, msg) UTEST_STRNEQ(x, y, n, msg, 0) +#define ASSERT_STRNEQ(x, y, n) UTEST_STRNEQ(x, y, n, "", 1) +#define ASSERT_STRNEQ_MSG(x, y, n, msg) UTEST_STRNEQ(x, y, n, msg, 1) + +#define UTEST_STRNNE(x, y, n, msg, is_assert) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + const char *xEval = (x); \ + const char *yEval = (y); \ + const size_t nEval = UTEST_CAST(size_t, n); \ + if (UTEST_NULL == xEval || UTEST_NULL == yEval || \ + 0 == UTEST_STRNCMP(xEval, yEval, nEval)) { \ + UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : \"%.*s\"\n", UTEST_CAST(int, nEval), xEval); \ + UTEST_PRINTF(" Actual : \"%.*s\"\n", UTEST_CAST(int, nEval), yEval); \ + if (strlen(msg) > 0) { \ + UTEST_PRINTF(" Message : %s\n", msg); \ + } \ + *utest_result = UTEST_TEST_FAILURE; \ + if (is_assert) { \ + return; \ + } \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define EXPECT_STRNNE(x, y, n) UTEST_STRNNE(x, y, n, "", 0) +#define EXPECT_STRNNE_MSG(x, y, n, msg) UTEST_STRNNE(x, y, n, msg, 0) +#define ASSERT_STRNNE(x, y, n) UTEST_STRNNE(x, y, n, "", 1) +#define ASSERT_STRNNE_MSG(x, y, n, msg) UTEST_STRNNE(x, y, n, msg, 1) + +#define UTEST_NEAR(x, y, epsilon, msg, is_assert) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + const double diff = \ + utest_fabs(UTEST_CAST(double, x) - UTEST_CAST(double, y)); \ + if (diff > UTEST_CAST(double, epsilon) || utest_isnan(diff)) { \ + UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : %f\n", UTEST_CAST(double, x)); \ + UTEST_PRINTF(" Actual : %f\n", UTEST_CAST(double, y)); \ + if (strlen(msg) > 0) { \ + UTEST_PRINTF(" Message : %s\n", msg); \ + } \ + *utest_result = UTEST_TEST_FAILURE; \ + if (is_assert) { \ + return; \ + } \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define EXPECT_NEAR(x, y, epsilon) UTEST_NEAR(x, y, epsilon, "", 0) +#define EXPECT_NEAR_MSG(x, y, epsilon, msg) UTEST_NEAR(x, y, epsilon, msg, 0) +#define ASSERT_NEAR(x, y, epsilon) UTEST_NEAR(x, y, epsilon, "", 1) +#define ASSERT_NEAR_MSG(x, y, epsilon, msg) UTEST_NEAR(x, y, epsilon, msg, 1) + +#if defined(UTEST_HAS_EXCEPTIONS) +#define UTEST_EXCEPTION(x, exception_type, msg, is_assert) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + int exception_caught = 0; \ + try { \ + x; \ + } catch (const exception_type &) { \ + exception_caught = 1; \ + } catch (...) { \ + exception_caught = 2; \ + } \ + if (1 != exception_caught) { \ + UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : %s exception\n", #exception_type); \ + UTEST_PRINTF(" Actual : %s\n", (2 == exception_caught) \ + ? "Unexpected exception" \ + : "No exception"); \ + if (strlen(msg) > 0) { \ + UTEST_PRINTF(" Message : %s\n", msg); \ + } \ + *utest_result = UTEST_TEST_FAILURE; \ + if (is_assert) { \ + return; \ + } \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define EXPECT_EXCEPTION(x, exception_type) \ + UTEST_EXCEPTION(x, exception_type, "", 0) +#define EXPECT_EXCEPTION_MSG(x, exception_type, msg) \ + UTEST_EXCEPTION(x, exception_type, msg, 0) +#define ASSERT_EXCEPTION(x, exception_type) \ + UTEST_EXCEPTION(x, exception_type, "", 1) +#define ASSERT_EXCEPTION_MSG(x, exception_type, msg) \ + UTEST_EXCEPTION(x, exception_type, msg, 1) + +#define UTEST_EXCEPTION_WITH_MESSAGE(x, exception_type, exception_message, \ + msg, is_assert) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + int exception_caught = 0; \ + char *message_caught = UTEST_NULL; \ + try { \ + x; \ + } catch (const exception_type &e) { \ + const char *const what = e.what(); \ + exception_caught = 1; \ + if (0 != \ + UTEST_STRNCMP(what, exception_message, strlen(exception_message))) { \ + const size_t message_size = strlen(what) + 1; \ + message_caught = UTEST_PTR_CAST(char *, malloc(message_size)); \ + UTEST_STRNCPY(message_caught, what, message_size); \ + } \ + } catch (...) { \ + exception_caught = 2; \ + } \ + if (1 != exception_caught) { \ + UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : %s exception\n", #exception_type); \ + UTEST_PRINTF(" Actual : %s\n", (2 == exception_caught) \ + ? "Unexpected exception" \ + : "No exception"); \ + if (strlen(msg) > 0) { \ + UTEST_PRINTF(" Message : %s\n", msg); \ + } \ + *utest_result = UTEST_TEST_FAILURE; \ + if (is_assert) { \ + return; \ + } \ + } else if (UTEST_NULL != message_caught) { \ + UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : %s exception with message %s\n", \ + #exception_type, exception_message); \ + UTEST_PRINTF(" Actual message : %s\n", message_caught); \ + if (strlen(msg) > 0) { \ + UTEST_PRINTF(" Message : %s\n", msg); \ + } \ + *utest_result = UTEST_TEST_FAILURE; \ + free(message_caught); \ + if (is_assert) { \ + return; \ + } \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define EXPECT_EXCEPTION_WITH_MESSAGE(x, exception_type, exception_message) \ + UTEST_EXCEPTION_WITH_MESSAGE(x, exception_type, exception_message, "", 0) +#define EXPECT_EXCEPTION_WITH_MESSAGE_MSG(x, exception_type, \ + exception_message, msg) \ + UTEST_EXCEPTION_WITH_MESSAGE(x, exception_type, exception_message, msg, 0) +#define ASSERT_EXCEPTION_WITH_MESSAGE(x, exception_type, exception_message) \ + UTEST_EXCEPTION_WITH_MESSAGE(x, exception_type, exception_message, "", 1) +#define ASSERT_EXCEPTION_WITH_MESSAGE_MSG(x, exception_type, \ + exception_message, msg) \ + UTEST_EXCEPTION_WITH_MESSAGE(x, exception_type, exception_message, msg, 1) +#endif + +#if defined(__clang__) +#if __has_warning("-Wunsafe-buffer-usage") +#define UTEST_SURPRESS_WARNINGS_BEGIN \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wunsafe-buffer-usage\"") +#define UTEST_SURPRESS_WARNINGS_END _Pragma("clang diagnostic pop") +#else +#define UTEST_SURPRESS_WARNINGS_BEGIN +#define UTEST_SURPRESS_WARNINGS_END +#endif +#elif defined(__GNUC__) && __GNUC__ >= 8 && defined(__cplusplus) +#define UTEST_SURPRESS_WARNINGS_BEGIN \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wclass-memaccess\"") +#define UTEST_SURPRESS_WARNINGS_END _Pragma("GCC diagnostic pop") +#else +#define UTEST_SURPRESS_WARNINGS_BEGIN +#define UTEST_SURPRESS_WARNINGS_END +#endif + +#define UTEST(SET, NAME) \ + UTEST_SURPRESS_WARNINGS_BEGIN \ + UTEST_EXTERN struct utest_state_s utest_state; \ + static void utest_run_##SET##_##NAME(int *utest_result); \ + static void utest_##SET##_##NAME(int *utest_result, size_t utest_index) { \ + (void)utest_index; \ + utest_run_##SET##_##NAME(utest_result); \ + } \ + UTEST_INITIALIZER(utest_register_##SET##_##NAME) { \ + const size_t index = utest_state.tests_length++; \ + const char name_part[] = #SET "." #NAME; \ + const size_t name_size = strlen(name_part) + 1; \ + char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ + utest_state.tests = UTEST_PTR_CAST( \ + struct utest_test_state_s *, \ + utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ + sizeof(struct utest_test_state_s) * \ + utest_state.tests_length)); \ + if (utest_state.tests && name) { \ + utest_state.tests[index].func = &utest_##SET##_##NAME; \ + utest_state.tests[index].name = name; \ + utest_state.tests[index].index = 0; \ + UTEST_SNPRINTF(name, name_size, "%s", name_part); \ + } else { \ + if (utest_state.tests) { \ + free(utest_state.tests); \ + utest_state.tests = NULL; \ + } \ + if (name) { \ + free(name); \ + } \ + } \ + } \ + UTEST_SURPRESS_WARNINGS_END \ + void utest_run_##SET##_##NAME(int *utest_result) + +#define UTEST_F_SETUP(FIXTURE) \ + static void utest_f_setup_##FIXTURE(int *utest_result, \ + struct FIXTURE *utest_fixture) + +#define UTEST_F_TEARDOWN(FIXTURE) \ + static void utest_f_teardown_##FIXTURE(int *utest_result, \ + struct FIXTURE *utest_fixture) + +#define UTEST_F(FIXTURE, NAME) \ + UTEST_SURPRESS_WARNINGS_BEGIN \ + UTEST_EXTERN struct utest_state_s utest_state; \ + static void utest_f_setup_##FIXTURE(int *, struct FIXTURE *); \ + static void utest_f_teardown_##FIXTURE(int *, struct FIXTURE *); \ + static void utest_run_##FIXTURE##_##NAME(int *, struct FIXTURE *); \ + static void utest_f_##FIXTURE##_##NAME(int *utest_result, \ + size_t utest_index) { \ + struct FIXTURE fixture; \ + (void)utest_index; \ + memset(&fixture, 0, sizeof(fixture)); \ + utest_f_setup_##FIXTURE(utest_result, &fixture); \ + if (UTEST_TEST_PASSED != *utest_result) { \ + return; \ + } \ + utest_run_##FIXTURE##_##NAME(utest_result, &fixture); \ + utest_f_teardown_##FIXTURE(utest_result, &fixture); \ + } \ + UTEST_INITIALIZER(utest_register_##FIXTURE##_##NAME) { \ + const size_t index = utest_state.tests_length++; \ + const char name_part[] = #FIXTURE "." #NAME; \ + const size_t name_size = strlen(name_part) + 1; \ + char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ + utest_state.tests = UTEST_PTR_CAST( \ + struct utest_test_state_s *, \ + utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ + sizeof(struct utest_test_state_s) * \ + utest_state.tests_length)); \ + if (utest_state.tests && name) { \ + utest_state.tests[index].func = &utest_f_##FIXTURE##_##NAME; \ + utest_state.tests[index].name = name; \ + utest_state.tests[index].index = 0; \ + UTEST_SNPRINTF(name, name_size, "%s", name_part); \ + } else { \ + if (utest_state.tests) { \ + free(utest_state.tests); \ + utest_state.tests = NULL; \ + } \ + if (name) { \ + free(name); \ + } \ + } \ + } \ + UTEST_SURPRESS_WARNINGS_END \ + void utest_run_##FIXTURE##_##NAME(int *utest_result, \ + struct FIXTURE *utest_fixture) + +#define UTEST_I_SETUP(FIXTURE) \ + static void utest_i_setup_##FIXTURE( \ + int *utest_result, struct FIXTURE *utest_fixture, size_t utest_index) + +#define UTEST_I_TEARDOWN(FIXTURE) \ + static void utest_i_teardown_##FIXTURE( \ + int *utest_result, struct FIXTURE *utest_fixture, size_t utest_index) + +#define UTEST_I(FIXTURE, NAME, INDEX) \ + UTEST_SURPRESS_WARNINGS_BEGIN \ + UTEST_EXTERN struct utest_state_s utest_state; \ + static void utest_run_##FIXTURE##_##NAME##_##INDEX(int *, struct FIXTURE *); \ + static void utest_i_##FIXTURE##_##NAME##_##INDEX(int *utest_result, \ + size_t index) { \ + struct FIXTURE fixture; \ + memset(&fixture, 0, sizeof(fixture)); \ + utest_i_setup_##FIXTURE(utest_result, &fixture, index); \ + if (UTEST_TEST_PASSED != *utest_result) { \ + return; \ + } \ + utest_run_##FIXTURE##_##NAME##_##INDEX(utest_result, &fixture); \ + utest_i_teardown_##FIXTURE(utest_result, &fixture, index); \ + } \ + UTEST_INITIALIZER(utest_register_##FIXTURE##_##NAME##_##INDEX) { \ + size_t i; \ + utest_uint64_t iUp; \ + for (i = 0; i < (INDEX); i++) { \ + const size_t index = utest_state.tests_length++; \ + const char name_part[] = #FIXTURE "." #NAME; \ + const size_t name_size = strlen(name_part) + 32; \ + char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ + utest_state.tests = UTEST_PTR_CAST( \ + struct utest_test_state_s *, \ + utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ + sizeof(struct utest_test_state_s) * \ + utest_state.tests_length)); \ + if (utest_state.tests && name) { \ + utest_state.tests[index].func = &utest_i_##FIXTURE##_##NAME##_##INDEX; \ + utest_state.tests[index].index = i; \ + utest_state.tests[index].name = name; \ + iUp = UTEST_CAST(utest_uint64_t, i); \ + UTEST_SNPRINTF(name, name_size, "%s/%" UTEST_PRIu64, name_part, iUp); \ + } else { \ + if (utest_state.tests) { \ + free(utest_state.tests); \ + utest_state.tests = NULL; \ + } \ + if (name) { \ + free(name); \ + } \ + } \ + } \ + } \ + UTEST_SURPRESS_WARNINGS_END \ + void utest_run_##FIXTURE##_##NAME##_##INDEX(int *utest_result, \ + struct FIXTURE *utest_fixture) + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif + +UTEST_WEAK +double utest_fabs(double d); +UTEST_WEAK +double utest_fabs(double d) { + union { + double d; + utest_uint64_t u; + } both; + both.d = d; + both.u &= 0x7fffffffffffffffu; + return both.d; +} + +UTEST_WEAK +int utest_isnan(double d); +UTEST_WEAK +int utest_isnan(double d) { + union { + double d; + utest_uint64_t u; + } both; + both.d = d; + both.u &= 0x7fffffffffffffffu; + return both.u > 0x7ff0000000000000u; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#if defined(__clang__) +#if __has_warning("-Wunsafe-buffer-usage") +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" +#endif +#endif + +UTEST_WEAK +int utest_should_filter_test(const char *filter, const char *testcase); +UTEST_WEAK int utest_should_filter_test(const char *filter, + const char *testcase) { + if (filter) { + const char *filter_cur = filter; + const char *testcase_cur = testcase; + const char *filter_wildcard = UTEST_NULL; + + while (('\0' != *filter_cur) && ('\0' != *testcase_cur)) { + if ('*' == *filter_cur) { + /* store the position of the wildcard */ + filter_wildcard = filter_cur; + + /* skip the wildcard character */ + filter_cur++; + + while (('\0' != *filter_cur) && ('\0' != *testcase_cur)) { + if ('*' == *filter_cur) { + /* + we found another wildcard (filter is something like *foo*) so we + exit the current loop, and return to the parent loop to handle + the wildcard case + */ + break; + } else if (*filter_cur != *testcase_cur) { + /* otherwise our filter didn't match, so reset it */ + filter_cur = filter_wildcard; + } + + /* move testcase along */ + testcase_cur++; + + /* move filter along */ + filter_cur++; + } + + if (('\0' == *filter_cur) && ('\0' == *testcase_cur)) { + return 0; + } + + /* if the testcase has been exhausted, we don't have a match! */ + if ('\0' == *testcase_cur) { + return 1; + } + } else { + if (*testcase_cur != *filter_cur) { + /* test case doesn't match filter */ + return 1; + } else { + /* move our filter and testcase forward */ + testcase_cur++; + filter_cur++; + } + } + } + + if (('\0' != *filter_cur) || + (('\0' != *testcase_cur) && + ((filter == filter_cur) || ('*' != filter_cur[-1])))) { + /* we have a mismatch! */ + return 1; + } + } + + return 0; +} + +static UTEST_INLINE FILE *utest_fopen(const char *filename, const char *mode) { +#ifdef _MSC_VER + FILE *file; + if (0 == fopen_s(&file, filename, mode)) { + return file; + } else { + return UTEST_NULL; + } +#else + return fopen(filename, mode); +#endif +} + +static UTEST_INLINE int utest_main(int argc, const char *const argv[]); +int utest_main(int argc, const char *const argv[]) { + utest_uint64_t failed = 0; + utest_uint64_t skipped = 0; + size_t index = 0; + size_t *failed_testcases = UTEST_NULL; + size_t failed_testcases_length = 0; + size_t *skipped_testcases = UTEST_NULL; + size_t skipped_testcases_length = 0; + const char *filter = UTEST_NULL; + utest_uint64_t ran_tests = 0; + int enable_mixed_units = 0; + int random_order = 0; + utest_uint32_t seed = 0; + + enum colours { RESET, GREEN, RED, YELLOW }; + + const int use_colours = UTEST_COLOUR_OUTPUT(); + const char *colours[] = {"\033[0m", "\033[32m", "\033[31m", "\033[33m"}; + + if (!use_colours) { + for (index = 0; index < sizeof colours / sizeof colours[0]; index++) { + colours[index] = ""; + } + } + /* loop through all arguments looking for our options */ + for (index = 1; index < UTEST_CAST(size_t, argc); index++) { + /* Informational switches */ + const char help_str[] = "--help"; + const char list_str[] = "--list-tests"; + /* Test config switches */ + const char filter_str[] = "--filter="; + const char output_str[] = "--output="; + const char enable_mixed_units_str[] = "--enable-mixed-units"; + const char random_order_str[] = "--random-order"; + const char random_order_with_seed_str[] = "--random-order="; + + if (0 == UTEST_STRNCMP(argv[index], help_str, strlen(help_str))) { + printf("utest.h - the single file unit testing solution for C/C++!\n" + "Command line Options:\n" + " --help Show this message and exit.\n" + " --filter=<filter> Filter the test cases to run (EG. " + "MyTest*.a would run MyTestCase.a but not MyTestCase.b).\n" + " --list-tests List testnames, one per line. Output " + "names can be passed to --filter.\n"); + printf(" --output=<output> Output an xunit XML file to the file " + "specified in <output>.\n" + " --enable-mixed-units Enable the per-test output to contain " + "mixed units (s/ms/us/ns).\n" + " --random-order[=<seed>] Randomize the order that the tests are " + "ran in. If the optional <seed> argument is not provided, then a " + "random starting seed is used.\n"); + goto cleanup; + } else if (0 == + UTEST_STRNCMP(argv[index], filter_str, strlen(filter_str))) { + /* user wants to filter what test cases run! */ + filter = argv[index] + strlen(filter_str); + } else if (0 == + UTEST_STRNCMP(argv[index], output_str, strlen(output_str))) { + utest_state.output = utest_fopen(argv[index] + strlen(output_str), "w+"); + } else if (0 == UTEST_STRNCMP(argv[index], list_str, strlen(list_str))) { + for (index = 0; index < utest_state.tests_length; index++) { + UTEST_PRINTF("%s\n", utest_state.tests[index].name); + } + /* when printing the test list, don't actually run the tests */ + return 0; + } else if (0 == UTEST_STRNCMP(argv[index], enable_mixed_units_str, + strlen(enable_mixed_units_str))) { + enable_mixed_units = 1; + } else if (0 == UTEST_STRNCMP(argv[index], random_order_with_seed_str, + strlen(random_order_with_seed_str))) { + seed = + UTEST_CAST(utest_uint32_t, + strtoul(argv[index] + strlen(random_order_with_seed_str), + UTEST_NULL, 10)); + random_order = 1; + } else if (0 == UTEST_STRNCMP(argv[index], random_order_str, + strlen(random_order_str))) { + const utest_int64_t ns = utest_ns(); + + // Some really poor pseudo-random using the current time. I do this + // because I really want to avoid using C's rand() because that'd mean our + // random would be affected by any srand() usage by the user (which I + // don't want). + seed = UTEST_CAST(utest_uint32_t, ns >> 32) * 31 + + UTEST_CAST(utest_uint32_t, ns & 0xffffffff); + random_order = 1; + } + } + + if (random_order) { + // Use Fisher-Yates with the Durstenfield's version to randomly re-order the + // tests. + for (index = utest_state.tests_length; index > 1; index--) { + // For the random order we'll use PCG. + const utest_uint32_t state = seed; + const utest_uint32_t word = + ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u; + const utest_uint32_t next = + ((word >> 22u) ^ word) % UTEST_CAST(utest_uint32_t, index); + + // Swap the randomly chosen element into the last location. + const struct utest_test_state_s copy = utest_state.tests[index - 1]; + utest_state.tests[index - 1] = utest_state.tests[next]; + utest_state.tests[next] = copy; + + // Move the seed onwards. + seed = seed * 747796405u + 2891336453u; + } + } + + for (index = 0; index < utest_state.tests_length; index++) { + if (utest_should_filter_test(filter, utest_state.tests[index].name)) { + continue; + } + + ran_tests++; + } + + printf("%s[==========]%s Running %" UTEST_PRIu64 " test cases.\n", + colours[GREEN], colours[RESET], UTEST_CAST(utest_uint64_t, ran_tests)); + + if (utest_state.output) { + fprintf(utest_state.output, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); + fprintf(utest_state.output, + "<testsuites tests=\"%" UTEST_PRIu64 "\" name=\"All\">\n", + UTEST_CAST(utest_uint64_t, ran_tests)); + fprintf(utest_state.output, + "<testsuite name=\"Tests\" tests=\"%" UTEST_PRIu64 "\">\n", + UTEST_CAST(utest_uint64_t, ran_tests)); + } + + for (index = 0; index < utest_state.tests_length; index++) { + int result = UTEST_TEST_PASSED; + utest_int64_t ns = 0; + + if (utest_should_filter_test(filter, utest_state.tests[index].name)) { + continue; + } + + printf("%s[ RUN ]%s %s\n", colours[GREEN], colours[RESET], + utest_state.tests[index].name); + + if (utest_state.output) { + fprintf(utest_state.output, "<testcase name=\"%s\">", + utest_state.tests[index].name); + } + + ns = utest_ns(); + errno = 0; +#if defined(UTEST_HAS_EXCEPTIONS) + UTEST_SURPRESS_WARNING_BEGIN + try { + utest_state.tests[index].func(&result, utest_state.tests[index].index); + } catch (const std::exception &err) { + printf(" Exception : %s\n", err.what()); + result = UTEST_TEST_FAILURE; + } catch (...) { + printf(" Exception : Unknown\n"); + result = UTEST_TEST_FAILURE; + } + UTEST_SURPRESS_WARNING_END +#else + utest_state.tests[index].func(&result, utest_state.tests[index].index); +#endif + ns = utest_ns() - ns; + + if (utest_state.output) { + fprintf(utest_state.output, "</testcase>\n"); + } + + // Record the failing test. + if (UTEST_TEST_FAILURE == result) { + const size_t failed_testcase_index = failed_testcases_length++; + failed_testcases = UTEST_PTR_CAST( + size_t *, utest_realloc(UTEST_PTR_CAST(void *, failed_testcases), + sizeof(size_t) * failed_testcases_length)); + if (UTEST_NULL != failed_testcases) { + failed_testcases[failed_testcase_index] = index; + } + failed++; + } else if (UTEST_TEST_SKIPPED == result) { + const size_t skipped_testcase_index = skipped_testcases_length++; + skipped_testcases = UTEST_PTR_CAST( + size_t *, utest_realloc(UTEST_PTR_CAST(void *, skipped_testcases), + sizeof(size_t) * skipped_testcases_length)); + if (UTEST_NULL != skipped_testcases) { + skipped_testcases[skipped_testcase_index] = index; + } + skipped++; + } + + { + const char *const units[] = {"ns", "us", "ms", "s", UTEST_NULL}; + unsigned int unit_index = 0; + utest_int64_t time = ns; + + if (enable_mixed_units) { + for (unit_index = 0; UTEST_NULL != units[unit_index]; unit_index++) { + if (10000 > time) { + break; + } + + time /= 1000; + } + } + + if (UTEST_TEST_FAILURE == result) { + printf("%s[ FAILED ]%s %s (%" UTEST_PRId64 "%s)\n", colours[RED], + colours[RESET], utest_state.tests[index].name, time, + units[unit_index]); + } else if (UTEST_TEST_SKIPPED == result) { + printf("%s[ SKIPPED ]%s %s (%" UTEST_PRId64 "%s)\n", colours[YELLOW], + colours[RESET], utest_state.tests[index].name, time, + units[unit_index]); + } else { + printf("%s[ OK ]%s %s (%" UTEST_PRId64 "%s)\n", colours[GREEN], + colours[RESET], utest_state.tests[index].name, time, + units[unit_index]); + } + } + } + + printf("%s[==========]%s %" UTEST_PRIu64 " test cases ran.\n", colours[GREEN], + colours[RESET], ran_tests); + printf("%s[ PASSED ]%s %" UTEST_PRIu64 " tests.\n", colours[GREEN], + colours[RESET], ran_tests - failed - skipped); + + if (0 != skipped) { + printf("%s[ SKIPPED ]%s %" UTEST_PRIu64 " tests, listed below:\n", + colours[YELLOW], colours[RESET], skipped); + for (index = 0; index < skipped_testcases_length; index++) { + printf("%s[ SKIPPED ]%s %s\n", colours[YELLOW], colours[RESET], + utest_state.tests[skipped_testcases[index]].name); + } + } + + if (0 != failed) { + printf("%s[ FAILED ]%s %" UTEST_PRIu64 " tests, listed below:\n", + colours[RED], colours[RESET], failed); + for (index = 0; index < failed_testcases_length; index++) { + printf("%s[ FAILED ]%s %s\n", colours[RED], colours[RESET], + utest_state.tests[failed_testcases[index]].name); + } + } + + if (utest_state.output) { + fprintf(utest_state.output, "</testsuite>\n</testsuites>\n"); + } + +cleanup: + for (index = 0; index < utest_state.tests_length; index++) { + free(UTEST_PTR_CAST(void *, utest_state.tests[index].name)); + } + + free(UTEST_PTR_CAST(void *, skipped_testcases)); + free(UTEST_PTR_CAST(void *, failed_testcases)); + free(UTEST_PTR_CAST(void *, utest_state.tests)); + + if (utest_state.output) { + fclose(utest_state.output); + } + + return UTEST_CAST(int, failed); +} + +#if defined(__clang__) +#if __has_warning("-Wunsafe-buffer-usage") +#pragma clang diagnostic pop +#endif +#endif + +/* + 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 = {0, 0, 0} + +/* + 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 */ diff --git a/tools/gentarball.sh b/tools/gentarball.sh new file mode 100755 index 0000000..90a8cb3 --- /dev/null +++ b/tools/gentarball.sh @@ -0,0 +1,41 @@ +#!/bin/sh +# +# Generate release tarballs for impl + +command -v git > /dev/null 2>&1 || (echo "error: you need git to run this script" && exit 1) + +findversion() { + eval $(grep PACKAGE_VERSION $(dirname $0)/../configure | sed 1q) + echo $PACKAGE_VERSION +} + +VERSION=$(findversion) + +DIR=dist/impl-${VERSION} +mkdir -p $DIR + +if [ -d .got ]; then + repodir=$(got info | grep '^repository' | cut -d: -f2 | xargs) + head=$(got br) +else + repodir=$(git rev-parse --show-toplevel)/.git + head=@ +fi + +echo "Making BZIP tarball" +git --git-dir="${repodir}" archive --format=tar --prefix=impl-$VERSION/ $head \ + | bzip2 -v > $DIR/impl-$VERSION.tar.bz2 +echo "Making XZ tarball" +git --git-dir="${repodir}" archive --format=tar --prefix=impl-$VERSION/ $head \ + | xz -v > $DIR/impl-$VERSION.tar.xz +echo "Making GZIP tarball" +git --git-dir="${repodir}" archive --format=tar --prefix=impl-$VERSION/ $head \ + | gzip -v > $DIR/impl-$VERSION.tar.gz + +( + cd $DIR + echo "Calculating SHA256SUMS" + sha256sum *.tar* > SHA256SUMS +) + +echo "Release Tarballs are at $DIR" |