diff --git a/.github/workflows/avr_build_test.yml b/.github/workflows/avr_build_test.yml new file mode 100644 index 0000000..41fb536 --- /dev/null +++ b/.github/workflows/avr_build_test.yml @@ -0,0 +1,40 @@ +name: AVR Build & Unit Tests + +on: + # This job takes a long time to run, so only run it when the canary build passes + # Ideally we'd use the workflow_run trigger and have GitHub launch this action, but the docs state: + # This event will only trigger a workflow run if the workflow file is on the default branch. + # ...which leads to some interesting behaviors + workflow_call: + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +jobs: + AVRSimulator-Unit-Test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.10' + + - name: Install PlatformIO + working-directory: ./test/avr + run: | + python -m pip install --upgrade pip + pip install --upgrade platformio + pip install wheel + platformio upgrade + platformio pkg update + + - name: Build All Environments + shell: pwsh + working-directory: ./test/avr + run: Get-Content -Path .\platformio.ini | Where-Object { $_.StartsWith("[env:") } | ForEach-Object { & pio run -e $_.SubString(5, $_.Length-6) } + + - name: Run Simulator Unit Tests + working-directory: ./test/avr + run: platformio test -v -e megaatmega2560_sim_unittest \ No newline at end of file diff --git a/.github/workflows/canary_build.yml b/.github/workflows/canary_build.yml new file mode 100644 index 0000000..95a7e93 --- /dev/null +++ b/.github/workflows/canary_build.yml @@ -0,0 +1,48 @@ +name: Canary Build and Test + +on: + push: + pull_request: + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +env: + build_type: Release + cpp_compiler: g++ + output_folder: ${{github.workspace}}/build/ + +jobs: + Canary-Build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Configure CMake & Build + env: + # Map from C++ compiler to equivalent C compiler + g++: gcc + clang++: clang + uses: threeal/cmake-action@v2.0.0 + with: + build-dir: ${{ env.output_folder }} + c-compiler: ${{ env[env.cpp_compiler] }} + cxx-compiler: ${{ env.cpp_compiler }} + build-args: -t tester + options: | + LIBDIVIDE_BUILD_TESTS=ON + CMAKE_BUILD_TYPE=${{ env.build_type }} + + - name: Test + working-directory: ${{env.output_folder}} + run: ctest --build-config ${{env.build_type}} --verbose --tests-regex tester + + # Kick off the full build if everything above succeeded + Full-Build: + needs: Canary-Build + uses: ./.github/workflows/full_build.yml + + # Kick off the full build if everything above succeeded + AVR-Build: + needs: Canary-Build + uses: ./.github/workflows/avr_build_test.yml \ No newline at end of file diff --git a/.github/workflows/full_build.yml b/.github/workflows/full_build.yml new file mode 100644 index 0000000..adc5ed0 --- /dev/null +++ b/.github/workflows/full_build.yml @@ -0,0 +1,121 @@ +name: Full Build and Test + +on: + # This job takes a long time to run, so only run it when the canary build passes + # Ideally we'd use the workflow_run trigger and have GitHub launch this action, but the docs state: + # This event will only trigger a workflow run if the workflow file is on the default branch. + # ...which leads to some interesting behaviors + workflow_call: + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +jobs: + Full-Build-Ubuntu: + runs-on: ${{ matrix.os }} + + strategy: + # We want to test all matrix combinations regardless of failure + fail-fast: false + + matrix: + os: + - ubuntu-24.04 + - ubuntu-20.04 + build_type: + - Release + - Sanitize + cpp_compiler: + - g++ + - clang++ + + env: + output_folder: ${{github.workspace}}/build/ + + steps: + - uses: actions/checkout@v4 + + - name: Configure CMake & Build + env: + # Map from C++ compiler to equivalent C compiler + g++: gcc + clang++: clang + uses: threeal/cmake-action@v2.0.0 + with: + build-dir: ${{ env.output_folder }} + c-compiler: ${{ env[matrix.cpp_compiler] }} + cxx-compiler: ${{ matrix.cpp_compiler }} + options: | + LIBDIVIDE_BUILD_TESTS=ON + CMAKE_BUILD_TYPE=${{ matrix.build_type }} + build-args: --config ${{ matrix.build_type }} + + - name: Test + working-directory: ${{ env.output_folder }} + # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest --build-config ${{ matrix.build_type }} --verbose + + Full-Build-Windows: + runs-on: ${{ matrix.os }} + + strategy: + # We want to test all matrix combinations regardless of failure + fail-fast: false + # This makes development easier + # max-parallel: 0 + matrix: + os: + - windows-2022 + - windows-2019 + build_type: + - Release + - Sanitize + cpp_compiler: + - clang-cl.exe + - cl.exe + include: + # Set generator per os + # Note: These *expand* the existing matrix configurations, it doesn't create new matrix configurations + # See https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategymatrixinclude + - os: windows-2022 + generator: Visual Studio 17 2022 + - os: windows-2019 + generator: Visual Studio 16 2019 + # clang-cl requires the toolset flag be set correctly + - cpp_compiler: clang-cl.exe + toolset: ClangCL + exclude: + # MSVC Clang toolchain fails sanitization + - cpp_compiler: clang-cl.exe + build_type: Sanitize + + env: + output_folder: ${{github.workspace}}/build/ + + steps: + - uses: actions/checkout@v4 + - uses: ilammy/msvc-dev-cmd@v1 + + - name: Configure CMake & Build + env: + # Map from C++ compiler to equivalent C compiler + cl.exe: cl.exe + clang-cl.exe: clang-cl.exe + toolset: ${{ matrix.toolset && format('-T {0}', matrix.toolset) || ''}} + uses: threeal/cmake-action@v2.0.0 + with: + build-dir: ${{ env.output_folder }} + c-compiler: ${{ env[matrix.cpp_compiler] }} + cxx-compiler: ${{ matrix.cpp_compiler }} + generator: ${{ matrix.generator }} + args: ${{ env.toolset }} + options: | + LIBDIVIDE_BUILD_TESTS=ON + CMAKE_BUILD_TYPE=${{ matrix.build_type }} + build-args: --config ${{ matrix.build_type }} + + - name: Test + working-directory: ${{ env.output_folder }} + # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest --build-config ${{ matrix.build_type }} --verbose diff --git a/CMakeLists.txt b/CMakeLists.txt index 193f358..194467d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,17 +9,23 @@ include(CheckCXXSourceRuns) include(GNUInstallDirs) include(CMakePackageConfigHelpers) include(CMakePushCheckState) -include(CMakeSanitize) # Compile options ################################################ # Maximum warnings level & warnings as error -add_compile_options( - "$<$:/W4;/WX>" - "$<$:-Wall;-Wextra;-pedantic;-Werror>" - "$<$:-Wall;-Wextra;-pedantic;-Werror>" - "$<$:-Wall;-Wextra;-pedantic;-Werror>" -) +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") # clang-cl + add_compile_options("/W4;/WX;") + else() # clang native + add_compile_options("-Wall;-Wextra;-pedantic;-Werror") + endif() +else() + add_compile_options( + "$<$:/W4;/WX>" + "$<$:-Wall;-Wextra;-pedantic;-Werror>" + "$<$:-Wall;-Wextra;-pedantic;-Werror>" + ) +endif() # Build options ################################################ @@ -232,6 +238,8 @@ endif() add_library(libdivide INTERFACE) add_library(libdivide::libdivide ALIAS libdivide) +include(CMakeSanitize) + target_compile_features(libdivide INTERFACE cxx_alias_templates) target_include_directories(libdivide INTERFACE @@ -303,13 +311,22 @@ endif() if (LIBDIVIDE_BUILD_TESTS) include(CTest) enable_testing() - add_test(tester tester) - add_test(benchmark_branchfree benchmark_branchfree) + add_test(tester tester) # cmake won't actually build the tests before it tries to run them add_test(build_tester "${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target tester) - add_test(build_benchmark_branchfree "${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target benchmark_branchfree) - set_tests_properties(tester benchmark_branchfree PROPERTIES DEPENDS "build_tester;build_benchmark_branchfree") + set_tests_properties(tester PROPERTIES DEPENDS "build_tester") + + add_test(test_c99 test_c99) + add_test(build_test_c99 "${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target test_c99) + set_tests_properties(test_c99 PROPERTIES DEPENDS "build_test_c99") + + # Only benchmark in release builds. + if("${BUILD_TYPE}" MATCHES RELEASE) + add_test(benchmark_branchfree benchmark_branchfree) + add_test(build_benchmark_branchfree "${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target benchmark_branchfree) + set_tests_properties(benchmark_branchfree PROPERTIES DEPENDS "build_benchmark_branchfree") + endif() endif() # Build the fuzzers (requires clang) ########################### diff --git a/CMakeSanitize b/CMakeSanitize index e025665..bf91c8f 100644 --- a/CMakeSanitize +++ b/CMakeSanitize @@ -12,17 +12,6 @@ if(CMAKE_CONFIGURATION_TYPES) FORCE) endif() -if (CMAKE_C_COMPILER_ID STREQUAL "MSVC") - set(COMPILER_FLAGS "/fsanitize=address /MTd") - set(LINKER_FLAGS "/INCREMENTAL:NO") -elseif(CMAKE_C_COMPILER_ID MATCHES "Clang") - set(COMPILER_FLAGS "-fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all -fno-omit-frame-pointer -U_DLL -U_MT -DDEBUG -O1 -g") - set(LINKER_FLAGS "-fsanitize=address") -elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU") - set(COMPILER_FLAGS "-fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all -fno-omit-frame-pointer -DDEBUG -O1 -g") - set(LINKER_FLAGS "-fsanitize=address -fsanitize=undefined") -endif() - set("CMAKE_CXX_FLAGS_${BUILD_NAME_U}" "${CMAKE_CXX_FLAGS_${BASE_BUILD_NAME_U}} ${COMPILER_FLAGS}" CACHE STRING "Flags used by the C++ compiler during ${BUILD_NAME} builds." FORCE) @@ -46,3 +35,19 @@ mark_as_advanced( set(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel ${BUILD_NAME}." FORCE) + +if (CMAKE_C_COMPILER_ID STREQUAL "MSVC") + target_compile_options(libdivide INTERFACE "$<$:/fsanitize=address;/MTd>") + target_link_options(libdivide INTERFACE "$<$:/INCREMENTAL:NO>") +elseif(CMAKE_C_COMPILER_ID MATCHES "Clang") + if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") # clang-cl + target_compile_options(libdivide INTERFACE "$<$:/fsanitize=address>") + target_link_libraries(libdivide INTERFACE "$<$:clang_rt.asan_dynamic-x86_64.lib;clang_rt.asan_dynamic_runtime_thunk-x86_64.lib>") + else() # clang native + target_compile_options(libdivide INTERFACE "$<$:-fsanitize=address;-fsanitize=undefined;-fno-sanitize-recover=all;-fno-omit-frame-pointer;-U_DLL;-U_MT;-DDEBUG;-O1;-g>") + target_link_options(libdivide INTERFACE "$<$:-fsanitize=address>") + endif() +elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU") + target_compile_options(libdivide INTERFACE "$<$:-fsanitize=address;-fsanitize=undefined;-fno-sanitize-recover=all;-fno-omit-frame-pointer;-DDEBUG;-O1;-g>") + target_link_options(libdivide INTERFACE "$<$:-fsanitize=address;-fsanitize=undefined>") +endif() \ No newline at end of file diff --git a/README.md b/README.md index 62b5668..8830e41 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # libdivide -[![Build Status](https://ci.appveyor.com/api/projects/status/github/ridiculousfish/libdivide?branch=master&svg=true)](https://ci.appveyor.com/project/kimwalisch/libdivide) +[![Build Status](https://github.com/ridiculousfish/libdivide/actions/workflows/canary_build.yml/badge.svg)](https://github.com/ridiculousfish/libdivide/actions/workflows/canary_build.yml) [![Github Releases](https://img.shields.io/github/release/ridiculousfish/libdivide.svg)](https://github.com/ridiculousfish/libdivide/releases) ```libdivide.h``` is a header-only C/C++ library for optimizing integer division. diff --git a/libdivide.h b/libdivide.h index f98a30b..4e6c855 100644 --- a/libdivide.h +++ b/libdivide.h @@ -34,8 +34,15 @@ #include #endif +// Clang-cl prior to Visual Studio 2022 doesn't include __umulh/__mulh intrinsics +#if defined(_MSC_VER) && defined(LIBDIVIDE_X86_64) && (!defined(__clang__) || _MSC_VER>1930) +#define LIBDIVIDE_X64_INTRINSICS +#endif + #if defined(_MSC_VER) +#if defined(LIBDIVIDE_X64_INTRINSICS) #include +#endif #pragma warning(push) // disable warning C4146: unary minus operator applied // to unsigned type, result still unsigned @@ -325,7 +332,7 @@ static LIBDIVIDE_INLINE int32_t libdivide_mullhi_s32(int32_t x, int32_t y) { } static LIBDIVIDE_INLINE uint64_t libdivide_mullhi_u64(uint64_t x, uint64_t y) { -#if defined(LIBDIVIDE_VC) && defined(LIBDIVIDE_X86_64) +#if defined(LIBDIVIDE_X64_INTRINSICS) return __umulh(x, y); #elif defined(HAS_INT128_T) __uint128_t xl = x, yl = y; @@ -351,7 +358,7 @@ static LIBDIVIDE_INLINE uint64_t libdivide_mullhi_u64(uint64_t x, uint64_t y) { } static LIBDIVIDE_INLINE int64_t libdivide_mullhi_s64(int64_t x, int64_t y) { -#if defined(LIBDIVIDE_VC) && defined(LIBDIVIDE_X86_64) +#if defined(LIBDIVIDE_X64_INTRINSICS) return __mulh(x, y); #elif defined(HAS_INT128_T) __int128_t xl = x, yl = y; diff --git a/test/DivideTest.h b/test/DivideTest.h index 219a6ea..69153cd 100644 --- a/test/DivideTest.h +++ b/test/DivideTest.h @@ -121,7 +121,7 @@ class DivideTest { PRINT_ERROR(F(", but got ")); PRINT_ERROR(result); PRINT_ERROR(F("\n")); - exit(1); + TEST_FAIL(); } } @@ -162,7 +162,7 @@ class DivideTest { PRINT_ERROR(F(", but got ")); PRINT_ERROR(result); PRINT_ERROR(F("\n")); - exit(1); + TEST_FAIL(); } else { #if 0 std::cout << "vec" << (CHAR_BIT * sizeof(VecType)) << " success for: " << numer << " / " << denom << " = " << result << std::endl; @@ -325,7 +325,7 @@ class DivideTest { PRINT_ERROR(F(", but got ")); PRINT_ERROR(recovered); PRINT_ERROR(F("\n")); - exit(1); + TEST_FAIL(); } test_edgecase_numerators(denom, the_divider); diff --git a/test/avr/platformio.ini b/test/avr/platformio.ini index a5fcfed..0aff89e 100644 --- a/test/avr/platformio.ini +++ b/test/avr/platformio.ini @@ -9,23 +9,41 @@ ; https://docs.platformio.org/page/projectconf.html [platformio] -default_envs = megaatmega2560_Test +default_envs = megaatmega2560_sim_unittest [env] monitor_speed = 115200 -extra_scripts = simavr_env.py [megaatmega2560_base] platform = atmelavr board = megaatmega2560 framework = arduino -debug_tool = simavr -debug_build_flags = -O0 -ggdb3 -g3 common_build_flags = -std=c++11 -Wall -Wextra -D MONITOR_SPEED=${env.monitor_speed} -I ../.. -[env:megaatmega2560_Test] +[env:megaatmega2560_sim_unittest] extends = megaatmega2560_base -build_flags = ${megaatmega2560_base.common_build_flags} -D TEST_LIBDIVIDE -D PRINT_DETAIL_PROGRESS -save-temps=obj +build_flags = ${megaatmega2560_base.common_build_flags} -D TEST_LIBDIVIDE -D PRINT_DETAIL_PROGRESS -DUNITY_INCLUDE_PRINT_FORMATTED -DUNITY_SUPPORT_64 -DSIMULATOR -save-temps=obj +build_type = test +platform_packages = + platformio/tool-simavr +test_speed = 9600 +upload_protocol = custom +upload_command = +test_testing_command = + ${platformio.packages_dir}/tool-simavr/bin/simavr + -m + atmega2560 + -f + 16000000L + ${platformio.build_dir}/${this.__env__}/firmware.elf + +[env:megaatmega2560-Og-sim] +build_type = debug +extends = env:megaatmega2560_sim_unittest +build_unflags = ${env:megaatmega2560_sim_unittest.build_unflags} -Os -flto -g2 -ggdb2 -Werror +build_flags = ${env:megaatmega2560_sim_unittest.build_flags} -O0 -ggdb3 -g3 +build_src_flags = ${env:megaatmega2560_sim_unittest.build_src_flags} -DUNOPTIMIZED_BUILD -O0 -ggdb3 -g3 +debug_tool = simavr [megaatmega2560_benchmark_base] extends = megaatmega2560_base @@ -74,7 +92,6 @@ extra_scripts = ${env.extra_scripts}, pre:./src/invariant_div_test/gen_constant_ build_unflags = -Os build_flags = ${megaatmega2560_base.common_build_flags} -O3 -ffast-math -funroll-loops -D TEST_DIV -D TEST_CONSTANTS -D TEST_UNSIGNED -save-temps=obj - [env:megaatmega2560_ConstantTestDivUnsignedTemplates] extends = megaatmega2560_base extra_scripts = ${env.extra_scripts}, pre:./src/invariant_div_test/gen_constant_tests.py diff --git a/test/avr/simavr_env.py b/test/avr/simavr_env.py deleted file mode 100644 index 504c03a..0000000 --- a/test/avr/simavr_env.py +++ /dev/null @@ -1,16 +0,0 @@ -import os - -Import('env') - -# Dump construction environment -# print(env.Dump()) - -platform = env.PioPlatform() - -simavr = f'{os.path.join(os.path.join(platform.get_package_dir("tool-simavr") or "", "bin"), "simavr.exe")}' - -# Single action/command per 1 target -env.AddCustomTarget( - 'Simulate', - '$PROG_PATH', - f'{simavr} -f $BOARD_F_CPU -m $BOARD_MCU $PROG_PATH') \ No newline at end of file diff --git a/test/avr/src/main.cpp b/test/avr/src/main.cpp index 6e9570f..52443d6 100644 --- a/test/avr/src/main.cpp +++ b/test/avr/src/main.cpp @@ -1,9 +1,9 @@ #include #include #if defined(BENCHMARK) -#include "..\..\benchmark.h" +#include "../../benchmark.h" #elif defined(TEST_LIBDIVIDE) -#include "..\..\DivideTest.h" +#include "../../DivideTest.h" #elif defined(TEST_CONSTANTS) #include "invariant_div_test/Constant_Div_Tests.h" #endif diff --git a/test/avr/test/main.cpp b/test/avr/test/main.cpp new file mode 100644 index 0000000..0d432e8 --- /dev/null +++ b/test/avr/test/main.cpp @@ -0,0 +1,62 @@ +#include +#include +// #include +// #include +// #include +#include +#include "../../DivideTest.h" + +static void test_int16(void) { + run_test(); +} +static void test_uint16(void) { + run_test(); +} +static void test_int32(void) { + run_test(); +} +static void test_uint32(void) { + run_test(); +} +static void test_int64(void) { + run_test(); +} +static void test_uint64(void) { + run_test(); +} + +void setup() { + + pinMode(LED_BUILTIN, OUTPUT); + + Serial.begin(MONITOR_SPEED); + +#if !defined(SIMULATOR) + delay(2000); +#endif + + UNITY_BEGIN(); // IMPORTANT LINE! + + RUN_TEST(test_int16); + RUN_TEST(test_uint16); + RUN_TEST(test_int32); + RUN_TEST(test_uint32); + RUN_TEST(test_int64); + RUN_TEST(test_uint64); + + UNITY_END(); // stop unit testing + + // Tell SimAVR we are done + cli(); + sleep_enable(); + sleep_cpu(); +} + +void loop() +{ + // Blink to indicate end of test + digitalWrite(LED_BUILTIN, HIGH); + delay(250); + digitalWrite(LED_BUILTIN, LOW); + delay(250); +} \ No newline at end of file diff --git a/test/avr_type_helpers.h b/test/avr_type_helpers.h index 100b4ea..286fbcb 100644 --- a/test/avr_type_helpers.h +++ b/test/avr_type_helpers.h @@ -3,6 +3,8 @@ #if defined(__AVR__) #include +#include +#include namespace std { @@ -31,8 +33,6 @@ namespace std typedef uint64_t type; }; - static const uint8_t CHAR_BIT = 8; - template struct numeric_limits { }; diff --git a/test/outputs.h b/test/outputs.h index 49c78aa..ac258a9 100644 --- a/test/outputs.h +++ b/test/outputs.h @@ -8,6 +8,9 @@ #if defined(__AVR__) #include +#if defined(UNIT_TEST) +#include +#endif // AVR doesn't support (s)printf() of 64-bit numbers. // PRId64 is undefined & GCC will issue a warning @@ -30,28 +33,76 @@ static inline char *to_str(char *buffer, int64_t n) { return to_str(buffer, (uint64_t)n); } -template -void print_serial(const _T &item) { - Serial.print(item); + +static inline char *to_str(char *buffer, uint32_t n) { + snprintf(buffer, 32, "%" PRIu32, n); + return buffer; +} +static inline char *to_str(char *buffer, int32_t n) { + snprintf(buffer, 32, "%" PRId32, n); + return buffer; } -template <> -void print_serial(const uint64_t &item) { - char buffer[32]; - Serial.print(to_str(buffer, item)); +static inline char *to_str(char *buffer, uint16_t n) { + snprintf(buffer, 32, "%" PRIu16, n); + return buffer; +} +static inline char *to_str(char *buffer, int16_t n) { + snprintf(buffer, 32, "%" PRId16, n); + return buffer; +} + +#if defined(UNIT_TEST) + void print_serial(const char *msg) { + if (strcmp(msg, "\n") != 0) { + TEST_MESSAGE(msg); + } + } + void print_serial(char *msg) { + if (strcmp(msg, "\n") != 0) { + TEST_MESSAGE(msg); + } + } +#else + void print_serial(const char *msg) { + Serial.print(msg); + } + void print_serial(char *msg) { + Serial.print(msg); + } +#endif + +void print_serial(const __FlashStringHelper *item) { + char buffer[64]; + strncpy_P(buffer, (const char*)item, sizeof(buffer) - 1); + print_serial(buffer); } -template <> -void print_serial(const int64_t &item) { +void print_serial(const String &item) { + print_serial(item.c_str()); +} + +template +void print_serial(const _T &item) { char buffer[32]; - Serial.print(to_str(buffer, item)); + print_serial(to_str(buffer, item)); } #define PRINT_ERROR(item) print_serial(item) #define PRINT_INFO(item) print_serial(item) +#if !defined(UNIT_TEST) +#define TEST_FAIL() exit(1) +#endif #else +#include + +#define PRINT_ERROR(item) std::cerr << item +#define PRINT_INFO(item) std::cout << item +#define F(item) item +#define TEST_FAIL() exit(1) + static inline char *to_str(char *buffer, uint64_t n) { snprintf(buffer, 32, "%" PRIu64, n); return buffer; @@ -60,21 +111,6 @@ static inline char *to_str(char *buffer, int64_t n) { snprintf(buffer, 32, "%" PRId64, n); return buffer; } - -#include - -#define PRINT_ERROR(item) std::cerr << item -#define PRINT_INFO(item) std::cout << item -#define F(item) item - -#endif - -#if defined(PRINT_DETAIL_PROGRESS) -#define PRINT_PROGRESS_MSG(item) PRINT_INFO(item) -#else -#define PRINT_PROGRESS_MSG(item) -#endif - static inline char *to_str(char *buffer, uint32_t n) { snprintf(buffer, 32, "%" PRIu32, n); return buffer; @@ -83,7 +119,6 @@ static inline char *to_str(char *buffer, int32_t n) { snprintf(buffer, 32, "%" PRId32, n); return buffer; } - static inline char *to_str(char *buffer, uint16_t n) { snprintf(buffer, 32, "%" PRIu16, n); return buffer; @@ -92,3 +127,11 @@ static inline char *to_str(char *buffer, int16_t n) { snprintf(buffer, 32, "%" PRId16, n); return buffer; } + +#endif + +#if defined(PRINT_DETAIL_PROGRESS) +#define PRINT_PROGRESS_MSG(item) PRINT_INFO(item) +#else +#define PRINT_PROGRESS_MSG(item) +#endif