diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..76eb064 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +binary/ +build/ +test_logs/ +*.o +.cache/ +.clangd/ +compile_commands.json +test_logs/* +.DS_Store +Thumbs.db diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..1cb5db2 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,122 @@ +cmake_minimum_required(VERSION 3.12) +project(enter_the_fog C) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# 1. BUILD PROFILES +set(BIN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/binary") +set(CMAKE_C_FLAGS_RELEASE "-O2 -s") +set(CMAKE_C_FLAGS_DEBUG "-g -O0") + +# --- SANITIZER LOGIC --- +# Enables ThreadSanitizer only when CMAKE_BUILD_TYPE is Debug +set(SANITIZER_FLAGS "") +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + message(STATUS "Debug build detected: Enabling ThreadSanitizer") + set(SANITIZER_FLAGS "-fsanitize=address") +endif() + +# 2. DEPENDENCIES +find_package(PkgConfig REQUIRED) +pkg_check_modules(RAYLIB REQUIRED raylib) + +# 3. SOURCE DISCOVERY +file(GLOB_RECURSE ALL_C_FILES "${CMAKE_CURRENT_SOURCE_DIR}/source_code/*.c") +file(GLOB_RECURSE ALL_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/source_code/*.h") + +set(PROJECT_INCLUDE_DIRS "") +set(EXTERN_INCLUDE_DIRS "") + +foreach(_header ${ALL_HEADERS}) + get_filename_component(_dir ${_header} DIRECTORY) + if(_dir MATCHES "/external_code/") + list(APPEND EXTERN_INCLUDE_DIRS ${_dir}) + else() + list(APPEND PROJECT_INCLUDE_DIRS ${_dir}) + endif() +endforeach() + +list(REMOVE_DUPLICATES PROJECT_INCLUDE_DIRS) +list(REMOVE_DUPLICATES EXTERN_INCLUDE_DIRS) + +set(TEST_SOURCES "") +set(EXTERN_SOURCES "") +set(CORE_SOURCES "") + +foreach(_file ${ALL_C_FILES}) + if(_file MATCHES ".*/test_[^/]+\\.c$" OR _file MATCHES ".*/tests/.*") + list(APPEND TEST_SOURCES ${_file}) + elseif(_file MATCHES ".*/external_code/.*") + list(APPEND EXTERN_SOURCES ${_file}) + elseif(NOT _file MATCHES ".*/main\\.c$" AND NOT _file MATCHES ".*/test_main\\.c$") + list(APPEND CORE_SOURCES ${_file}) + endif() +endforeach() + +# 4. TARGETS + +# --- EXTERN LIB (Third party code) --- +add_library(enter_the_fog_extern STATIC ${EXTERN_SOURCES}) +target_include_directories(enter_the_fog_extern SYSTEM PUBLIC ${EXTERN_INCLUDE_DIRS}) + +# --- CORE LIB (Your code) --- +add_library(enter_the_fog_core STATIC ${CORE_SOURCES}) +target_include_directories(enter_the_fog_core PUBLIC ${PROJECT_INCLUDE_DIRS}) +target_link_libraries(enter_the_fog_core PRIVATE enter_the_fog_extern) + +# Apply Sanitizers to Core +target_compile_options(enter_the_fog_core PRIVATE + -Wall -Wextra -Wpedantic -Werror -Wconversion -Wsign-conversion -Wunused-variable -Wunused-function -Wmissing-declarations -Wshadow -Wno-implicit-fallthrough -Wredundant-decls -Wfloat-equal -Winline -Wnull-dereference -Waddress -Wno-long-long -Wimplicit-function-declaration + ${SANITIZER_FLAGS}) +target_link_options(enter_the_fog_core INTERFACE ${SANITIZER_FLAGS}) + +# --- EXECUTABLES --- + +# Main Game +add_executable(enter_the_fog "${CMAKE_CURRENT_SOURCE_DIR}/source_code/main.c") +target_link_libraries(enter_the_fog enter_the_fog_core enter_the_fog_extern ${RAYLIB_LIBRARIES} m) +set_target_properties(enter_the_fog PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${BIN_DIR}") +target_compile_options(enter_the_fog PRIVATE ${SANITIZER_FLAGS}) +target_link_options(enter_the_fog PRIVATE ${SANITIZER_FLAGS}) + +# Test Suite +add_executable(test_suite "${CMAKE_CURRENT_SOURCE_DIR}/source_code/test_main.c" ${TEST_SOURCES}) +target_link_libraries(test_suite enter_the_fog_core enter_the_fog_extern ${RAYLIB_LIBRARIES} m) +set_target_properties(test_suite PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${BIN_DIR}") +target_compile_options(test_suite PRIVATE ${SANITIZER_FLAGS}) +target_link_options(test_suite PRIVATE ${SANITIZER_FLAGS}) + +# 5. WORKFLOW COMMANDS (LSP Fix Included) +set(ROOT_JSON "${CMAKE_CURRENT_SOURCE_DIR}/compile_commands.json") + +add_custom_target(test + COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/../test" + COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug -B "${CMAKE_BINARY_DIR}/../test" -S "${CMAKE_SOURCE_DIR}" + COMMAND ${CMAKE_COMMAND} --build "${CMAKE_BINARY_DIR}/../test" --target test_suite + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_BINARY_DIR}/../test/compile_commands.json" "${ROOT_JSON}" + COMMAND "${BIN_DIR}/test_suite" + COMMENT "Building and running tests with ThreadSanitizer...") + +add_custom_target(debug + COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/../debug" + COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug -B "${CMAKE_BINARY_DIR}/../debug" -S "${CMAKE_SOURCE_DIR}" + COMMAND ${CMAKE_COMMAND} --build "${CMAKE_BINARY_DIR}/../debug" --target enter_the_fog + # This specifically copies the JSON from the debug build folder to the root + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_BINARY_DIR}/../debug/compile_commands.json" "${ROOT_JSON}" + COMMENT "Building debug binary and updating LSP commands...") + +target_compile_options(enter_the_fog PRIVATE + -Wall -Wextra -Wpedantic -Werror -Wconversion -Wsign-conversion -Wunused-variable -Wunused-function -Wmissing-declarations -Wshadow -Wno-implicit-fallthrough -Wredundant-decls -Wfloat-equal -Winline -Wnull-dereference -Waddress -Wno-long-long -Wimplicit-function-declaration + ${SANITIZER_FLAGS}) + +target_compile_options(test_suite PRIVATE + -Wall -Wextra -Wpedantic -Werror -Wconversion -Wsign-conversion -Wunused-variable -Wunused-function -Wmissing-declarations -Wshadow -Wno-implicit-fallthrough -Wredundant-decls -Wfloat-equal -Winline -Wnull-dereference -Waddress -Wno-long-long -Wimplicit-function-declaration + ${SANITIZER_FLAGS}) + +# 6. INITIAL LSP SUPPORT +# Catch-all for when you run cmake manually from a build folder +if(EXISTS "${CMAKE_BINARY_DIR}/compile_commands.json") + execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${CMAKE_BINARY_DIR}/compile_commands.json" "${ROOT_JSON}") +endif() diff --git a/clean b/clean new file mode 100755 index 0000000..7581812 --- /dev/null +++ b/clean @@ -0,0 +1,29 @@ +#!/bin/sh + +# Get the project root directory +ROOT_DIR=$(cd "$(dirname "$0")" && pwd) +BUILD_DIR="$ROOT_DIR/build" + +echo "--- Cleaning Build Directories ---" + +# List of directories to remove +targets="debug release test" + +for type in $targets; do + target_path="$BUILD_DIR/$type" + + if [ -d "$target_path" ]; then + echo "Removing $target_path..." + rm -rf "$target_path" + else + echo "Skipping $type (already clean)." + fi +done + +# Optional: Clean up the copied compile_commands.json in root +if [ -f "$ROOT_DIR/compile_commands.json" ]; then + echo "Removing root compile_commands.json..." + rm "$ROOT_DIR/compile_commands.json" +fi + +echo "Done." diff --git a/compile b/compile new file mode 100755 index 0000000..e3c94cc --- /dev/null +++ b/compile @@ -0,0 +1,53 @@ +#!/bin/sh + +# Get the absolute path of the project root (where this script lives) +# This ensures the script works even if called from elsewhere +ROOT_DIR=$(cd "$(dirname "$0")" && pwd) +BUILD_DIR="$ROOT_DIR/build" + +usage() { + echo "Usage: $0 {debug|release|test}" + exit 1 +} + +# Check if an argument was provided +if [ -z "$1" ]; then + usage +fi + +TYPE="$1" + +case "$TYPE" in + debug|release|test) + TARGET_DIR="$BUILD_DIR/$TYPE" + + # Create directory structure if it doesn't exist + mkdir -p "$TARGET_DIR" + + echo "--- Entering $TYPE build mode ---" + cd "$TARGET_DIR" || exit 1 + + # Set CMake build type based on argument + # We capitalize the first letter to match CMake conventions + if [ "$TYPE" = "debug" ]; then + CMAKE_TYPE="Debug" + elif [ "$TYPE" = "release" ]; then + CMAKE_TYPE="Release" + else + CMAKE_TYPE="Debug" # Tests usually run in debug mode + fi + + # Run CMake and Make + cmake -DCMAKE_BUILD_TYPE="$CMAKE_TYPE" ../.. + + # If it's the test build, we target 'test_suite', otherwise 'all' + if [ "$TYPE" = "test" ]; then + make test_suite + else + make + fi + ;; + *) + usage + ;; +esac diff --git a/resources/atlas.png b/resources/atlas.png new file mode 100644 index 0000000..2bf01e3 Binary files /dev/null and b/resources/atlas.png differ diff --git a/resources/screenshot.png b/resources/screenshot.png new file mode 100644 index 0000000..804932f Binary files /dev/null and b/resources/screenshot.png differ diff --git a/resources/shaders/fog.fs b/resources/shaders/fog.fs new file mode 100644 index 0000000..7c43b36 --- /dev/null +++ b/resources/shaders/fog.fs @@ -0,0 +1,44 @@ +#version 330 + +// Input vertex attributes +in vec3 fragPosition; +in vec2 fragTexCoord; +in vec4 fragColor; +in vec3 fragNormal; + +// Input uniform values +uniform sampler2D texture0; +uniform vec4 colDiffuse; + +// Fog uniforms +uniform vec3 viewPos; +uniform float fogDensity; +uniform float fogStart; // <--- New Uniform: Distance where fog begins + +// Output fragment color +out vec4 finalColor; + +void main() +{ + // 1. Get the base color + vec4 texelColor = texture(texture0, fragTexCoord); + vec4 baseColor = texelColor * colDiffuse * fragColor; + + // 2. Calculate distance from camera to fragment + float dist = length(viewPos - fragPosition); + + // 3. Calculate effective distance (distance minus the clear zone) + // We use max() to ensure we don't get negative distance inside the fogStart radius + float effectiveDist = max(0.0, dist - fogStart); + + // 4. Calculate fog factor using Exponential Squared formula + // We use effectiveDist so fog only begins accumulating after fogStart + float fogFactor = exp(-pow(effectiveDist * fogDensity, 2.0)); + fogFactor = clamp(fogFactor, 0.0, 1.0); + + // 5. Define fog color + vec3 fogColor = vec3(0.47, 0.7, 0.70); + + // 6. Final Mix + finalColor = vec4(mix(fogColor, baseColor.rgb, fogFactor), baseColor.a); +} diff --git a/resources/shaders/fog.vs b/resources/shaders/fog.vs new file mode 100644 index 0000000..95b6e47 --- /dev/null +++ b/resources/shaders/fog.vs @@ -0,0 +1,29 @@ +#version 330 + +// Input vertex attributes +in vec3 vertexPosition; +in vec2 vertexTexCoord; +in vec3 vertexNormal; +in vec4 vertexColor; + +// Input uniform values +uniform mat4 mvp; +uniform mat4 matModel; + +// Output vertex attributes (to fragment shader) +out vec3 fragPosition; +out vec2 fragTexCoord; +out vec4 fragColor; +out vec3 fragNormal; + +void main() +{ + // Calculate fragment position in world space + fragPosition = vec3(matModel * vec4(vertexPosition, 1.0)); + fragTexCoord = vertexTexCoord; + fragColor = vertexColor; + fragNormal = normalize(vec3(matModel * vec4(vertexNormal, 0.0))); + // 1. Get the base color of the object + // Calculate final vertex position + gl_Position = mvp * vec4(vertexPosition, 1.0); +} diff --git a/source_code/block_module/block/_internal/private_block.c b/source_code/block_module/block/_internal/private_block.c new file mode 100644 index 0000000..e03baab --- /dev/null +++ b/source_code/block_module/block/_internal/private_block.c @@ -0,0 +1,66 @@ +#include "private_block.h" + +#include "texture_atlas_mapper.h" + +/** + * The global registry of block properties and rendering data. + * * This array serves as the "Source of Truth" for every block type in the game. + * It defines the physical properties (transparency), the vertex colors, + * and the specific texture indices for each of the six cube faces. + * * The array is indexed using the 'block_id_struct' enum values to ensure + * O(1) look-up time for block data during mesh generation and lighting. + */ +const block_render_data_struct _block_library[] = +{ + [block_air] = { + .color = { 0, 0, 0, 0 }, + .is_transparent = true, + .texture = { + .top = 0, .bottom = 0, + .front = 0, .back = 0, + .left = 0, .right = 0 + } + }, + + [block_dirt] = { + .color = { 255, 255, 255, 255 }, + .is_transparent = false, + .texture = { + .top = dirt_texture, .bottom = dirt_texture, + .front = dirt_texture, .back = dirt_texture, + .left = dirt_texture, .right = dirt_texture + } + }, + + [block_grass] = { + .color = { 255, 255, 255, 255 }, + .is_transparent = false, + .texture = { + .top = grass_top_texture, .bottom = dirt_texture, + .front = grass_side_texture, .back = grass_side_texture, + .left = grass_side_texture, .right = grass_side_texture + } + }, + + [block_stone] = { + .color = { 255, 255, 255, 255 }, + .is_transparent = false, + .texture = { + .top = stone_texture, .bottom = stone_texture, + .front = stone_texture, .back = stone_texture, + .left = stone_texture, .right = stone_texture + } + }, + + [block_wood] = { + .color = { 255, 255, 255, 255 }, + .is_transparent = false, + .texture = { + .top = wood_texture, .bottom = wood_texture, + .front = wood_texture, .back = wood_texture, + .left = wood_texture, .right = wood_texture + } + } +}; + + diff --git a/source_code/block_module/block/_internal/private_block.h b/source_code/block_module/block/_internal/private_block.h new file mode 100644 index 0000000..0971869 --- /dev/null +++ b/source_code/block_module/block/_internal/private_block.h @@ -0,0 +1,8 @@ +#ifndef private_block_h +#define private_block_h + +#include "block.h" + +extern const block_render_data_struct _block_library[]; + +#endif diff --git a/source_code/block_module/block/block.c b/source_code/block_module/block/block.c new file mode 100644 index 0000000..6b962f9 --- /dev/null +++ b/source_code/block_module/block/block.c @@ -0,0 +1,94 @@ +#include "block.h" +#include "_internal/private_block.h" + +#include "texture_atlas_mapper.h" + +/** + * Retrieves the rendering data and metadata for a specific block identifier. + * * This function acts as a safe accessor for the global block library. It + * performs bounds checking on the provided identifier to ensure it exists + * within the library's memory range. If the identifier is invalid or + * out of bounds, it returns the data for 'block_air' as a safe default + * to prevent segmentation faults or undefined rendering behavior. + * * @param id_to_look_up The unique identifier of the block to retrieve. + * @return The rendering data structure corresponding to the block identifier. + */ +block_render_data_struct get_block_data(block_id_struct id_to_look_up) +{ + if (id_to_look_up < 0 || + id_to_look_up >= block_type_count) + { + return _block_library[block_air]; + } + + return _block_library[id_to_look_up]; +} + +/** + * Calculates the texture coordinates (UVs) for a specific face of a block. + * This function maps a block's texture index to a 2D position within a 16x16 + * texture atlas and handles the V-axis inversion required for OpenGL. + */ +void get_block_face_texture_coordinates(block_id_struct block_id, + int32_t face_direction, + float texture_coordinates[4][2]) +{ + const int32_t texture_atlas_size = 16; + int32_t texture_index = 0; + + /* Retrieve the specific texture index for the requested face */ + switch (face_direction) + { + case face_top: + texture_index = get_block_data(block_id).texture.top; + break; + case face_bottom: + texture_index = get_block_data(block_id).texture.bottom; + break; + case face_front: + texture_index = get_block_data(block_id).texture.front; + break; + case face_back: + texture_index = get_block_data(block_id).texture.back; + break; + case face_left: + texture_index = get_block_data(block_id).texture.left; + break; + case face_right: + texture_index = get_block_data(block_id).texture.right; + break; + } + + /* Determine the grid position within the atlas */ + const int32_t atlas_column = texture_index % texture_atlas_size; + const int32_t atlas_row = texture_index / texture_atlas_size; + + /* Calculate the normalized coordinate steps */ + const float coordinate_step = 1.0f / (float)texture_atlas_size; + + const float horizontal_start = (float)atlas_column * coordinate_step; + const float vertical_start = (float)atlas_row * coordinate_step; + + /* Invert the vertical axis to align with OpenGL's bottom-left origin */ + const float vertical_minimum = 1.0f - (vertical_start + coordinate_step); + const float vertical_maximum = 1.0f - vertical_start; + + const float horizontal_end = horizontal_start + coordinate_step; + + /* Assign coordinates to the four vertices of the face quad */ + // Top-Left vertex + texture_coordinates[0][0] = horizontal_start; + texture_coordinates[0][1] = vertical_maximum; + + // Top-Right vertex + texture_coordinates[1][0] = horizontal_end; + texture_coordinates[1][1] = vertical_maximum; + + // Bottom-Right vertex + texture_coordinates[2][0] = horizontal_end; + texture_coordinates[2][1] = vertical_minimum; + + // Bottom-Left vertex + texture_coordinates[3][0] = horizontal_start; + texture_coordinates[3][1] = vertical_minimum; +} diff --git a/source_code/block_module/block/block.h b/source_code/block_module/block/block.h new file mode 100644 index 0000000..54f00a1 --- /dev/null +++ b/source_code/block_module/block/block.h @@ -0,0 +1,51 @@ +#ifndef block_h +#define block_h + +#include +#include + +typedef enum block_id_struct { + block_air = 0, + block_dirt = 1, + block_grass = 2, + block_stone = 3, + block_wood = 4, + block_type_count +} block_id_struct; + +typedef struct block_render_data_struct { + struct Color color; + bool is_transparent; + + struct { + uint8_t top; + uint8_t bottom; + uint8_t front; + uint8_t back; + uint8_t left; + uint8_t right; + } texture; +} block_render_data_struct; + +/** + * Retrieves the rendering data and metadata for a specific block identifier. + * * This function acts as a safe accessor for the global block library. It + * performs bounds checking on the provided identifier to ensure it exists + * within the library's memory range. If the identifier is invalid or + * out of bounds, it returns the data for 'block_air' as a safe default + * to prevent segmentation faults or undefined rendering behavior. + * * @param id_to_look_up The unique identifier of the block to retrieve. + * @return The rendering data structure corresponding to the block identifier. + */ +block_render_data_struct get_block_data(block_id_struct id_to_look_up); + +/** + * Calculates the texture coordinates (UVs) for a specific face of a block. + * This function maps a block's texture index to a 2D position within a 16x16 + * texture atlas and handles the V-axis inversion required for OpenGL. + */ +void get_block_face_texture_coordinates(block_id_struct block_id, + int32_t face_direction, + float texture_coordinates[4][2]); + +#endif diff --git a/source_code/block_module/block/tests/test_block.c b/source_code/block_module/block/tests/test_block.c new file mode 100644 index 0000000..94e55db --- /dev/null +++ b/source_code/block_module/block/tests/test_block.c @@ -0,0 +1,120 @@ +#include "test_block.h" +#include "../block.h" + +#include "texture_atlas_mapper.h" + +#include "unity.h" + +void test_get_block_data_bounds_checking(void) +{ + // Test way too high + block_render_data_struct data = get_block_data(999); + TEST_ASSERT_TRUE(data.is_transparent); + TEST_ASSERT_EQUAL_INT(0, data.texture.top); + + // Test negative + data = get_block_data(-1); + TEST_ASSERT_TRUE(data.is_transparent); +} + +/* Verify that Grass has different textures for top and bottom */ +void test_grass_block_texture_indices(void) +{ + block_render_data_struct grass = get_block_data(block_grass); + + // Assert that the top is grass, but the bottom is dirt + TEST_ASSERT_EQUAL_INT(grass_top_texture, grass.texture.top); + TEST_ASSERT_EQUAL_INT(dirt_texture, grass.texture.bottom); + TEST_ASSERT_EQUAL_INT(grass_side_texture, grass.texture.front); +} + +void test_get_block_face_texture_coordinates_math(void) +{ + float uvs[4][2]; + + // Let's test Dirt. + // If dirt_texture is index 2 in your texture_atlas_mapper.h: + // atlas_column = 2 % 16 = 2 + // horizontal_start = 2 * (1.0/16.0) = 0.125 + + get_block_face_texture_coordinates(block_dirt, face_front, uvs); + + float expected_start_x = (float)(dirt_texture % 16) * (1.0f / 16.0f); + + // Top-Left X should be the start of the tile + TEST_ASSERT_FLOAT_WITHIN(0.0001f, expected_start_x, uvs[0][0]); + // Top-Right X should be start + 1/16th + TEST_ASSERT_FLOAT_WITHIN(0.0001f, expected_start_x + 0.0625f, uvs[1][0]); +} + +void test_block_library_integrity(void) +{ + for (int i = 0; i < block_type_count; i++) + { + block_render_data_struct data = get_block_data(i); + + if (i == block_air) + { + TEST_ASSERT_TRUE_MESSAGE(data.is_transparent, + "Air must be transparent"); + continue; + } + + // Ensure solid blocks have full opacity + if (!data.is_transparent) + { + TEST_ASSERT_EQUAL_UINT8_MESSAGE(255, data.color.a, + "Solid block has invalid alpha"); + } + + // Ensure texture indices are within a reasonable range (e.g., 256 for a 16x16 atlas) + TEST_ASSERT_LESS_THAN_INT_MESSAGE(256, data.texture.top, + "Texture index out of atlas range"); + } +} + +void test_get_block_face_uv_winding_and_size(void) +{ + float uvs[4][2]; + get_block_face_texture_coordinates(block_dirt, face_front, uvs); + + // Verify the width of the UV quad is exactly 1/16th (0.0625) + float width = uvs[1][0] - uvs[0][0]; // TopRight.x - TopLeft.x + TEST_ASSERT_FLOAT_WITHIN(0.0001f, 0.0625f, width); + + // Verify the height of the UV quad is exactly 1/16th + // Note: vertical_maximum - vertical_minimum + float height = uvs[0][1] - uvs[3][1]; // TopLeft.y - BottomLeft.y + TEST_ASSERT_FLOAT_WITHIN(0.0001f, 0.0625f, height); + + // Verify Winding: Top should be Y-greater than Bottom + TEST_ASSERT_MESSAGE(uvs[0][1] > uvs[3][1], "UV mapping is upside down!"); + + // Verify Winding: Right should be X-greater than Left + TEST_ASSERT_MESSAGE(uvs[1][0] > uvs[0][0], "UV mapping is mirrored horizontally!"); +} + +void test_stone_faces_are_consistent(void) +{ + float top_uvs[4][2]; + float front_uvs[4][2]; + + get_block_face_texture_coordinates(block_stone, face_top, top_uvs); + get_block_face_texture_coordinates(block_stone, face_front, front_uvs); + + for(int i = 0; i < 4; i++) + { + TEST_ASSERT_EQUAL_FLOAT(top_uvs[i][0], front_uvs[i][0]); + TEST_ASSERT_EQUAL_FLOAT(top_uvs[i][1], front_uvs[i][1]); + } +} + +void run_block_tests(void) +{ + RUN_TEST(test_get_block_data_bounds_checking); + RUN_TEST(test_grass_block_texture_indices); + RUN_TEST(test_get_block_face_texture_coordinates_math); + RUN_TEST(test_block_library_integrity); + RUN_TEST(test_get_block_face_uv_winding_and_size); + RUN_TEST(test_stone_faces_are_consistent); +} diff --git a/source_code/block_module/block/tests/test_block.h b/source_code/block_module/block/tests/test_block.h new file mode 100644 index 0000000..b693876 --- /dev/null +++ b/source_code/block_module/block/tests/test_block.h @@ -0,0 +1,18 @@ +#ifndef test_block_h +#define test_block_h + +void test_get_block_data_bounds_checking(void); + +void test_grass_block_texture_indices(void); + +void test_get_block_face_texture_coordinates_math(void); + +void test_block_library_integrity(void); + +void test_get_block_face_uv_winding_and_size(void); + +void test_stone_faces_are_consistent(void); + +void run_block_tests(void); + +#endif diff --git a/source_code/block_module/texture_atlas_mapper/texture_atlas_mapper.h b/source_code/block_module/texture_atlas_mapper/texture_atlas_mapper.h new file mode 100644 index 0000000..c988dcf --- /dev/null +++ b/source_code/block_module/texture_atlas_mapper/texture_atlas_mapper.h @@ -0,0 +1,17 @@ +#ifndef texture_atlas_mapper_h +#define texture_atlas_mapper_h + +#define dirt_texture 242 +#define grass_top_texture 240 +#define grass_side_texture 243 +#define wood_texture 229 +#define stone_texture 241 + +#define face_top 0 +#define face_bottom 1 +#define face_front 2 +#define face_back 3 +#define face_left 4 +#define face_right 5 + +#endif diff --git a/source_code/chunk_module/chunk/_internal/private_chunk.c b/source_code/chunk_module/chunk/_internal/private_chunk.c new file mode 100644 index 0000000..eba7f62 --- /dev/null +++ b/source_code/chunk_module/chunk/_internal/private_chunk.c @@ -0,0 +1,45 @@ +#include "private_chunk.h" +#include "chunk.h" + +#include "raylib.h" + +#include +#include +#include +#include + + +void _initialize_chunk(struct chunk_struct* chunk, struct chunk_position_struct position_in_world) +{ + memset(chunk, 0, sizeof(struct chunk_struct)); + + chunk->position = position_in_world; + chunk->model = (Model){0}; + + chunk->bounds.min.x = (float)position_in_world.x; + chunk->bounds.min.y = (float)position_in_world.y; + chunk->bounds.min.z = (float)position_in_world.z; + + chunk->bounds.max = (Vector3) + { + (float)position_in_world.x + chunk_size, + (float)position_in_world.y + chunk_size, + (float)position_in_world.z + chunk_size + }; + + chunk->pending_mesh = NULL; + atomic_init(&chunk->build_state, state_new); + atomic_init(&chunk->marked_for_unload, false); + atomic_store_explicit(&chunk->reference_counter, 1, memory_order_relaxed); +} + +void _free_chunk_data(struct chunk_struct* chunk) +{ + if (chunk == NULL) + { + return; + } + + free(chunk); +} + diff --git a/source_code/chunk_module/chunk/_internal/private_chunk.h b/source_code/chunk_module/chunk/_internal/private_chunk.h new file mode 100644 index 0000000..3252937 --- /dev/null +++ b/source_code/chunk_module/chunk/_internal/private_chunk.h @@ -0,0 +1,10 @@ +#ifndef private_chunk_h +#define private_chunk_h + +struct chunk_position_struct; +struct chunk_struct; + +void _initialize_chunk(struct chunk_struct* chunk, struct chunk_position_struct world_position); +void _free_chunk_data(struct chunk_struct* chunk); + +#endif diff --git a/source_code/chunk_module/chunk/chunk.c b/source_code/chunk_module/chunk/chunk.c new file mode 100644 index 0000000..c2d9896 --- /dev/null +++ b/source_code/chunk_module/chunk/chunk.c @@ -0,0 +1,41 @@ +#include "chunk.h" +#include "_internal/private_chunk.h" + +#include "chunk_mesh_builder.h" + +#include + +chunk_struct* initialize_chunk(struct chunk_position_struct position_in_world) { + chunk_struct *chunk = malloc(sizeof(chunk_struct)); + + if ( chunk == NULL ) + { + return NULL; + } + + _initialize_chunk(chunk, position_in_world); + return chunk; +} + +void destroy_chunk(struct chunk_struct* chunk) +{ + destroy_chunk_mesh(chunk); + _free_chunk_data(chunk); +} + +uint32_t index_chunk(int32_t x, int32_t y, int32_t z) { + return (uint32_t)(x + y * chunk_size + z * chunk_size * chunk_size); +} + +chunk_position_struct offset_chunk_position(struct chunk_position_struct origin_to_offset, + int32_t x_offset, + int32_t y_offset, + int32_t z_offset) +{ + chunk_position_struct new_chunk_coordinate; + new_chunk_coordinate.x = origin_to_offset.x + x_offset; + new_chunk_coordinate.y = origin_to_offset.y + y_offset; + new_chunk_coordinate.z = origin_to_offset.z + z_offset; + + return new_chunk_coordinate; +} diff --git a/source_code/chunk_module/chunk/chunk.h b/source_code/chunk_module/chunk/chunk.h new file mode 100644 index 0000000..c2d6d06 --- /dev/null +++ b/source_code/chunk_module/chunk/chunk.h @@ -0,0 +1,66 @@ +#ifndef chunk_h +#define chunk_h + +#include "raylib.h" +#include +#include +#include + +#define chunk_size 32 + +struct cpu_mesh_data_struct; + +typedef enum chunk_state_enum +{ + state_new = 0, + state_needs_data, + state_getting_data, + + state_needs_terrain, + state_getting_terrain, + + state_needs_lighting, + state_getting_lighting, + + state_needs_mesh, + state_getting_mesh, + + state_need_upload_to_gpu, + state_chunk_pending_upload, + state_chunk_uploaded_to_render_queue, + state_chunk_is_uploading, + state_chunk_finished_uploading +} chunk_state_enum; + + +typedef struct chunk_position_struct +{ + int32_t x; + int32_t y; + int32_t z; +} chunk_position_struct; + +typedef struct chunk_struct +{ + _Atomic uint32_t reference_counter; + chunk_position_struct position; + atomic_int build_state; + atomic_bool marked_for_unload; + BoundingBox bounds; + Model model; + struct cpu_mesh_data_struct* pending_mesh; + uint16_t block_data[chunk_size * chunk_size * chunk_size]; + uint8_t light_data[chunk_size * chunk_size * chunk_size]; +} chunk_struct; + +chunk_struct* initialize_chunk(struct chunk_position_struct position_in_world); + +chunk_position_struct offset_chunk_position(struct chunk_position_struct origin_to_offset, + int32_t x_offset, + int32_t y_offset, + int32_t z_offset); + +void destroy_chunk(struct chunk_struct* chunk); +uint32_t index_chunk(int32_t x, int32_t y, int32_t z); + +#endif diff --git a/source_code/chunk_module/chunk/tests/test_chunk.c b/source_code/chunk_module/chunk/tests/test_chunk.c new file mode 100644 index 0000000..27b5288 --- /dev/null +++ b/source_code/chunk_module/chunk/tests/test_chunk.c @@ -0,0 +1,56 @@ +#include "test_chunk.h" + +#include "chunk.h" + +#include "unity.h" + +#include + +/* Verify spatial indexing is unique and within the 1D array bounds */ +void test_chunk_index_calculation_is_unique_and_valid(void) +{ + uint16_t maximum_index = chunk_size * chunk_size * chunk_size; + + uint32_t index_origin = index_chunk(0, 0, 0); + uint32_t index_far_corner = index_chunk(chunk_size - 1, + chunk_size - 1, + chunk_size - 1); + + TEST_ASSERT_EQUAL_UINT16(0, index_origin); + TEST_ASSERT_LESS_THAN_UINT16(maximum_index, index_far_corner); +} + +/* Verify that a new chunk is initialized with the correct defaults */ +void test_chunk_initialization_sets_correct_defaults(void) +{ + chunk_position_struct initial_position = { 16.0f, 0.0f, -32.0f }; + chunk_struct *test_chunk = initialize_chunk(initial_position); + + TEST_ASSERT_NOT_NULL(test_chunk); + TEST_ASSERT_EQUAL_FLOAT(initial_position.x, test_chunk->position.x); + TEST_ASSERT_EQUAL_INT(state_needs_data, test_chunk->build_state); + + destroy_chunk(test_chunk); +} + +/* Verify that chunk bounds are correctly calculated based on world position */ +void test_chunk_bounds_calculation_is_accurate(void) +{ + chunk_position_struct initial_position = { 100.0f, 100.0f, 100.0f }; + chunk_struct *test_chunk = initialize_chunk(initial_position); + + float expected_maximum_x = (float)(initial_position.x + chunk_size); + + TEST_ASSERT_EQUAL_FLOAT(initial_position.x, test_chunk->bounds.min.x); + TEST_ASSERT_EQUAL_FLOAT(expected_maximum_x, test_chunk->bounds.max.x); + + destroy_chunk(test_chunk); +} + +/* Runner for this specific module */ +void run_chunk_tests(void) +{ + RUN_TEST(test_chunk_index_calculation_is_unique_and_valid); + RUN_TEST(test_chunk_initialization_sets_correct_defaults); + RUN_TEST(test_chunk_bounds_calculation_is_accurate); +} diff --git a/source_code/chunk_module/chunk/tests/test_chunk.h b/source_code/chunk_module/chunk/tests/test_chunk.h new file mode 100644 index 0000000..f346982 --- /dev/null +++ b/source_code/chunk_module/chunk/tests/test_chunk.h @@ -0,0 +1,12 @@ +#ifndef test_chunk_h +#define test_chunk_h + +void test_chunk_index_calculation_is_unique_and_valid(void); + +void test_chunk_initialization_sets_correct_defaults(void); + +void test_chunk_bounds_calculation_is_accurate(void); + +void run_chunk_tests(void); + +#endif diff --git a/source_code/display_module/display/display.c b/source_code/display_module/display/display.c new file mode 100644 index 0000000..f9dc589 --- /dev/null +++ b/source_code/display_module/display/display.c @@ -0,0 +1,5 @@ +#include "display.h" + +const int screen_height = 300; +const int screen_width = 600; + diff --git a/source_code/display_module/display/display.h b/source_code/display_module/display/display.h new file mode 100644 index 0000000..7c44de0 --- /dev/null +++ b/source_code/display_module/display/display.h @@ -0,0 +1,8 @@ +#ifndef display_h +#define display_h + +extern const int screen_width; +extern const int screen_height; + + +#endif diff --git a/source_code/external_code/Unity/.editorconfig b/source_code/external_code/Unity/.editorconfig new file mode 100644 index 0000000..7b7a47a --- /dev/null +++ b/source_code/external_code/Unity/.editorconfig @@ -0,0 +1,27 @@ +############################################################################### +# Unity Project - A Test Framework for C +# .editorconfig - F. Zahn 2019 +############################################################################### + +# This is the topmost .editorconfig file +root = true + +# Settings that apply to all languages / files +[*] +charset = utf-8 +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.txt] +trim_trailing_whitespace = false + +[*.rb] +indent_size = 2 + +[*.yml] +indent_size = 2 diff --git a/source_code/external_code/Unity/.gitattributes b/source_code/external_code/Unity/.gitattributes new file mode 100644 index 0000000..f84c162 --- /dev/null +++ b/source_code/external_code/Unity/.gitattributes @@ -0,0 +1,31 @@ +* text=auto + +# These files are text and should be normalized (convert crlf to lf) +*.rb text +*.test text +*.c text +*.cpp text +*.h text +*.txt text +*.yml text +*.s79 text +*.bat text +*.xcl text +*.inc text +*.info text +*.md text +makefile text +rakefile text +meson.build text + + +#These files are binary and should not be normalized +*.doc binary +*.odt binary +*.pdf binary +*.ewd binary +*.eww binary +*.dni binary +*.wsdt binary +*.dbgdt binary +*.mac binary diff --git a/source_code/external_code/Unity/.github/FUNDING.yml b/source_code/external_code/Unity/.github/FUNDING.yml new file mode 100644 index 0000000..5aa5126 --- /dev/null +++ b/source_code/external_code/Unity/.github/FUNDING.yml @@ -0,0 +1,13 @@ +github: ThrowTheSwitch +#patreon: # Replace with a single Patreon username +#open_collective: # Replace with a single Open Collective username +#ko_fi: # Replace with a single Ko-fi username +#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +#liberapay: # Replace with a single Liberapay username +#issuehunt: # Replace with a single IssueHunt username +#lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry +#polar: # Replace with a single Polar username +#buy_me_a_coffee: # Replace with a single Buy Me a Coffee username +#thanks_dev: # Replace with a single thanks.dev username +#custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/source_code/external_code/Unity/.github/workflows/main.yml b/source_code/external_code/Unity/.github/workflows/main.yml new file mode 100644 index 0000000..f0b6d93 --- /dev/null +++ b/source_code/external_code/Unity/.github/workflows/main.yml @@ -0,0 +1,35 @@ +--- +# Continuous Integration Workflow: Test case suite run + validation build check +name: CI + +# Controls when the action will run. +# Triggers the workflow on push or pull request events but only for the master branch +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + # Job: Unit test suite + unit-tests: + name: "Unit Tests" + runs-on: ubuntu-latest + strategy: + matrix: + ruby: ['2.7', '3.0', '3.1', '3.2'] + steps: + # Install Ruby Testing Tools + - name: Setup Ruby Testing Tools + run: | + sudo gem install rspec + sudo gem install rubocop -v 1.57.2 + + # Checks out repository under $GITHUB_WORKSPACE + - name: Checkout Latest Repo + uses: actions/checkout@v4 + + # Run Tests + - name: Run All Unit Tests + run: | + cd test && rake ci diff --git a/source_code/external_code/Unity/.gitignore b/source_code/external_code/Unity/.gitignore new file mode 100644 index 0000000..211683a --- /dev/null +++ b/source_code/external_code/Unity/.gitignore @@ -0,0 +1,19 @@ +build/ +builddir/ +test/sandbox +.DS_Store +examples/example_1/subprojects/unity +examples/example_1/test1.exe +examples/example_1/test2.exe +examples/example_2/all_tests.exe +examples/example_1/test1.out +examples/example_1/test2.out +examples/example_2/all_tests.out +examples/example_4/builddir +*.sublime-project +*.sublime-workspace +*.cmake +Makefile +CMakeFiles +CMakeCache.txt +!unityConfig.cmake diff --git a/source_code/external_code/Unity/LICENSE.txt b/source_code/external_code/Unity/LICENSE.txt new file mode 100644 index 0000000..3e3aad5 --- /dev/null +++ b/source_code/external_code/Unity/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2007-25 Mike Karlesky, Mark VanderVoord, & Greg Williams + +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/source_code/external_code/Unity/README.md b/source_code/external_code/Unity/README.md new file mode 100644 index 0000000..6be02aa --- /dev/null +++ b/source_code/external_code/Unity/README.md @@ -0,0 +1,234 @@ +# Unity Test ![CI][] + +__Copyright (c) 2007 - 2024 Unity Project by Mike Karlesky, Mark VanderVoord, and Greg Williams__ + +Welcome to the Unity Test Project, one of the main projects of ThrowTheSwitch.org. +Unity Test is a unit testing framework built for C, with a focus on working with embedded toolchains. + +This project is made to test code targetting microcontrollers big and small. +The core project is a single C file and a pair of headers, allowing it to be added to your existing build setup without too much headache. +You may use any compiler you wish, and may use most existing build systems including Make, CMake, etc. +If you'd like to leave the hard work to us, you might be interested in Ceedling, a build tool also by ThrowTheSwitch.org. + +If you're new to Unity, we encourage you to tour the [getting started guide][]. + +You can also find the [change log][] and [known issues][] in our documentation. + +## Getting Started + +The [docs][] folder contains a [getting started guide][] and much more tips about using Unity. + +## Unity Assertion Summary + +For the full list, see [UnityAssertionsReference.md][]. + +### Basic Validity Tests + + TEST_ASSERT_TRUE(condition) + +Evaluates whatever code is in condition and fails if it evaluates to false + + TEST_ASSERT_FALSE(condition) + +Evaluates whatever code is in condition and fails if it evaluates to true + + TEST_ASSERT(condition) + +Another way of calling `TEST_ASSERT_TRUE` + + TEST_ASSERT_UNLESS(condition) + +Another way of calling `TEST_ASSERT_FALSE` + + TEST_FAIL() + TEST_FAIL_MESSAGE(message) + +This test is automatically marked as a failure. +The message is output stating why. + +### Numerical Assertions: Integers + + TEST_ASSERT_EQUAL_INT(expected, actual) + TEST_ASSERT_EQUAL_INT8(expected, actual) + TEST_ASSERT_EQUAL_INT16(expected, actual) + TEST_ASSERT_EQUAL_INT32(expected, actual) + TEST_ASSERT_EQUAL_INT64(expected, actual) + +Compare two integers for equality and display errors as signed integers. +A cast will be performed to your natural integer size so often this can just be used. +When you need to specify the exact size, you can use a specific version. + + TEST_ASSERT_EQUAL_UINT(expected, actual) + TEST_ASSERT_EQUAL_UINT8(expected, actual) + TEST_ASSERT_EQUAL_UINT16(expected, actual) + TEST_ASSERT_EQUAL_UINT32(expected, actual) + TEST_ASSERT_EQUAL_UINT64(expected, actual) + +Compare two integers for equality and display errors as unsigned integers. +Like INT, there are variants for different sizes also. + + TEST_ASSERT_EQUAL_HEX(expected, actual) + TEST_ASSERT_EQUAL_HEX8(expected, actual) + TEST_ASSERT_EQUAL_HEX16(expected, actual) + TEST_ASSERT_EQUAL_HEX32(expected, actual) + TEST_ASSERT_EQUAL_HEX64(expected, actual) + +Compares two integers for equality and display errors as hexadecimal. +Like the other integer comparisons, you can specify the size... +here the size will also affect how many nibbles are shown (for example, `HEX16` will show 4 nibbles). + + TEST_ASSERT_EQUAL(expected, actual) + +Another way of calling TEST_ASSERT_EQUAL_INT + + TEST_ASSERT_INT_WITHIN(delta, expected, actual) + +Asserts that the actual value is within plus or minus delta of the expected value. +This also comes in size specific variants. + + TEST_ASSERT_GREATER_THAN(threshold, actual) + +Asserts that the actual value is greater than the threshold. +This also comes in size specific variants. + + TEST_ASSERT_LESS_THAN(threshold, actual) + +Asserts that the actual value is less than the threshold. +This also comes in size specific variants. + +### Arrays + + _ARRAY + +You can append `_ARRAY` to any of these macros to make an array comparison of that type. +Here you will need to care a bit more about the actual size of the value being checked. +You will also specify an additional argument which is the number of elements to compare. +For example: + + TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, elements) + + _EACH_EQUAL + +Another array comparison option is to check that EVERY element of an array is equal to a single expected value. +You do this by specifying the EACH_EQUAL macro. +For example: + + TEST_ASSERT_EACH_EQUAL_INT32(expected, actual, elements) + +### Numerical Assertions: Bitwise + + TEST_ASSERT_BITS(mask, expected, actual) + +Use an integer mask to specify which bits should be compared between two other integers. +High bits in the mask are compared, low bits ignored. + + TEST_ASSERT_BITS_HIGH(mask, actual) + +Use an integer mask to specify which bits should be inspected to determine if they are all set high. +High bits in the mask are compared, low bits ignored. + + TEST_ASSERT_BITS_LOW(mask, actual) + +Use an integer mask to specify which bits should be inspected to determine if they are all set low. +High bits in the mask are compared, low bits ignored. + + TEST_ASSERT_BIT_HIGH(bit, actual) + +Test a single bit and verify that it is high. +The bit is specified 0-31 for a 32-bit integer. + + TEST_ASSERT_BIT_LOW(bit, actual) + +Test a single bit and verify that it is low. +The bit is specified 0-31 for a 32-bit integer. + +### Numerical Assertions: Floats + + TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual) + TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual) + +Asserts that the actual value is within plus or minus delta of the expected value. + + TEST_ASSERT_FLOAT_NOT_WITHIN(delta, expected, actual) + TEST_ASSERT_DOUBLE_NOT_WITHIN(delta, expected, actual) + +Asserts that the actual value is NOT within plus or minus delta of the expected value. + + TEST_ASSERT_EQUAL_FLOAT(expected, actual) + TEST_ASSERT_EQUAL_DOUBLE(expected, actual) + +Asserts that two floating point values are "equal" within a small % delta of the expected value. + + TEST_ASSERT_NOT_EQUAL_FLOAT(expected, actual) + TEST_ASSERT_NOT_EQUAL_DOUBLE(expected, actual) + +Asserts that two floating point values are NOT "equal" within a small % delta of the expected value. + + TEST_ASSERT_LESS_THAN_FLOAT(threshold, actual) + TEST_ASSERT_LESS_THAN_DOUBLE(threshold, actual) + TEST_ASSERT_GREATER_THAN_FLOAT(threshold, actual) + TEST_ASSERT_GREATER_THAN_DOUBLE(threshold, actual) + +Asserts that the actual value is less than or greater than the threshold. + +There are also `LESS_OR_EQUAL` and `GREATER_OR_EQUAL` variations. +These obey the same rules for equality as do `TEST_ASSERT_EQUAL_FLOAT` and `TEST_ASSERT_EQUAL_DOUBLE`: +If the two values are within a small % delta of the expected value, the assertion will pass. + +### String Assertions + + TEST_ASSERT_EQUAL_STRING(expected, actual) + +Compare two null-terminate strings. +Fail if any character is different or if the lengths are different. + + TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len) + +Compare two strings. +Fail if any character is different, stop comparing after len characters. + + TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, actual, message) + +Compare two null-terminate strings. +Fail if any character is different or if the lengths are different. +Output a custom message on failure. + + TEST_ASSERT_EQUAL_STRING_LEN_MESSAGE(expected, actual, len, message) + +Compare two strings. +Fail if any character is different, stop comparing after len characters. +Output a custom message on failure. + +### Pointer Assertions + +Most pointer operations can be performed by simply using the integer comparisons above. +However, a couple of special cases are added for clarity. + + TEST_ASSERT_NULL(pointer) + +Fails if the pointer is not equal to NULL + + TEST_ASSERT_NOT_NULL(pointer) + +Fails if the pointer is equal to NULL + +### Memory Assertions + + TEST_ASSERT_EQUAL_MEMORY(expected, actual, len) + +Compare two blocks of memory. +This is a good generic assertion for types that can't be coerced into acting like standard types... +but since it's a memory compare, you have to be careful that your data types are packed. + +### \_MESSAGE + +You can append `\_MESSAGE` to any of the macros to make them take an additional argument. +This argument is a string that will be printed at the end of the failure strings. +This is useful for specifying more information about the problem. + +[CI]: https://github.com/ThrowTheSwitch/Unity/workflows/CI/badge.svg +[getting started guide]: docs/UnityGettingStartedGuide.md +[change log]: docs/UnityChangeLog.md +[known issues]: docs/UnityKnownIssues.md +[docs]: docs/ +[UnityAssertionsReference.md]: docs/UnityAssertionsReference.md diff --git a/source_code/external_code/Unity/auto/__init__.py b/source_code/external_code/Unity/auto/__init__.py new file mode 100644 index 0000000..15f87c3 --- /dev/null +++ b/source_code/external_code/Unity/auto/__init__.py @@ -0,0 +1,7 @@ +# ========================================================================= +# Unity - A Test Framework for C +# ThrowTheSwitch.org +# Copyright (c) 2007-25 Mike Karlesky, Mark VanderVoord, & Greg Williams +# SPDX-License-Identifier: MIT +# ========================================================================= + diff --git a/source_code/external_code/Unity/auto/colour_prompt.rb b/source_code/external_code/Unity/auto/colour_prompt.rb new file mode 100644 index 0000000..566efe9 --- /dev/null +++ b/source_code/external_code/Unity/auto/colour_prompt.rb @@ -0,0 +1,120 @@ +# ========================================================================= +# Unity - A Test Framework for C +# ThrowTheSwitch.org +# Copyright (c) 2007-25 Mike Karlesky, Mark VanderVoord, & Greg Williams +# SPDX-License-Identifier: MIT +# ========================================================================= + +if RUBY_PLATFORM =~ /(win|w)32$/ + begin + require 'Win32API' + rescue LoadError + puts 'ERROR! "Win32API" library not found' + puts '"Win32API" is required for colour on a windows machine' + puts ' try => "gem install Win32API" on the command line' + puts + end + # puts + # puts 'Windows Environment Detected...' + # puts 'Win32API Library Found.' + # puts +end + +class ColourCommandLine + def initialize + return unless RUBY_PLATFORM =~ /(win|w)32$/ + + get_std_handle = Win32API.new('kernel32', 'GetStdHandle', ['L'], 'L') + @set_console_txt_attrb = + Win32API.new('kernel32', 'SetConsoleTextAttribute', %w[L N], 'I') + @hout = get_std_handle.call(-11) + end + + def change_to(new_colour) + if RUBY_PLATFORM =~ /(win|w)32$/ + @set_console_txt_attrb.call(@hout, win32_colour(new_colour)) + else + "\033[30;#{posix_colour(new_colour)};22m" + end + end + + def win32_colour(colour) + case colour + when :black then 0 + when :dark_blue then 1 + when :dark_green then 2 + when :dark_cyan then 3 + when :dark_red then 4 + when :dark_purple then 5 + when :dark_yellow, :narrative then 6 + when :default_white, :default, :dark_white then 7 + when :silver then 8 + when :blue then 9 + when :green, :success then 10 + when :cyan, :output then 11 + when :red, :failure then 12 + when :purple then 13 + when :yellow then 14 + when :white then 15 + else + 0 + end + end + + def posix_colour(colour) + # ANSI Escape Codes - Foreground colors + # | Code | Color | + # | 39 | Default foreground color | + # | 30 | Black | + # | 31 | Red | + # | 32 | Green | + # | 33 | Yellow | + # | 34 | Blue | + # | 35 | Magenta | + # | 36 | Cyan | + # | 37 | Light gray | + # | 90 | Dark gray | + # | 91 | Light red | + # | 92 | Light green | + # | 93 | Light yellow | + # | 94 | Light blue | + # | 95 | Light magenta | + # | 96 | Light cyan | + # | 97 | White | + + case colour + when :black then 30 + when :red, :failure then 31 + when :green, :success then 32 + when :yellow then 33 + when :blue, :narrative then 34 + when :purple, :magenta then 35 + when :cyan, :output then 36 + when :white, :default_white then 37 + when :default then 39 + else + 39 + end + end + + def out_c(mode, colour, str) + case RUBY_PLATFORM + when /(win|w)32$/ + change_to(colour) + $stdout.puts str if mode == :puts + $stdout.print str if mode == :print + change_to(:default_white) + else + $stdout.puts("#{change_to(colour)}#{str}\033[0m") if mode == :puts + $stdout.print("#{change_to(colour)}#{str}\033[0m") if mode == :print + end + end +end + +def colour_puts(role, str) + ColourCommandLine.new.out_c(:puts, role, str) +end + +def colour_print(role, str) + ColourCommandLine.new.out_c(:print, role, str) +end diff --git a/source_code/external_code/Unity/auto/colour_reporter.rb b/source_code/external_code/Unity/auto/colour_reporter.rb new file mode 100644 index 0000000..cac748f --- /dev/null +++ b/source_code/external_code/Unity/auto/colour_reporter.rb @@ -0,0 +1,40 @@ +# ========================================================================= +# Unity - A Test Framework for C +# ThrowTheSwitch.org +# Copyright (c) 2007-25 Mike Karlesky, Mark VanderVoord, & Greg Williams +# SPDX-License-Identifier: MIT +# ========================================================================= + +require_relative 'colour_prompt' + +$colour_output = true + +def report(message) + if !$colour_output + $stdout.puts(message) + else + message = message.join('\n') if message.instance_of?(Array) + message.each_line do |line| + line.chomp! + colour = case line + when /(?:total\s+)?tests:?\s+(\d+)\s+(?:total\s+)?failures:?\s+\d+\s+Ignored:?/i + Regexp.last_match(1).to_i.zero? ? :green : :red + when /PASS/ + :green + when /^OK$/ + :green + when /(?:FAIL|ERROR)/ + :red + when /IGNORE/ + :yellow + when /^(?:Creating|Compiling|Linking)/ + :white + else + :silver + end + colour_puts(colour, line) + end + end + $stdout.flush + $stderr.flush +end diff --git a/source_code/external_code/Unity/auto/extract_version.py b/source_code/external_code/Unity/auto/extract_version.py new file mode 100755 index 0000000..7c2f3d3 --- /dev/null +++ b/source_code/external_code/Unity/auto/extract_version.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 +# ========================================================================= +# Unity - A Test Framework for C +# ThrowTheSwitch.org +# Copyright (c) 2007-25 Mike Karlesky, Mark VanderVoord, & Greg Williams +# SPDX-License-Identifier: MIT +# ========================================================================= + +import re +import sys + +ver_re = re.compile(r"^#define\s+UNITY_VERSION_(?:MAJOR|MINOR|BUILD)\s+(\d+)$") +version = [] + +with open(sys.argv[1], "r") as f: + for line in f: + m = ver_re.match(line) + if m: + version.append(m.group(1)) + +print(".".join(version)) + diff --git a/source_code/external_code/Unity/auto/generate_config.yml b/source_code/external_code/Unity/auto/generate_config.yml new file mode 100644 index 0000000..d1cd9b2 --- /dev/null +++ b/source_code/external_code/Unity/auto/generate_config.yml @@ -0,0 +1,43 @@ +# ========================================================================= +# Unity - A Test Framework for C +# ThrowTheSwitch.org +# Copyright (c) 2007-25 Mike Karlesky, Mark VanderVoord, & Greg Williams +# SPDX-License-Identifier: MIT +# ========================================================================= + +#this is a sample configuration file for generate_module +#you would use it by calling generate_module with the -ygenerate_config.yml option +#files like this are useful for customizing generate_module to your environment +:generate_module: + :defaults: + #these defaults are used in place of any missing options at the command line + :path_src: ../src/ + :path_inc: ../src/ + :path_tst: ../test/ + :update_svn: true + :includes: + #use [] for no additional includes, otherwise list the includes on separate lines + :src: + - Defs.h + - Board.h + :inc: [] + :tst: + - Defs.h + - Board.h + - Exception.h + :boilerplates: + #these are inserted at the top of generated files. + #just comment out or remove if not desired. + #use %1$s where you would like the file name to appear (path/extension not included) + :src: | + //------------------------------------------- + // %1$s.c + //------------------------------------------- + :inc: | + //------------------------------------------- + // %1$s.h + //------------------------------------------- + :tst: | + //------------------------------------------- + // Test%1$s.c : Units tests for %1$s.c + //------------------------------------------- diff --git a/source_code/external_code/Unity/auto/generate_module.rb b/source_code/external_code/Unity/auto/generate_module.rb new file mode 100644 index 0000000..d335cab --- /dev/null +++ b/source_code/external_code/Unity/auto/generate_module.rb @@ -0,0 +1,318 @@ +# ========================================================================= +# Unity - A Test Framework for C +# ThrowTheSwitch.org +# Copyright (c) 2007-25 Mike Karlesky, Mark VanderVoord, & Greg Williams +# SPDX-License-Identifier: MIT +# ========================================================================= + +# This script creates all the files with start code necessary for a new module. +# A simple module only requires a source file, header file, and test file. +# Triad modules require a source, header, and test file for each triad type (like model, conductor, and hardware). + +require 'rubygems' +require 'fileutils' +require 'pathname' + +# TEMPLATE_TST +TEMPLATE_TST ||= '#ifdef %5$s + +#include "unity.h" + +%2$s#include "%1$s.h" + +void setUp(void) +{ +} + +void tearDown(void) +{ +} + +void test_%4$s_NeedToImplement(void) +{ + TEST_IGNORE_MESSAGE("Need to Implement %1$s"); +} + +#endif // %5$s +'.freeze + +# TEMPLATE_SRC +TEMPLATE_SRC ||= '%2$s#include "%1$s.h" +'.freeze + +# TEMPLATE_INC +TEMPLATE_INC ||= '#ifndef %3$s_H +#define %3$s_H +%2$s + +#endif // %3$s_H +'.freeze + +class UnityModuleGenerator + ############################ + def initialize(options = nil) + @options = UnityModuleGenerator.default_options + case options + when NilClass then @options + when String then @options.merge!(UnityModuleGenerator.grab_config(options)) + when Hash then @options.merge!(options) + else raise 'If you specify arguments, it should be a filename or a hash of options' + end + + # Create default file paths if none were provided + @options[:path_src] = "#{__dir__}/../src/" if @options[:path_src].nil? + @options[:path_inc] = @options[:path_src] if @options[:path_inc].nil? + @options[:path_tst] = "#{__dir__}/../test/" if @options[:path_tst].nil? + @options[:path_src] += '/' unless @options[:path_src][-1] == 47 + @options[:path_inc] += '/' unless @options[:path_inc][-1] == 47 + @options[:path_tst] += '/' unless @options[:path_tst][-1] == 47 + + # Built in patterns + @patterns = { + 'src' => { + '' => { inc: [] } + }, + 'test' => { + '' => { inc: [] } + }, + 'dh' => { + 'Driver' => { inc: [create_filename('%1$s', 'Hardware.h')] }, + 'Hardware' => { inc: [] } + }, + 'dih' => { + 'Driver' => { inc: [create_filename('%1$s', 'Hardware.h'), create_filename('%1$s', 'Interrupt.h')] }, + 'Interrupt' => { inc: [create_filename('%1$s', 'Hardware.h')] }, + 'Hardware' => { inc: [] } + }, + 'mch' => { + 'Model' => { inc: [] }, + 'Conductor' => { inc: [create_filename('%1$s', 'Model.h'), create_filename('%1$s', 'Hardware.h')] }, + 'Hardware' => { inc: [] } + }, + 'mvp' => { + 'Model' => { inc: [] }, + 'Presenter' => { inc: [create_filename('%1$s', 'Model.h'), create_filename('%1$s', 'View.h')] }, + 'View' => { inc: [] } + } + } + end + + ############################ + def self.default_options + { + pattern: 'src', + includes: { + src: [], + inc: [], + tst: [] + }, + update_svn: false, + boilerplates: {}, + test_prefix: 'Test', + mock_prefix: 'Mock', + test_define: 'TEST' + } + end + + ############################ + def self.grab_config(config_file) + options = default_options + unless config_file.nil? || config_file.empty? + require_relative 'yaml_helper' + yaml_guts = YamlHelper.load_file(config_file) + options.merge!(yaml_guts[:unity] || yaml_guts[:cmock]) + raise "No :unity or :cmock section found in #{config_file}" unless options + end + options + end + + ############################ + def files_to_operate_on(module_name, pattern = nil) + # strip any leading path information from the module name and save for later + subfolder = File.dirname(module_name) + module_name = File.basename(module_name) + + # create triad definition + prefix = @options[:test_prefix] || 'Test' + triad = [{ ext: '.c', path: @options[:path_src], prefix: '', template: TEMPLATE_SRC, inc: :src, boilerplate: @options[:boilerplates][:src] }, + { ext: '.h', path: @options[:path_inc], prefix: '', template: TEMPLATE_INC, inc: :inc, boilerplate: @options[:boilerplates][:inc] }, + { ext: '.c', path: @options[:path_tst], prefix: prefix, template: TEMPLATE_TST, inc: :tst, boilerplate: @options[:boilerplates][:tst], test_define: @options[:test_define] }] + + # prepare the pattern for use + pattern = (pattern || @options[:pattern] || 'src').downcase + patterns = @patterns[pattern] + raise "ERROR: The design pattern '#{pattern}' specified isn't one that I recognize!" if patterns.nil? + + # single file patterns (currently just 'test') can reject the other parts of the triad + triad.select! { |v| v[:inc] == :tst } if pattern == 'test' + + # Assemble the path/names of the files we need to work with. + files = [] + triad.each do |cfg| + patterns.each_pair do |pattern_file, pattern_traits| + submodule_name = create_filename(module_name, pattern_file) + filename = cfg[:prefix] + submodule_name + cfg[:ext] + files << { + path: (Pathname.new("#{cfg[:path]}#{subfolder}") + filename).cleanpath, + name: submodule_name, + template: cfg[:template], + test_define: cfg[:test_define], + boilerplate: cfg[:boilerplate], + includes: case (cfg[:inc]) + when :src then (@options[:includes][:src] || []) | (pattern_traits[:inc].map { |f| format(f, module_name) }) + when :inc then @options[:includes][:inc] || [] + when :tst then (@options[:includes][:tst] || []) | (pattern_traits[:inc].map { |f| format("#{@options[:mock_prefix]}#{f}", module_name) }) + end + } + end + end + + files + end + + ############################ + def neutralize_filename(name, start_cap: true) + return name if name.empty? + + name = name.split(/(?:\s+|_|(?=[A-Z][a-z]))|(?<=[a-z])(?=[A-Z])/).map(&:capitalize).join('_') + name = name[0].downcase + name[1..] unless start_cap + name + end + + ############################ + def create_filename(part1, part2 = '') + name = part2.empty? ? part1 : "#{part1}_#{part2}" + case (@options[:naming]) + when 'bumpy' then neutralize_filename(name, start_cap: false).delete('_') + when 'camel' then neutralize_filename(name).delete('_') + when 'snake' then neutralize_filename(name).downcase + when 'caps' then neutralize_filename(name).upcase + else name + end + end + + ############################ + def generate(module_name, pattern = nil) + files = files_to_operate_on(module_name, pattern) + + # Abort if all of the module files already exist + all_files_exist = true + files.each do |file| + all_files_exist = false unless File.exist?(file[:path]) + end + raise "ERROR: File #{files[0][:name]} already exists. Exiting." if all_files_exist + + # Create Source Modules + files.each_with_index do |file, _i| + # If this file already exists, don't overwrite it. + if File.exist?(file[:path]) + puts "File #{file[:path]} already exists!" + next + end + # Create the path first if necessary. + FileUtils.mkdir_p(File.dirname(file[:path]), verbose: false) + File.open(file[:path], 'w') do |f| + f.write("#{file[:boilerplate]}\n" % [file[:name]]) unless file[:boilerplate].nil? + f.write(file[:template] % [file[:name], + file[:includes].map { |ff| "#include \"#{ff}\"\n" }.join, + file[:name].upcase.tr('-', '_'), + file[:name].tr('-', '_'), + file[:test_define]]) + end + if @options[:update_svn] + `svn add \"#{file[:path]}\"` + if $!.exitstatus.zero? + puts "File #{file[:path]} created and added to source control" + else + puts "File #{file[:path]} created but FAILED adding to source control!" + end + else + puts "File #{file[:path]} created" + end + end + puts 'Generate Complete' + end + + ############################ + def destroy(module_name, pattern = nil) + files_to_operate_on(module_name, pattern).each do |filespec| + file = filespec[:path] + if File.exist?(file) + if @options[:update_svn] + `svn delete \"#{file}\" --force` + puts "File #{file} deleted and removed from source control" + else + FileUtils.remove(file) + puts "File #{file} deleted" + end + else + puts "File #{file} does not exist so cannot be removed." + end + end + puts 'Destroy Complete' + end +end + +############################ +# Handle As Command Line If Called That Way +if $0 == __FILE__ + destroy = false + options = {} + module_name = nil + + # Parse the command line parameters. + ARGV.each do |arg| + case arg + when /^-d/ then destroy = true + when /^-u/ then options[:update_svn] = true + when /^-p"?(\w+)"?/ then options[:pattern] = Regexp.last_match(1) + when /^-s"?(.+)"?/ then options[:path_src] = Regexp.last_match(1) + when /^-i"?(.+)"?/ then options[:path_inc] = Regexp.last_match(1) + when /^-t"?(.+)"?/ then options[:path_tst] = Regexp.last_match(1) + when /^-n"?(.+)"?/ then options[:naming] = Regexp.last_match(1) + when /^-y"?(.+)"?/ then options = UnityModuleGenerator.grab_config(Regexp.last_match(1)) + when /^(\w+)/ + raise "ERROR: You can't have more than one Module name specified!" unless module_name.nil? + + module_name = arg + when /^-(h|-help)/ + ARGV = [].freeze + else + raise "ERROR: Unknown option specified '#{arg}'" + end + end + + unless ARGV[0] + puts ["\nGENERATE MODULE\n-------- ------", + "\nUsage: ruby generate_module [options] module_name", + " -i\"include\" sets the path to output headers to 'include' (DEFAULT ../src)", + " -s\"../src\" sets the path to output source to '../src' (DEFAULT ../src)", + " -t\"C:/test\" sets the path to output source to 'C:/test' (DEFAULT ../test)", + ' -p"MCH" sets the output pattern to MCH.', + ' dh - driver hardware.', + ' dih - driver interrupt hardware.', + ' mch - model conductor hardware.', + ' mvp - model view presenter.', + ' src - just a source module, header and test. (DEFAULT)', + ' test - just a test file.', + ' -d destroy module instead of creating it.', + ' -n"camel" sets the file naming convention.', + ' bumpy - BumpyCaseFilenames.', + ' camel - camelCaseFilenames.', + ' snake - snake_case_filenames.', + ' caps - CAPS_CASE_FILENAMES.', + ' -u update subversion too (requires subversion command line)', + ' -y"my.yml" selects a different yaml config file for module generation', + ''].join("\n") + exit + end + + raise 'ERROR: You must have a Module name specified! (use option -h for help)' if module_name.nil? + + if destroy + UnityModuleGenerator.new(options).destroy(module_name) + else + UnityModuleGenerator.new(options).generate(module_name) + end + +end diff --git a/source_code/external_code/Unity/auto/generate_test_runner.rb b/source_code/external_code/Unity/auto/generate_test_runner.rb new file mode 100755 index 0000000..cc82cfc --- /dev/null +++ b/source_code/external_code/Unity/auto/generate_test_runner.rb @@ -0,0 +1,631 @@ +#!/usr/bin/env ruby +# ========================================================================= +# Unity - A Test Framework for C +# ThrowTheSwitch.org +# Copyright (c) 2007-25 Mike Karlesky, Mark VanderVoord, & Greg Williams +# SPDX-License-Identifier: MIT +# ========================================================================= + +class UnityTestRunnerGenerator + def initialize(options = nil) + @options = UnityTestRunnerGenerator.default_options + case options + when NilClass + @options + when String + @options.merge!(UnityTestRunnerGenerator.grab_config(options)) + when Hash + # Check if some of these have been specified + @options[:has_setup] = !options[:setup_name].nil? + @options[:has_teardown] = !options[:teardown_name].nil? + @options[:has_suite_setup] = !options[:suite_setup].nil? + @options[:has_suite_teardown] = !options[:suite_teardown].nil? + @options.merge!(options) + else + raise 'If you specify arguments, it should be a filename or a hash of options' + end + require_relative 'type_sanitizer' + end + + def self.default_options + { + includes: [], + defines: [], + plugins: [], + framework: :unity, + test_prefix: 'test|spec|should', + mock_prefix: 'Mock', + mock_suffix: '', + setup_name: 'setUp', + teardown_name: 'tearDown', + test_reset_name: 'resetTest', + test_verify_name: 'verifyTest', + main_name: 'main', # set to :auto to automatically generate each time + main_export_decl: '', + cmdline_args: false, + omit_begin_end: false, + use_param_tests: false, + use_system_files: true, + include_extensions: '(?:hpp|hh|H|h)', + source_extensions: '(?:cpp|cc|ino|C|c)', + shuffle_tests: false, + rng_seed: 0 + } + end + + def self.grab_config(config_file) + options = default_options + unless config_file.nil? || config_file.empty? + require_relative 'yaml_helper' + yaml_guts = YamlHelper.load_file(config_file) + options.merge!(yaml_guts[:unity] || yaml_guts[:cmock]) + raise "No :unity or :cmock section found in #{config_file}" unless options + end + options + end + + def run(input_file, output_file, options = nil) + @options.merge!(options) unless options.nil? + + # pull required data from source file + source = File.read(input_file) + source = source.force_encoding('ISO-8859-1').encode('utf-8', replace: nil) + tests = find_tests(source) + headers = find_includes(source) + testfile_includes = @options[:use_system_files] ? (headers[:local] + headers[:system]) : (headers[:local]) + used_mocks = find_mocks(testfile_includes) + testfile_includes = (testfile_includes - used_mocks) + testfile_includes.delete_if { |inc| inc =~ /(unity|cmock)/ } + find_setup_and_teardown(source) + + # build runner file + generate(input_file, output_file, tests, used_mocks, testfile_includes) + + # determine which files were used to return them + all_files_used = [input_file, output_file] + all_files_used += testfile_includes.map { |filename| "#{filename}.c" } unless testfile_includes.empty? + all_files_used += @options[:includes] unless @options[:includes].empty? + all_files_used += headers[:linkonly] unless headers[:linkonly].empty? + all_files_used.uniq + end + + def generate(input_file, output_file, tests, used_mocks, testfile_includes) + File.open(output_file, 'w') do |output| + create_header(output, used_mocks, testfile_includes) + create_run_test_params_struct(output) + create_externs(output, tests, used_mocks) + create_mock_management(output, used_mocks) + create_setup(output) + create_teardown(output) + create_suite_setup(output) + create_suite_teardown(output) + create_reset(output) + create_run_test(output) unless tests.empty? + create_args_wrappers(output, tests) + create_shuffle_tests(output) if @options[:shuffle_tests] + create_main(output, input_file, tests, used_mocks) + end + + return unless @options[:header_file] && !@options[:header_file].empty? + + File.open(@options[:header_file], 'w') do |output| + create_h_file(output, @options[:header_file], tests, testfile_includes, used_mocks) + end + end + + def find_tests(source) + tests_and_line_numbers = [] + + # contains characters which will be substituted from within strings, doing + # this prevents these characters from interfering with scrubbers + # @ is not a valid C character, so there should be no clashes with files genuinely containing these markers + substring_subs = { '{' => '@co@', '}' => '@cc@', ';' => '@ss@', '/' => '@fs@' } + substring_re = Regexp.union(substring_subs.keys) + substring_unsubs = substring_subs.invert # the inverse map will be used to fix the strings afterwords + substring_unsubs['@quote@'] = '\\"' + substring_unsubs['@apos@'] = '\\\'' + substring_unre = Regexp.union(substring_unsubs.keys) + source_scrubbed = source.clone + source_scrubbed = source_scrubbed.gsub(/\\"/, '@quote@') # hide escaped quotes to allow capture of the full string/char + source_scrubbed = source_scrubbed.gsub(/\\'/, '@apos@') # hide escaped apostrophes to allow capture of the full string/char + source_scrubbed = source_scrubbed.gsub(/("[^"\n]*")|('[^'\n]*')/) { |s| s.gsub(substring_re, substring_subs) } # temporarily hide problematic characters within strings + source_scrubbed = source_scrubbed.gsub(/\/\/(?:.+\/\*|\*(?:$|[^\/])).*$/, '') # remove line comments that comment out the start of blocks + source_scrubbed = source_scrubbed.gsub(/\/\*.*?\*\//m, '') # remove block comments + source_scrubbed = source_scrubbed.gsub(/\/\/.*$/, '') # remove line comments (all that remain) + lines = source_scrubbed.split(/(^\s*\#.*$) | (;|\{|\}) /x) # Treat preprocessor directives as a logical line. Match ;, {, and } as end of lines + .map { |line| line.gsub(substring_unre, substring_unsubs) } # unhide the problematic characters previously removed + + lines.each_with_index do |line, _index| + # find tests + next unless line =~ /^((?:\s*(?:TEST_(?:CASE|RANGE|MATRIX))\s*\(.*?\)\s*)*)\s*void\s+((?:#{@options[:test_prefix]}).*)\s*\(\s*(.*)\s*\)/m + next unless line =~ /^((?:\s*(?:TEST_(?:CASE|RANGE|MATRIX))\s*\(.*?\)\s*)*)\s*void\s+((?:#{@options[:test_prefix]})\w*)\s*\(\s*(.*)\s*\)/m + + arguments = Regexp.last_match(1) + name = Regexp.last_match(2) + call = Regexp.last_match(3) + params = Regexp.last_match(4) + args = nil + + if @options[:use_param_tests] && !arguments.empty? + args = [] + type_and_args = arguments.split(/TEST_(CASE|RANGE|MATRIX)/) + (1...type_and_args.length).step(2).each do |i| + case type_and_args[i] + when 'CASE' + args << type_and_args[i + 1].sub(/^\s*\(\s*(.*?)\s*\)\s*$/m, '\1') + + when 'RANGE' + args += type_and_args[i + 1].scan(/(\[|<)\s*(-?\d+.?\d*)\s*,\s*(-?\d+.?\d*)\s*,\s*(-?\d+.?\d*)\s*(\]|>)/m).map do |arg_values_str| + exclude_end = arg_values_str[0] == '<' && arg_values_str[-1] == '>' + arg_values_str[1...-1].map do |arg_value_str| + arg_value_str.include?('.') ? arg_value_str.to_f : arg_value_str.to_i + end.push(exclude_end) + end.map do |arg_values| + Range.new(arg_values[0], arg_values[1], arg_values[3]).step(arg_values[2]).to_a + end.reduce(nil) do |result, arg_range_expanded| + result.nil? ? arg_range_expanded.map { |a| [a] } : result.product(arg_range_expanded) + end.map do |arg_combinations| + arg_combinations.flatten.join(', ') + end + + when 'MATRIX' + single_arg_regex_string = /(?:(?:"(?:\\"|[^\\])*?")+|(?:'\\?.')+|(?:[^\s\]\["',]|\[[\d\S_-]+\])+)/.source + args_regex = /\[((?:\s*#{single_arg_regex_string}\s*,?)*(?:\s*#{single_arg_regex_string})?\s*)\]/m + arg_elements_regex = /\s*(#{single_arg_regex_string})\s*,\s*/m + + args += type_and_args[i + 1].scan(args_regex).flatten.map do |arg_values_str| + "#{arg_values_str},".scan(arg_elements_regex) + end.reduce do |result, arg_range_expanded| + result.product(arg_range_expanded) + end.map do |arg_combinations| + arg_combinations.flatten.join(', ') + end + end + end + end + + tests_and_line_numbers << { test: name, args: args, call: call, params: params, line_number: 0 } + end + + tests_and_line_numbers.uniq! { |v| v[:test] } + + # determine line numbers and create tests to run + source_lines = source.split("\n") + source_index = 0 + tests_and_line_numbers.size.times do |i| + source_lines[source_index..].each_with_index do |line, index| + next unless line =~ /\s+#{tests_and_line_numbers[i][:test]}(?:\s|\()/ + + source_index += index + tests_and_line_numbers[i][:line_number] = source_index + 1 + break + end + end + + tests_and_line_numbers + end + + def find_includes(source) + # remove comments (block and line, in three steps to ensure correct precedence) + source.gsub!(/\/\/(?:.+\/\*|\*(?:$|[^\/])).*$/, '') # remove line comments that comment out the start of blocks + source.gsub!(/\/\*.*?\*\//m, '') # remove block comments + source.gsub!(/\/\/.*$/, '') # remove line comments (all that remain) + + # parse out includes + { + local: source.scan(/^\s*#include\s+"\s*(.+\.#{@options[:include_extensions]})\s*"/).flatten, + system: source.scan(/^\s*#include\s+<\s*(.+)\s*>/).flatten.map { |inc| "<#{inc}>" }, + linkonly: source.scan(/^TEST_SOURCE_FILE\(\s*"\s*(.+\.#{@options[:source_extensions]})\s*"/).flatten + } + end + + def find_mocks(includes) + mock_headers = [] + includes.each do |include_path| + include_file = File.basename(include_path) + mock_headers << include_path if include_file =~ /^#{@options[:mock_prefix]}.*#{@options[:mock_suffix]}\.h$/i + end + mock_headers + end + + def find_setup_and_teardown(source) + @options[:has_setup] = source =~ /void\s+#{@options[:setup_name]}\s*\(/ + @options[:has_teardown] = source =~ /void\s+#{@options[:teardown_name]}\s*\(/ + @options[:has_suite_setup] ||= (source =~ /void\s+suiteSetUp\s*\(/) + @options[:has_suite_teardown] ||= (source =~ /int\s+suiteTearDown\s*\(int\s+([a-zA-Z0-9_])+\s*\)/) + end + + def count_tests(tests) + if @options[:use_param_tests] + idx = 0 + tests.each do |test| + if (test[:args].nil? || test[:args].empty?) + idx += 1 + else + test[:args].each do |args| + idx += 1 + end + end + end + return idx + else + return tests.size + end + end + + def create_header(output, mocks, testfile_includes = []) + output.puts('/* AUTOGENERATED FILE. DO NOT EDIT. */') + output.puts("\n/*=======Automagically Detected Files To Include=====*/") + output.puts('extern "C" {') if @options[:externcincludes] + if @options[:shuffle_tests] + output.puts('#include ') + if @options[:rng_seed] == 0 + output.puts('#include ') + end + end + output.puts("#include \"#{@options[:framework]}.h\"") + output.puts('#include "cmock.h"') unless mocks.empty? + output.puts('}') if @options[:externcincludes] + if @options[:defines] && !@options[:defines].empty? + output.puts('/* injected defines for unity settings, etc */') + @options[:defines].each do |d| + def_only = d.match(/(\w+).*/)[1] + output.puts("#ifndef #{def_only}\n#define #{d}\n#endif /* #{def_only} */") + end + end + if @options[:header_file] && !@options[:header_file].empty? + output.puts("#include \"#{File.basename(@options[:header_file])}\"") + else + @options[:includes].flatten.uniq.compact.each do |inc| + output.puts("#include #{inc.include?('<') ? inc : "\"#{inc}\""}") + end + testfile_includes.each do |inc| + output.puts("#include #{inc.include?('<') ? inc : "\"#{inc}\""}") + end + end + output.puts('extern "C" {') if @options[:externcincludes] + mocks.each do |mock| + output.puts("#include \"#{mock}\"") + end + output.puts('}') if @options[:externcincludes] + output.puts('#include "CException.h"') if @options[:plugins].include?(:cexception) + + return unless @options[:enforce_strict_ordering] + + output.puts('') + output.puts('int GlobalExpectCount;') + output.puts('int GlobalVerifyOrder;') + output.puts('char* GlobalOrderError;') + end + + def create_run_test_params_struct(output) + output.puts("\n/*=======Structure Used By Test Runner=====*/") + output.puts('struct UnityRunTestParameters') + output.puts('{') + output.puts(' UnityTestFunction func;') + output.puts(' const char* name;') + output.puts(' UNITY_LINE_TYPE line_num;') + output.puts('};') + end + + def create_externs(output, tests, _mocks) + output.puts("\n/*=======External Functions This Runner Calls=====*/") + output.puts("extern void #{@options[:setup_name]}(void);") + output.puts("extern void #{@options[:teardown_name]}(void);") + output.puts("\n#ifdef __cplusplus\nextern \"C\"\n{\n#endif") if @options[:externc] + tests.each do |test| + output.puts("extern void #{test[:test]}(#{test[:call] || 'void'});") + end + output.puts("#ifdef __cplusplus\n}\n#endif") if @options[:externc] + output.puts('') + end + + def create_mock_management(output, mock_headers) + output.puts("\n/*=======Mock Management=====*/") + output.puts('static void CMock_Init(void)') + output.puts('{') + + if @options[:enforce_strict_ordering] + output.puts(' GlobalExpectCount = 0;') + output.puts(' GlobalVerifyOrder = 0;') + output.puts(' GlobalOrderError = NULL;') + end + + mocks = mock_headers.map { |mock| File.basename(mock, '.*') } + mocks.each do |mock| + mock_clean = TypeSanitizer.sanitize_c_identifier(mock) + output.puts(" #{mock_clean}_Init();") + end + output.puts("}\n") + + output.puts('static void CMock_Verify(void)') + output.puts('{') + mocks.each do |mock| + mock_clean = TypeSanitizer.sanitize_c_identifier(mock) + output.puts(" #{mock_clean}_Verify();") + end + output.puts("}\n") + + output.puts('static void CMock_Destroy(void)') + output.puts('{') + mocks.each do |mock| + mock_clean = TypeSanitizer.sanitize_c_identifier(mock) + output.puts(" #{mock_clean}_Destroy();") + end + output.puts("}\n") + end + + def create_setup(output) + return if @options[:has_setup] + + output.puts("\n/*=======Setup (stub)=====*/") + output.puts("void #{@options[:setup_name]}(void) {}") + end + + def create_teardown(output) + return if @options[:has_teardown] + + output.puts("\n/*=======Teardown (stub)=====*/") + output.puts("void #{@options[:teardown_name]}(void) {}") + end + + def create_suite_setup(output) + return if @options[:suite_setup].nil? + + output.puts("\n/*=======Suite Setup=====*/") + output.puts('void suiteSetUp(void)') + output.puts('{') + output.puts(@options[:suite_setup]) + output.puts('}') + end + + def create_suite_teardown(output) + return if @options[:suite_teardown].nil? + + output.puts("\n/*=======Suite Teardown=====*/") + output.puts('int suiteTearDown(int num_failures)') + output.puts('{') + output.puts(@options[:suite_teardown]) + output.puts('}') + end + + def create_reset(output) + output.puts("\n/*=======Test Reset Options=====*/") + output.puts("void #{@options[:test_reset_name]}(void);") + output.puts("void #{@options[:test_reset_name]}(void)") + output.puts('{') + output.puts(" #{@options[:teardown_name]}();") + output.puts(' CMock_Verify();') + output.puts(' CMock_Destroy();') + output.puts(' CMock_Init();') + output.puts(" #{@options[:setup_name]}();") + output.puts('}') + output.puts("void #{@options[:test_verify_name]}(void);") + output.puts("void #{@options[:test_verify_name]}(void)") + output.puts('{') + output.puts(' CMock_Verify();') + output.puts('}') + end + + def create_run_test(output) + require 'erb' + file = File.read(File.join(__dir__, 'run_test.erb')) + template = ERB.new(file, trim_mode: '<>') + output.puts("\n#{template.result(binding)}") + end + + def create_args_wrappers(output, tests) + return unless @options[:use_param_tests] + + output.puts("\n/*=======Parameterized Test Wrappers=====*/") + tests.each do |test| + next if test[:args].nil? || test[:args].empty? + + test[:args].each.with_index(1) do |args, idx| + output.puts("static void runner_args#{idx}_#{test[:test]}(void)") + output.puts('{') + output.puts(" #{test[:test]}(#{args});") + output.puts("}\n") + end + end + end + + def create_shuffle_tests(output) + output.puts("\n/*=======Shuffle Test Order=====*/") + output.puts('static void shuffleTests(struct UnityRunTestParameters run_test_params_arr[], int num_of_tests)') + output.puts('{') + + # Use Fisher-Yates shuffle algorithm + output.puts(' for (int i = num_of_tests - 1; i > 0; i--)') + output.puts(' {') + output.puts(' int j = rand() % (i + 1);') + output.puts(' struct UnityRunTestParameters temp = run_test_params_arr[i];') + output.puts(' run_test_params_arr[i] = run_test_params_arr[j];') + output.puts(' run_test_params_arr[j] = temp;') + output.puts(' }') + output.puts('}') + end + + def create_main(output, filename, tests, used_mocks) + output.puts("\n/*=======MAIN=====*/") + main_name = @options[:main_name].to_sym == :auto ? "main_#{filename.gsub('.c', '')}" : (@options[:main_name]).to_s + if @options[:cmdline_args] + if main_name != 'main' + output.puts("#{@options[:main_export_decl]} int #{main_name}(int argc, char** argv);") + end + output.puts("#{@options[:main_export_decl]} int #{main_name}(int argc, char** argv)") + output.puts('{') + output.puts('#ifdef UNITY_USE_COMMAND_LINE_ARGS') + output.puts(' int parse_status = UnityParseOptions(argc, argv);') + output.puts(' if (parse_status != 0)') + output.puts(' {') + output.puts(' if (parse_status < 0)') + output.puts(' {') + output.puts(" UnityPrint(\"#{filename.gsub('.c', '').gsub(/\\/, '\\\\\\')}.\");") + output.puts(' UNITY_PRINT_EOL();') + tests.each do |test| + if (!@options[:use_param_tests]) || test[:args].nil? || test[:args].empty? + output.puts(" UnityPrint(\" #{test[:test]}\");") + output.puts(' UNITY_PRINT_EOL();') + else + test[:args].each do |args| + output.puts(" UnityPrint(\" #{test[:test]}(#{args})\");") + output.puts(' UNITY_PRINT_EOL();') + end + end + end + output.puts(' return 0;') + output.puts(' }') + output.puts(' return parse_status;') + output.puts(' }') + output.puts('#endif') + else + main_return = @options[:omit_begin_end] ? 'void' : 'int' + if main_name != 'main' + output.puts("#{@options[:main_export_decl]} #{main_return} #{main_name}(void);") + end + output.puts("#{main_return} #{main_name}(void)") + output.puts('{') + end + output.puts(' suiteSetUp();') if @options[:has_suite_setup] + if @options[:omit_begin_end] + output.puts(" UnitySetTestFile(\"#{filename.gsub(/\\/, '\\\\\\')}\");") + else + output.puts(" UnityBegin(\"#{filename.gsub(/\\/, '\\\\\\')}\");") + end + if @options[:shuffle_tests] + output.puts + if @options[:rng_seed] == 0 + output.puts(' srand(time(NULL));') + else + output.puts(" srand(#{@options[:rng_seed]});") + end + end + output.puts + output.puts(" int number_of_tests = #{count_tests(tests)};") + output.puts(' struct UnityRunTestParameters run_test_params_arr[number_of_tests];') + output.puts + idx = 0 + tests.each do |test| + if (!@options[:use_param_tests]) || test[:args].nil? || test[:args].empty? + output.puts(" run_test_params_arr[#{idx}].func = #{test[:test]};") + output.puts(" run_test_params_arr[#{idx}].name = \"#{test[:test]}\";") + output.puts(" run_test_params_arr[#{idx}].line_num = #{test[:line_number]};") + idx += 1 + else + test[:args].each.with_index(1) do |args, arg_idx| + wrapper = "runner_args#{arg_idx}_#{test[:test]}" + testname = "#{test[:test]}(#{args})".dump + output.puts(" run_test_params_arr[#{idx}].func = #{wrapper};") + output.puts(" run_test_params_arr[#{idx}].name = #{testname};") + output.puts(" run_test_params_arr[#{idx}].line_num = #{test[:line_number]};") + idx += 1 + end + end + end + output.puts + if @options[:shuffle_tests] + output.puts(' shuffleTests(run_test_params_arr, number_of_tests);') + output.puts + end + output.puts(' for (int i = 0; i < number_of_tests; i++)') + output.puts(' {') + output.puts(' run_test(run_test_params_arr[i].func, run_test_params_arr[i].name, run_test_params_arr[i].line_num);') + output.puts(' }') + output.puts + output.puts(' CMock_Guts_MemFreeFinal();') unless used_mocks.empty? + if @options[:has_suite_teardown] + if @options[:omit_begin_end] + output.puts(' (void) suite_teardown(0);') + else + output.puts(' return suiteTearDown(UNITY_END());') + end + else + output.puts(' return UNITY_END();') unless @options[:omit_begin_end] + end + output.puts('}') + end + + def create_h_file(output, filename, tests, testfile_includes, used_mocks) + filename = File.basename(filename).gsub(/[-\/\\.,\s]/, '_').upcase + output.puts('/* AUTOGENERATED FILE. DO NOT EDIT. */') + output.puts("#ifndef _#{filename}") + output.puts("#define _#{filename}\n\n") + output.puts("#include \"#{@options[:framework]}.h\"") + output.puts('#include "cmock.h"') unless used_mocks.empty? + @options[:includes].flatten.uniq.compact.each do |inc| + output.puts("#include #{inc.include?('<') ? inc : "\"#{inc}\""}") + end + testfile_includes.each do |inc| + output.puts("#include #{inc.include?('<') ? inc : "\"#{inc}\""}") + end + output.puts "\n" + tests.each do |test| + if test[:params].nil? || test[:params].empty? + output.puts("void #{test[:test]}(void);") + else + output.puts("void #{test[:test]}(#{test[:params]});") + end + end + output.puts("#endif\n\n") + end +end + +if $0 == __FILE__ + options = { includes: [] } + + # parse out all the options first (these will all be removed as we go) + ARGV.reject! do |arg| + case arg + when '-cexception' + options[:plugins] = [:cexception] + true + when '-externcincludes' + options[:externcincludes] = true + true + when /\.*\.ya?ml$/ + options = UnityTestRunnerGenerator.grab_config(arg) + true + when /--(\w+)="?(.*)"?/ + options[Regexp.last_match(1).to_sym] = Regexp.last_match(2) + true + when /\.*\.(?:hpp|hh|H|h)$/ + options[:includes] << arg + true + else false + end + end + + # make sure there is at least one parameter left (the input file) + unless ARGV[0] + puts ["\nusage: ruby #{__FILE__} (files) (options) input_test_file (output)", + "\n input_test_file - this is the C file you want to create a runner for", + ' output - this is the name of the runner file to generate', + ' defaults to (input_test_file)_Runner', + ' files:', + ' *.yml / *.yaml - loads configuration from here in :unity or :cmock', + ' *.h - header files are added as #includes in runner', + ' options:', + ' -cexception - include cexception support', + ' -externc - add extern "C" for cpp support', + ' --setup_name="" - redefine setUp func name to something else', + ' --teardown_name="" - redefine tearDown func name to something else', + ' --main_name="" - redefine main func name to something else', + ' --test_prefix="" - redefine test prefix from default test|spec|should', + ' --test_reset_name="" - redefine resetTest func name to something else', + ' --test_verify_name="" - redefine verifyTest func name to something else', + ' --suite_setup="" - code to execute for setup of entire suite', + ' --suite_teardown="" - code to execute for teardown of entire suite', + ' --use_param_tests=1 - enable parameterized tests (disabled by default)', + ' --omit_begin_end=1 - omit calls to UnityBegin and UNITY_END (disabled by default)', + ' --header_file="" - path/name of test header file to generate too', + ' --shuffle_tests=1 - enable shuffling of the test execution order (disabled by default)', + ' --rng_seed=1 - seed value for randomization of test execution order'].join("\n") + exit 1 + end + + # create the default test runner name if not specified + ARGV[1] = ARGV[0].gsub('.c', '_Runner.c') unless ARGV[1] + + UnityTestRunnerGenerator.new(options).run(ARGV[0], ARGV[1]) +end diff --git a/source_code/external_code/Unity/auto/parse_output.rb b/source_code/external_code/Unity/auto/parse_output.rb new file mode 100644 index 0000000..1711337 --- /dev/null +++ b/source_code/external_code/Unity/auto/parse_output.rb @@ -0,0 +1,390 @@ +# ========================================================================= +# Unity - A Test Framework for C +# ThrowTheSwitch.org +# Copyright (c) 2007-25 Mike Karlesky, Mark VanderVoord, & Greg Williams +# SPDX-License-Identifier: MIT +# ========================================================================= + +#============================================================ +# Author: John Theofanopoulos +# A simple parser. Takes the output files generated during the +# build process and extracts information relating to the tests. +# +# Notes: +# To capture an output file under VS builds use the following: +# devenv [build instructions] > Output.txt & type Output.txt +# +# To capture an output file under Linux builds use the following: +# make | tee Output.txt +# +# This script can handle the following output formats: +# - normal output (raw unity) +# - fixture output (unity_fixture.h/.c) +# - fixture output with verbose flag set ("-v") +# - time output flag set (UNITY_INCLUDE_EXEC_TIME define enabled with milliseconds output) +# +# To use this parser use the following command +# ruby parseOutput.rb [options] [file] +# options: -xml : produce a JUnit compatible XML file +# -suiteRequiredSuiteName +# : replace default test suite name to +# "RequiredSuiteName" (can be any name) +# file: file to scan for results +#============================================================ + +# Parser class for handling the input file +class ParseOutput + def initialize + # internal data + @class_name_idx = 0 + @result_usual_idx = 3 + @path_delim = nil + + # xml output related + @xml_out = false + @array_list = false + + # current suite name and statistics + ## testsuite name + @real_test_suite_name = 'Unity' + ## classname for testcase + @test_suite = nil + @total_tests = 0 + @test_passed = 0 + @test_failed = 0 + @test_ignored = 0 + end + + # Set the flag to indicate if there will be an XML output file or not + def set_xml_output + @xml_out = true + end + + # Set the flag to indicate if there will be an XML output file or not + def test_suite_name=(cli_arg) + @real_test_suite_name = cli_arg + puts "Real test suite name will be '#{@real_test_suite_name}'" + end + + def xml_encode_s(str) + str.encode(:xml => :attr) + end + + # If write our output to XML + def write_xml_output + output = File.open('report.xml', 'w') + output << "\n" + @array_list.each do |item| + output << item << "\n" + end + end + + # Pushes the suite info as xml to the array list, which will be written later + def push_xml_output_suite_info + # Insert opening tag at front + heading = "" + @array_list.insert(0, heading) + # Push back the closing tag + @array_list.push '' + end + + # Pushes xml output data to the array list, which will be written later + def push_xml_output_passed(test_name, execution_time = 0) + @array_list.push " " + end + + # Pushes xml output data to the array list, which will be written later + def push_xml_output_failed(test_name, reason, execution_time = 0) + @array_list.push " " + @array_list.push " #{reason}" + @array_list.push ' ' + end + + # Pushes xml output data to the array list, which will be written later + def push_xml_output_ignored(test_name, reason, execution_time = 0) + @array_list.push " " + @array_list.push " #{reason}" + @array_list.push ' ' + end + + # This function will try and determine when the suite is changed. This is + # is the name that gets added to the classname parameter. + def test_suite_verify(test_suite_name) + # Split the path name + test_name = test_suite_name.split(@path_delim) + + # Remove the extension and extract the base_name + base_name = test_name[test_name.size - 1].split('.')[0] + + # Return if the test suite hasn't changed + return unless base_name.to_s != @test_suite.to_s + + @test_suite = base_name + printf "New Test: %s\n", @test_suite + end + + # Prepares the line for verbose fixture output ("-v") + def prepare_fixture_line(line) + line = line.sub('IGNORE_TEST(', '') + line = line.sub('TEST(', '') + line = line.sub(')', ',') + line = line.chomp + array = line.split(',') + array.map { |x| x.to_s.lstrip.chomp } + end + + # Test was flagged as having passed so format the output. + # This is using the Unity fixture output and not the original Unity output. + def test_passed_unity_fixture(array) + class_name = array[0] + test_name = array[1] + test_suite_verify(class_name) + printf "%-40s PASS\n", test_name + + push_xml_output_passed(test_name) if @xml_out + end + + # Test was flagged as having failed so format the output. + # This is using the Unity fixture output and not the original Unity output. + def test_failed_unity_fixture(array) + class_name = array[0] + test_name = array[1] + test_suite_verify(class_name) + reason_array = array[2].split(':') + reason = "#{reason_array[-1].lstrip.chomp} at line: #{reason_array[-4]}" + + printf "%-40s FAILED\n", test_name + + push_xml_output_failed(test_name, reason) if @xml_out + end + + # Test was flagged as being ignored so format the output. + # This is using the Unity fixture output and not the original Unity output. + def test_ignored_unity_fixture(array) + class_name = array[0] + test_name = array[1] + reason = 'No reason given' + if array.size > 2 + reason_array = array[2].split(':') + tmp_reason = reason_array[-1].lstrip.chomp + reason = tmp_reason == 'IGNORE' ? 'No reason given' : tmp_reason + end + test_suite_verify(class_name) + printf "%-40s IGNORED\n", test_name + + push_xml_output_ignored(test_name, reason) if @xml_out + end + + # Test was flagged as having passed so format the output + def test_passed(array) + # ':' symbol will be valid in function args now + real_method_name = array[@result_usual_idx - 1..-2].join(':') + array = array[0..@result_usual_idx - 2] + [real_method_name] + [array[-1]] + + last_item = array.length - 1 + test_time = get_test_time(array[last_item]) + test_name = array[last_item - 1] + test_suite_verify(array[@class_name_idx]) + printf "%-40s PASS %10d ms\n", test_name, test_time + + return unless @xml_out + + push_xml_output_passed(test_name, test_time) if @xml_out + end + + # Test was flagged as having failed so format the line + def test_failed(array) + # ':' symbol will be valid in function args now + real_method_name = array[@result_usual_idx - 1..-3].join(':') + array = array[0..@result_usual_idx - 3] + [real_method_name] + array[-2..] + + last_item = array.length - 1 + test_time = get_test_time(array[last_item]) + test_name = array[last_item - 2] + reason = "#{array[last_item].chomp.lstrip} at line: #{array[last_item - 3]}" + class_name = array[@class_name_idx] + + if test_name.start_with? 'TEST(' + array2 = test_name.split(' ') + + test_suite = array2[0].sub('TEST(', '') + test_suite = test_suite.sub(',', '') + class_name = test_suite + + test_name = array2[1].sub(')', '') + end + + test_suite_verify(class_name) + printf "%-40s FAILED %10d ms\n", test_name, test_time + + push_xml_output_failed(test_name, reason, test_time) if @xml_out + end + + # Test was flagged as being ignored so format the output + def test_ignored(array) + # ':' symbol will be valid in function args now + real_method_name = array[@result_usual_idx - 1..-3].join(':') + array = array[0..@result_usual_idx - 3] + [real_method_name] + array[-2..] + + last_item = array.length - 1 + test_time = get_test_time(array[last_item]) + test_name = array[last_item - 2] + reason = array[last_item].chomp.lstrip + class_name = array[@class_name_idx] + + if test_name.start_with? 'TEST(' + array2 = test_name.split(' ') + + test_suite = array2[0].sub('TEST(', '') + test_suite = test_suite.sub(',', '') + class_name = test_suite + + test_name = array2[1].sub(')', '') + end + + test_suite_verify(class_name) + printf "%-40s IGNORED %10d ms\n", test_name, test_time + + push_xml_output_ignored(test_name, reason, test_time) if @xml_out + end + + # Test time will be in ms + def get_test_time(value_with_time) + test_time_array = value_with_time.scan(/\((-?\d+.?\d*) ms\)\s*$/).flatten.map do |arg_value_str| + arg_value_str.include?('.') ? arg_value_str.to_f : arg_value_str.to_i + end + + test_time_array.any? ? test_time_array[0] : 0 + end + + # Adjusts the os specific members according to the current path style + # (Windows or Unix based) + def detect_os_specifics(line) + if line.include? '\\' + # Windows X:\Y\Z + @class_name_idx = 1 + @path_delim = '\\' + else + # Unix Based /X/Y/Z + @class_name_idx = 0 + @path_delim = '/' + end + end + + # Main function used to parse the file that was captured. + def process(file_name) + @array_list = [] + + puts "Parsing file: #{file_name}" + + @test_passed = 0 + @test_failed = 0 + @test_ignored = 0 + puts '' + puts '=================== RESULTS =====================' + puts '' + # Apply binary encoding. Bad symbols will be unchanged + File.open(file_name, 'rb').each do |line| + # Typical test lines look like these: + # ---------------------------------------------------- + # 1. normal output: + # /.c:36:test_tc1000_opsys:FAIL: Expected 1 Was 0 + # /.c:112:test_tc5004_initCanChannel:IGNORE: Not Yet Implemented + # /.c:115:test_tc5100_initCanVoidPtrs:PASS + # + # 2. fixture output + # /.c:63:TEST(, ):FAIL: Expected 0x00001234 Was 0x00005A5A + # /.c:36:TEST(, ):IGNORE + # Note: "PASS" information won't be generated in this mode + # + # 3. fixture output with verbose information ("-v") + # TEST()/:168::FAIL: Expected 0x8D Was 0x8C + # TEST(, )/:22::IGNORE: This Test Was Ignored On Purpose + # IGNORE_TEST() + # TEST() PASS + # + # Note: Where path is different on Unix vs Windows devices (Windows leads with a drive letter)! + detect_os_specifics(line) + line_array = line.split(':') + + # If we were able to split the line then we can look to see if any of our target words + # were found. Case is important. + next unless (line_array.size >= 4) || (line.start_with? 'TEST(') || (line.start_with? 'IGNORE_TEST(') + + # check if the output is fixture output (with verbose flag "-v") + if (line.start_with? 'TEST(') || (line.start_with? 'IGNORE_TEST(') + line_array = prepare_fixture_line(line) + if line.include? ' PASS' + test_passed_unity_fixture(line_array) + @test_passed += 1 + elsif line.include? 'FAIL' + test_failed_unity_fixture(line_array) + @test_failed += 1 + elsif line.include? 'IGNORE' + test_ignored_unity_fixture(line_array) + @test_ignored += 1 + end + # normal output / fixture output (without verbose "-v") + elsif line.include? ':PASS' + test_passed(line_array) + @test_passed += 1 + elsif line.include? ':FAIL' + test_failed(line_array) + @test_failed += 1 + elsif line.include? ':IGNORE:' + test_ignored(line_array) + @test_ignored += 1 + elsif line.include? ':IGNORE' + line_array.push('No reason given') + test_ignored(line_array) + @test_ignored += 1 + elsif line_array.size >= 4 + # We will check output from color compilation + if line_array[@result_usual_idx..].any? { |l| l.include? 'PASS' } + test_passed(line_array) + @test_passed += 1 + elsif line_array[@result_usual_idx..].any? { |l| l.include? 'FAIL' } + test_failed(line_array) + @test_failed += 1 + elsif line_array[@result_usual_idx..-2].any? { |l| l.include? 'IGNORE' } + test_ignored(line_array) + @test_ignored += 1 + elsif line_array[@result_usual_idx..].any? { |l| l.include? 'IGNORE' } + line_array.push("No reason given (#{get_test_time(line_array[@result_usual_idx..])} ms)") + test_ignored(line_array) + @test_ignored += 1 + end + end + @total_tests = @test_passed + @test_failed + @test_ignored + end + puts '' + puts '=================== SUMMARY =====================' + puts '' + puts "Tests Passed : #{@test_passed}" + puts "Tests Failed : #{@test_failed}" + puts "Tests Ignored : #{@test_ignored}" + + return unless @xml_out + + # push information about the suite + push_xml_output_suite_info + # write xml output file + write_xml_output + end +end + +# If the command line has no values in, used a default value of Output.txt +parse_my_file = ParseOutput.new + +if ARGV.size >= 1 + ARGV.each do |arg| + if arg == '-xml' + parse_my_file.set_xml_output + elsif arg.start_with?('-suite') + parse_my_file.test_suite_name = arg.delete_prefix('-suite') + else + parse_my_file.process(arg) + break + end + end +end diff --git a/source_code/external_code/Unity/auto/run_test.erb b/source_code/external_code/Unity/auto/run_test.erb new file mode 100644 index 0000000..e334d65 --- /dev/null +++ b/source_code/external_code/Unity/auto/run_test.erb @@ -0,0 +1,37 @@ +/*=======Test Runner Used To Run Each Test=====*/ +static void run_test(UnityTestFunction func, const char* name, UNITY_LINE_TYPE line_num) +{ + Unity.CurrentTestName = name; + Unity.CurrentTestLineNumber = (UNITY_UINT) line_num; +#ifdef UNITY_USE_COMMAND_LINE_ARGS + if (!UnityTestMatches()) + return; +#endif + Unity.NumberOfTests++; + UNITY_CLR_DETAILS(); + UNITY_EXEC_TIME_START(); + CMock_Init(); + if (TEST_PROTECT()) + { +<% if @options[:plugins].include?(:cexception) %> + volatile CEXCEPTION_T e; + Try { + <%= @options[:setup_name] %>(); + func(); + } Catch(e) { + TEST_ASSERT_EQUAL_HEX32_MESSAGE(CEXCEPTION_NONE, e, "Unhandled Exception!"); + } +<% else %> + <%= @options[:setup_name] %>(); + func(); +<% end %> + } + if (TEST_PROTECT()) + { + <%= @options[:teardown_name] %>(); + CMock_Verify(); + } + CMock_Destroy(); + UNITY_EXEC_TIME_STOP(); + UnityConcludeTest(); +} diff --git a/source_code/external_code/Unity/auto/stylize_as_junit.py b/source_code/external_code/Unity/auto/stylize_as_junit.py new file mode 100644 index 0000000..49e8aff --- /dev/null +++ b/source_code/external_code/Unity/auto/stylize_as_junit.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python3 +# ========================================================================= +# Unity - A Test Framework for C +# ThrowTheSwitch.org +# Copyright (c) 2007-25 Mike Karlesky, Mark VanderVoord, & Greg Williams +# SPDX-License-Identifier: MIT +# ========================================================================= + +import sys +import os +from glob import glob +import argparse + +from pyparsing import * +from junit_xml import TestSuite, TestCase + + +class UnityTestSummary: + def __init__(self): + self.report = '' + self.total_tests = 0 + self.failures = 0 + self.ignored = 0 + self.targets = 0 + self.root = None + self.output = None + self.test_suites = dict() + + def run(self): + # Clean up result file names + results = [] + for target in self.targets: + results.append(target.replace('\\', '/')) + + # Dig through each result file, looking for details on pass/fail: + for result_file in results: + lines = list(map(lambda line: line.rstrip(), open(result_file, "r").read().split('\n'))) + if len(lines) == 0: + raise Exception("Empty test result file: %s" % result_file) + + # define an expression for your file reference + entry_one = Combine( + oneOf(list(alphas)) + ':/' + + Word(alphanums + '_-./')) + + entry_two = Word(printables + ' ', excludeChars=':') + entry = entry_one | entry_two + + delimiter = Literal(':').suppress() + # Format of a result line is `[file_name]:line:test_name:RESULT[:msg]` + tc_result_line = Group(ZeroOrMore(entry.setResultsName('tc_file_name')) + + delimiter + entry.setResultsName('tc_line_nr') + + delimiter + entry.setResultsName('tc_name') + + delimiter + entry.setResultsName('tc_status') + + Optional(delimiter + entry.setResultsName('tc_msg'))).setResultsName("tc_line") + + eol = LineEnd().suppress() + sol = LineStart().suppress() + blank_line = sol + eol + + # Format of the summary line is `# Tests # Failures # Ignored` + tc_summary_line = Group(Word(nums).setResultsName("num_of_tests") + "Tests" + Word(nums).setResultsName( + "num_of_fail") + "Failures" + Word(nums).setResultsName("num_of_ignore") + "Ignored").setResultsName( + "tc_summary") + tc_end_line = Or(Literal("FAIL"), Literal('Ok')).setResultsName("tc_result") + + # run it and see... + pp1 = tc_result_line | Optional(tc_summary_line | tc_end_line) + pp1.ignore(blank_line | OneOrMore("-")) + + result = list() + for l in lines: + result.append((pp1.parseString(l)).asDict()) + # delete empty results + result = filter(None, result) + + tc_list = list() + for r in result: + if 'tc_line' in r: + tmp_tc_line = r['tc_line'] + + # get only the file name which will be used as the classname + if 'tc_file_name' in tmp_tc_line: + file_name = tmp_tc_line['tc_file_name'].split('\\').pop().split('/').pop().rsplit('.', 1)[0] + else: + file_name = result_file.strip("./") + tmp_tc = TestCase(name=tmp_tc_line['tc_name'], classname=file_name) + if 'tc_status' in tmp_tc_line: + if str(tmp_tc_line['tc_status']) == 'IGNORE': + if 'tc_msg' in tmp_tc_line: + tmp_tc.add_skipped_info(message=tmp_tc_line['tc_msg'], + output=r'[File]={0}, [Line]={1}'.format( + tmp_tc_line['tc_file_name'], tmp_tc_line['tc_line_nr'])) + else: + tmp_tc.add_skipped_info(message=" ") + elif str(tmp_tc_line['tc_status']) == 'FAIL': + if 'tc_msg' in tmp_tc_line: + tmp_tc.add_failure_info(message=tmp_tc_line['tc_msg'], + output=r'[File]={0}, [Line]={1}'.format( + tmp_tc_line['tc_file_name'], tmp_tc_line['tc_line_nr'])) + else: + tmp_tc.add_failure_info(message=" ") + + tc_list.append((str(result_file), tmp_tc)) + + for k, v in tc_list: + try: + self.test_suites[k].append(v) + except KeyError: + self.test_suites[k] = [v] + ts = [] + for suite_name in self.test_suites: + ts.append(TestSuite(suite_name, self.test_suites[suite_name])) + + with open(self.output, 'w') as f: + TestSuite.to_file(f, ts, prettyprint='True', encoding='utf-8') + + return self.report + + def set_targets(self, target_array): + self.targets = target_array + + def set_root_path(self, path): + self.root = path + + def set_output(self, output): + self.output = output + + +if __name__ == '__main__': + uts = UnityTestSummary() + parser = argparse.ArgumentParser(description= + """Takes as input the collection of *.testpass and *.testfail result + files, and converts them to a JUnit formatted XML.""") + parser.add_argument('targets_dir', metavar='result_file_directory', + type=str, nargs='?', default='./', + help="""The location of your results files. + Defaults to current directory if not specified.""") + parser.add_argument('root_path', nargs='?', + default='os.path.split(__file__)[0]', + help="""Helpful for producing more verbose output if + using relative paths.""") + parser.add_argument('--output', '-o', type=str, default="result.xml", + help="""The name of the JUnit-formatted file (XML).""") + args = parser.parse_args() + + if args.targets_dir[-1] != '/': + args.targets_dir+='/' + targets = list(map(lambda x: x.replace('\\', '/'), glob(args.targets_dir + '*.test*'))) + if len(targets) == 0: + raise Exception("No *.testpass or *.testfail files found in '%s'" % args.targets_dir) + uts.set_targets(targets) + + # set the root path + uts.set_root_path(args.root_path) + + # set output + uts.set_output(args.output) + + # run the summarizer + print(uts.run()) diff --git a/source_code/external_code/Unity/auto/stylize_as_junit.rb b/source_code/external_code/Unity/auto/stylize_as_junit.rb new file mode 100755 index 0000000..e003918 --- /dev/null +++ b/source_code/external_code/Unity/auto/stylize_as_junit.rb @@ -0,0 +1,253 @@ +#!/usr/bin/env ruby +# ========================================================================= +# Unity - A Test Framework for C +# ThrowTheSwitch.org +# Copyright (c) 2007-25 Mike Karlesky, Mark VanderVoord, & Greg Williams +# SPDX-License-Identifier: MIT +# ========================================================================= + +require 'fileutils' +require 'optparse' +require 'ostruct' +require 'set' + +VERSION = 1.0 + +class ArgvParser + # + # Return a structure describing the options. + # + def self.parse(args) + # The options specified on the command line will be collected in *options*. + # We set default values here. + options = OpenStruct.new + options.results_dir = '.' + options.root_path = '.' + options.out_file = 'results.xml' + + opts = OptionParser.new do |o| + o.banner = 'Usage: unity_to_junit.rb [options]' + + o.separator '' + o.separator 'Specific options:' + + o.on('-r', '--results ', 'Look for Unity Results files here.') do |results| + # puts "results #{results}" + options.results_dir = results + end + + o.on('-p', '--root_path ', 'Prepend this path to files in results.') do |root_path| + options.root_path = root_path + end + + o.on('-o', '--output ', 'XML file to generate.') do |out_file| + # puts "out_file: #{out_file}" + options.out_file = out_file + end + + o.separator '' + o.separator 'Common options:' + + # No argument, shows at tail. This will print an options summary. + o.on_tail('-h', '--help', 'Show this message') do + puts o + exit + end + + # Another typical switch to print the version. + o.on_tail('--version', 'Show version') do + puts "unity_to_junit.rb version #{VERSION}" + exit + end + end + + opts.parse!(args) + options + end +end + +class UnityToJUnit + include FileUtils::Verbose + attr_reader :report, :total_tests, :failures, :ignored + attr_writer :targets, :root, :out_file + + def initialize + @report = '' + @unit_name = '' + end + + def run + # Clean up result file names + results = @targets.map { |target| target.tr('\\', '/') } + # puts "Output File: #{@out_file}" + f = File.new(@out_file, 'w') + write_xml_header(f) + write_suites_header(f) + results.each do |result_file| + lines = File.readlines(result_file).map(&:chomp) + + raise "Empty test result file: #{result_file}" if lines.empty? + + result_output = get_details(result_file, lines) + tests, failures, ignored = parse_test_summary(lines) + result_output[:counts][:total] = tests + result_output[:counts][:failed] = failures + result_output[:counts][:ignored] = ignored + result_output[:counts][:passed] = (result_output[:counts][:total] - result_output[:counts][:failed] - result_output[:counts][:ignored]) + + # use line[0] from the test output to get the test_file path and name + test_file_str = lines[0].tr('\\', '/') + test_file_str = test_file_str.split(':') + test_file = if test_file_str.length < 2 + result_file + else + "#{test_file_str[0]}:#{test_file_str[1]}" + end + result_output[:source][:path] = File.dirname(test_file) + result_output[:source][:file] = File.basename(test_file) + + # save result_output + @unit_name = File.basename(test_file, '.*') + + write_suite_header(result_output[:counts], f) + write_failures(result_output, f) + write_tests(result_output, f) + write_ignored(result_output, f) + write_suite_footer(f) + end + write_suites_footer(f) + f.close + end + + def usage(err_msg = nil) + puts "\nERROR: " + puts err_msg if err_msg + puts 'Usage: unity_to_junit.rb [options]' + puts '' + puts 'Specific options:' + puts ' -r, --results Look for Unity Results files here.' + puts ' -p, --root_path Prepend this path to files in results.' + puts ' -o, --output XML file to generate.' + puts '' + puts 'Common options:' + puts ' -h, --help Show this message' + puts ' --version Show version' + + exit 1 + end + + protected + + def get_details(_result_file, lines) + results = results_structure + lines.each do |line| + line = line.tr('\\', '/') + _src_file, src_line, test_name, status, msg = line.split(/:/) + case status + when 'IGNORE' then results[:ignores] << { test: test_name, line: src_line, message: msg } + when 'FAIL' then results[:failures] << { test: test_name, line: src_line, message: msg } + when 'PASS' then results[:successes] << { test: test_name, line: src_line, message: msg } + end + end + results + end + + def parse_test_summary(summary) + raise "Couldn't parse test results: #{summary}" unless summary.find { |v| v =~ /(\d+) Tests (\d+) Failures (\d+) Ignored/ } + + [Regexp.last_match(1).to_i, Regexp.last_match(2).to_i, Regexp.last_match(3).to_i] + end + + private + + def results_structure + { + source: { path: '', file: '' }, + successes: [], + failures: [], + ignores: [], + counts: { total: 0, passed: 0, failed: 0, ignored: 0 }, + stdout: [] + } + end + + def write_xml_header(stream) + stream.puts "" + end + + def write_suites_header(stream) + stream.puts '' + end + + def write_suite_header(counts, stream) + stream.puts "\t" + end + + def write_failures(results, stream) + result = results[:failures] + result.each do |item| + filename = File.join(results[:source][:path], File.basename(results[:source][:file], '.*')) + stream.puts "\t\t" + stream.puts "\t\t\t" + stream.puts "\t\t\t [File] #{filename} [Line] #{item[:line]} " + stream.puts "\t\t" + end + end + + def write_tests(results, stream) + result = results[:successes] + result.each do |item| + stream.puts "\t\t" + end + end + + def write_ignored(results, stream) + result = results[:ignores] + result.each do |item| + filename = File.join(results[:source][:path], File.basename(results[:source][:file], '.*')) + puts "Writing ignored tests for test harness: #{filename}" + stream.puts "\t\t" + stream.puts "\t\t\t" + stream.puts "\t\t\t [File] #{filename} [Line] #{item[:line]} " + stream.puts "\t\t" + end + end + + def write_suite_footer(stream) + stream.puts "\t" + end + + def write_suites_footer(stream) + stream.puts '' + end +end + +if $0 == __FILE__ + # parse out the command options + options = ArgvParser.parse(ARGV) + + # create an instance to work with + utj = UnityToJUnit.new + begin + # look in the specified or current directory for result files + targets = "#{options.results_dir.tr('\\', '/')}**/*.test*" + + results = Dir[targets] + + raise "No *.testpass, *.testfail, or *.testresults files found in '#{targets}'" if results.empty? + + utj.targets = results + + # set the root path + utj.root = options.root_path + + # set the output XML file name + # puts "Output File from options: #{options.out_file}" + utj.out_file = options.out_file + + # run the summarizer + puts utj.run + rescue StandardError => e + utj.usage e.message + end +end diff --git a/source_code/external_code/Unity/auto/test_file_filter.rb b/source_code/external_code/Unity/auto/test_file_filter.rb new file mode 100644 index 0000000..f9c8d90 --- /dev/null +++ b/source_code/external_code/Unity/auto/test_file_filter.rb @@ -0,0 +1,28 @@ +# ========================================================================= +# Unity - A Test Framework for C +# ThrowTheSwitch.org +# Copyright (c) 2007-25 Mike Karlesky, Mark VanderVoord, & Greg Williams +# SPDX-License-Identifier: MIT +# ========================================================================= + +require_relative 'yaml_helper' + +module RakefileHelpers + class TestFileFilter + def initialize(all_files = false) + @all_files = all_files + + return unless @all_files + + file = 'test_file_filter.yml' + return unless File.exist?(file) + + filters = YamlHelper.load_file(file) + @all_files = filters[:all_files] + @only_files = filters[:only_files] + @exclude_files = filters[:exclude_files] + end + + attr_accessor :all_files, :only_files, :exclude_files + end +end diff --git a/source_code/external_code/Unity/auto/type_sanitizer.rb b/source_code/external_code/Unity/auto/type_sanitizer.rb new file mode 100644 index 0000000..cfadb0d --- /dev/null +++ b/source_code/external_code/Unity/auto/type_sanitizer.rb @@ -0,0 +1,13 @@ +# ========================================================================= +# Unity - A Test Framework for C +# ThrowTheSwitch.org +# Copyright (c) 2007-25 Mike Karlesky, Mark VanderVoord, & Greg Williams +# SPDX-License-Identifier: MIT +# ========================================================================= + +module TypeSanitizer + def self.sanitize_c_identifier(unsanitized) + # convert filename to valid C identifier by replacing invalid chars with '_' + unsanitized.gsub(/[-\/\\.,\s]/, '_') + end +end diff --git a/source_code/external_code/Unity/auto/unity_test_summary.py b/source_code/external_code/Unity/auto/unity_test_summary.py new file mode 100644 index 0000000..60f5fd4 --- /dev/null +++ b/source_code/external_code/Unity/auto/unity_test_summary.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +# ========================================================================= +# Unity - A Test Framework for C +# ThrowTheSwitch.org +# Copyright (c) 2007-25 Mike Karlesky, Mark VanderVoord, & Greg Williams +# SPDX-License-Identifier: MIT +# ========================================================================= + +import sys +import os +import re +from glob import glob + +class UnityTestSummary: + def __init__(self): + self.report = '' + self.total_tests = 0 + self.failures = 0 + self.ignored = 0 + + def run(self): + # Clean up result file names + results = [] + for target in self.targets: + results.append(target.replace('\\', '/')) + + # Dig through each result file, looking for details on pass/fail: + failure_output = [] + ignore_output = [] + + for result_file in results: + lines = list(map(lambda line: line.rstrip(), open(result_file, "r").read().split('\n'))) + if len(lines) == 0: + raise Exception("Empty test result file: %s" % result_file) + + details = self.get_details(result_file, lines) + failures = details['failures'] + ignores = details['ignores'] + if len(failures) > 0: failure_output.append('\n'.join(failures)) + if len(ignores) > 0: ignore_output.append('n'.join(ignores)) + tests,failures,ignored = self.parse_test_summary('\n'.join(lines)) + self.total_tests += tests + self.failures += failures + self.ignored += ignored + + if self.ignored > 0: + self.report += "\n" + self.report += "--------------------------\n" + self.report += "UNITY IGNORED TEST SUMMARY\n" + self.report += "--------------------------\n" + self.report += "\n".join(ignore_output) + + if self.failures > 0: + self.report += "\n" + self.report += "--------------------------\n" + self.report += "UNITY FAILED TEST SUMMARY\n" + self.report += "--------------------------\n" + self.report += '\n'.join(failure_output) + + self.report += "\n" + self.report += "--------------------------\n" + self.report += "OVERALL UNITY TEST SUMMARY\n" + self.report += "--------------------------\n" + self.report += "{total_tests} TOTAL TESTS {failures} TOTAL FAILURES {ignored} IGNORED\n".format(total_tests = self.total_tests, failures=self.failures, ignored=self.ignored) + self.report += "\n" + + return self.report + + def set_targets(self, target_array): + self.targets = target_array + + def set_root_path(self, path): + self.root = path + + def usage(self, err_msg=None): + print("\nERROR: ") + if err_msg: + print(err_msg) + print("\nUsage: unity_test_summary.py result_file_directory/ root_path/") + print(" result_file_directory - The location of your results files.") + print(" Defaults to current directory if not specified.") + print(" Should end in / if specified.") + print(" root_path - Helpful for producing more verbose output if using relative paths.") + sys.exit(1) + + def get_details(self, result_file, lines): + results = { 'failures': [], 'ignores': [], 'successes': [] } + for line in lines: + parts = line.split(':') + if len(parts) == 5: + src_file,src_line,test_name,status,msg = parts + elif len(parts) == 4: + src_file,src_line,test_name,status = parts + msg = '' + else: + continue + if len(self.root) > 0: + line_out = "%s%s" % (self.root, line) + else: + line_out = line + if status == 'IGNORE': + results['ignores'].append(line_out) + elif status == 'FAIL': + results['failures'].append(line_out) + elif status == 'PASS': + results['successes'].append(line_out) + return results + + def parse_test_summary(self, summary): + m = re.search(r"([0-9]+) Tests ([0-9]+) Failures ([0-9]+) Ignored", summary) + if not m: + raise Exception("Couldn't parse test results: %s" % summary) + + return int(m.group(1)), int(m.group(2)), int(m.group(3)) + + +if __name__ == '__main__': + uts = UnityTestSummary() + try: + #look in the specified or current directory for result files + if len(sys.argv) > 1: + targets_dir = sys.argv[1] + else: + targets_dir = './' + targets = list(map(lambda x: x.replace('\\', '/'), glob(targets_dir + '**/*.test*', recursive=True))) + if len(targets) == 0: + raise Exception("No *.testpass or *.testfail files found in '%s'" % targets_dir) + uts.set_targets(targets) + + #set the root path + if len(sys.argv) > 2: + root_path = sys.argv[2] + else: + root_path = os.path.split(__file__)[0] + uts.set_root_path(root_path) + + #run the summarizer + print(uts.run()) + except Exception as e: + uts.usage(e) diff --git a/source_code/external_code/Unity/auto/unity_test_summary.rb b/source_code/external_code/Unity/auto/unity_test_summary.rb new file mode 100644 index 0000000..f6a64de --- /dev/null +++ b/source_code/external_code/Unity/auto/unity_test_summary.rb @@ -0,0 +1,140 @@ +#!/usr/bin/env ruby +# ========================================================================= +# Unity - A Test Framework for C +# ThrowTheSwitch.org +# Copyright (c) 2007-25 Mike Karlesky, Mark VanderVoord, & Greg Williams +# SPDX-License-Identifier: MIT +# ========================================================================= + +# +# unity_test_summary.rb +# +require 'fileutils' +require 'set' + +class UnityTestSummary + include FileUtils::Verbose + + attr_reader :report, :total_tests, :failures, :ignored + attr_writer :targets, :root + + def initialize(_opts = {}) + @report = '' + @total_tests = 0 + @failures = 0 + @ignored = 0 + end + + def run + # Clean up result file names + results = @targets.map { |target| target.tr('\\', '/') } + + # Dig through each result file, looking for details on pass/fail: + failure_output = [] + ignore_output = [] + + results.each do |result_file| + lines = File.readlines(result_file).map(&:chomp) + + raise "Empty test result file: #{result_file}" if lines.empty? + + output = get_details(result_file, lines) + failure_output << output[:failures] unless output[:failures].empty? + ignore_output << output[:ignores] unless output[:ignores].empty? + tests, failures, ignored = parse_test_summary(lines) + @total_tests += tests + @failures += failures + @ignored += ignored + end + + if @ignored > 0 + @report += "\n" + @report += "--------------------------\n" + @report += "UNITY IGNORED TEST SUMMARY\n" + @report += "--------------------------\n" + @report += ignore_output.flatten.join("\n") + end + + if @failures > 0 + @report += "\n" + @report += "--------------------------\n" + @report += "UNITY FAILED TEST SUMMARY\n" + @report += "--------------------------\n" + @report += failure_output.flatten.join("\n") + end + + @report += "\n" + @report += "--------------------------\n" + @report += "OVERALL UNITY TEST SUMMARY\n" + @report += "--------------------------\n" + @report += "#{@total_tests} TOTAL TESTS #{@failures} TOTAL FAILURES #{@ignored} IGNORED\n" + @report += "\n" + end + + def usage(err_msg = nil) + puts "\nERROR: " + puts err_msg if err_msg + puts "\nUsage: unity_test_summary.rb result_file_directory/ root_path/" + puts ' result_file_directory - The location of your results files.' + puts ' Defaults to current directory if not specified.' + puts ' Should end in / if specified.' + puts ' root_path - Helpful for producing more verbose output if using relative paths.' + exit 1 + end + + protected + + def get_details(_result_file, lines) + results = { failures: [], ignores: [], successes: [] } + lines.each do |line| + status_match = line.match(/^[^:]+:[^:]+:\w+(?:\([^)]*\))?:([^:]+):?/) + next unless status_match + + status = status_match.captures[0] + + line_out = (@root && (@root != 0) ? "#{@root}#{line}" : line).gsub(/\//, '\\') + case status + when 'IGNORE' then results[:ignores] << line_out + when 'FAIL' then results[:failures] << line_out + when 'PASS' then results[:successes] << line_out + end + end + results + end + + def parse_test_summary(summary) + raise "Couldn't parse test results: #{summary}" unless summary.find { |v| v =~ /(\d+) Tests (\d+) Failures (\d+) Ignored/ } + + [Regexp.last_match(1).to_i, Regexp.last_match(2).to_i, Regexp.last_match(3).to_i] + end +end + +if $0 == __FILE__ + + # parse out the command options + opts, args = ARGV.partition { |v| v =~ /^--\w+/ } + opts.map! { |v| v[2..].to_sym } + + # create an instance to work with + uts = UnityTestSummary.new(opts) + + begin + # look in the specified or current directory for result files + args[0] ||= './' + targets = "#{ARGV[0].tr('\\', '/')}**/*.test*" + results = Dir[targets] + + raise "No *.testpass, *.testfail, or *.testresults files found in '#{targets}'" if results.empty? + + uts.targets = results + + # set the root path + args[1] ||= "#{Dir.pwd}/" + uts.root = ARGV[1] + + # run the summarizer + puts uts.run + rescue StandardError => e + uts.usage e.message + end +end diff --git a/source_code/external_code/Unity/auto/yaml_helper.rb b/source_code/external_code/Unity/auto/yaml_helper.rb new file mode 100644 index 0000000..6d1bf7a --- /dev/null +++ b/source_code/external_code/Unity/auto/yaml_helper.rb @@ -0,0 +1,23 @@ +# ========================================================================= +# Unity - A Test Framework for C +# ThrowTheSwitch.org +# Copyright (c) 2007-25 Mike Karlesky, Mark VanderVoord, & Greg Williams +# SPDX-License-Identifier: MIT +# ========================================================================= + +require 'yaml' + +module YamlHelper + def self.load(body) + if YAML.respond_to?(:unsafe_load) + YAML.unsafe_load(body) + else + YAML.load(body) + end + end + + def self.load_file(file) + body = File.read(file) + self.load(body) + end +end diff --git a/source_code/external_code/Unity/extras/bdd/readme.md b/source_code/external_code/Unity/extras/bdd/readme.md new file mode 100644 index 0000000..e703588 --- /dev/null +++ b/source_code/external_code/Unity/extras/bdd/readme.md @@ -0,0 +1,40 @@ +# Unity Project - BDD Feature + +Unity's Behavior-Driven Development (BDD) test feature. It allows developers to structure and describe various phases (Given, When, Then) of a test scenario in a BDD-style format. + +## Introduction + +This project is based on the Unity framework originally created by Mike Karlesky, Mark VanderVoord, and Greg Williams in 2007. The project extends Unity by providing macros to define BDD structures with descriptive elements. Feature added by Michael Gene Brockus (Dreamer). + +## License + +This project is distributed under the MIT License. See the [license.txt](license.txt) file for more information. + +## Usage + +### BDD Macros + +The provided BDD macros allow you to structure your test scenarios in a descriptive manner. These macros are for descriptive purposes only and do not have functional behavior. + +- `GIVEN(description)`: Describes the "Given" phase of a test scenario. +- `WHEN(description)`: Describes the "When" phase of a test scenario. +- `THEN(description)`: Describes the "Then" phase of a test scenario. + +Example usage: + +```c +GIVEN("a valid input") { + // Test setup and context + // ... + + WHEN("the input is processed") { + // Perform the action + // ... + + THEN("the expected outcome occurs") { + // Assert the outcome + // ... + } + } +} +``` diff --git a/source_code/external_code/Unity/extras/bdd/src/unity_bdd.h b/source_code/external_code/Unity/extras/bdd/src/unity_bdd.h new file mode 100644 index 0000000..feff492 --- /dev/null +++ b/source_code/external_code/Unity/extras/bdd/src/unity_bdd.h @@ -0,0 +1,44 @@ +/* ========================================================================= + Unity - A Test Framework for C + ThrowTheSwitch.org + Copyright (c) 2007-25 Mike Karlesky, Mark VanderVoord, & Greg Williams + SPDX-License-Identifier: MIT +========================================================================= */ + +#ifndef UNITY_BDD_TEST_H_ +#define UNITY_BDD_TEST_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +/** + * @brief Macros for defining a Behavior-Driven Development (BDD) structure with descriptions. + * + * These macros provide a way to structure and describe different phases (Given, When, Then) of a + * test scenario in a BDD-style format. However, they don't have functional behavior by themselves + * and are used for descriptive purposes. + */ +#define GIVEN(description) \ + if (0) { \ + printf("Given %s\n", description); \ + } else + +#define WHEN(description) \ + if (0) { \ + printf("When %s\n", description); \ + } else + +#define THEN(description) \ + if (0) { \ + printf("Then %s\n", description); \ + } else + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source_code/external_code/Unity/extras/eclipse/error_parsers.txt b/source_code/external_code/Unity/extras/eclipse/error_parsers.txt new file mode 100644 index 0000000..94e34ff --- /dev/null +++ b/source_code/external_code/Unity/extras/eclipse/error_parsers.txt @@ -0,0 +1,26 @@ +Eclipse error parsers +===================== + +These are a godsend for extracting & quickly navigating to +warnings & error messages from console output. Unforunately +I don't know how to write an Eclipse plugin so you'll have +to add them manually. + +To add a console parser to Eclipse, go to Window --> Preferences +--> C/C++ --> Build --> Settings. Click on the 'Error Parsers' +tab and then click the 'Add...' button. See the table below for +the parser fields to add. + +Eclipse will only parse the console output during a build, so +running your unit tests must be part of your build process. +Either add this to your make/rakefile, or add it as a post- +build step in your Eclipse project settings. + + +Unity unit test error parsers +----------------------------- +Severity Pattern File Line Description +------------------------------------------------------------------------------- +Error (\.+)(.*?):(\d+):(.*?):FAIL: (.*) $2 $3 $5 +Warning (\.+)(.*?):(\d+):(.*?):IGNORE: (.*) $2 $3 $5 +Warning (\.+)(.*?):(\d+):(.*?):IGNORE\s*$ $2 $3 Ignored test diff --git a/source_code/external_code/Unity/library.json b/source_code/external_code/Unity/library.json new file mode 100644 index 0000000..839b5ab --- /dev/null +++ b/source_code/external_code/Unity/library.json @@ -0,0 +1,25 @@ +# ========================================================================= +# Unity - A Test Framework for C +# ThrowTheSwitch.org +# Copyright (c) 2007-24 Mike Karlesky, Mark VanderVoord, & Greg Williams +# SPDX-License-Identifier: MIT +# ========================================================================= + +{ + "name": "Unity", + "version": "2.6.0", + "keywords": "unit-testing, testing, tdd, testing-framework", + "description": "Simple Unit Testing for C", + "homepage": "http://www.throwtheswitch.org/unity", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/ThrowTheSwitch/Unity.git" + }, + "frameworks": "*", + "platforms": "*", + "headers": "unity.h", + "build": { + "extraScript": "platformio-build.py" + } +} diff --git a/source_code/external_code/Unity/src/meson.build b/source_code/external_code/Unity/src/meson.build new file mode 100644 index 0000000..5365227 --- /dev/null +++ b/source_code/external_code/Unity/src/meson.build @@ -0,0 +1,17 @@ +# +# build script written by : Michael Gene Brockus. +# github repo author: Mike Karlesky, Mark VanderVoord, Greg Williams. +# +# license: MIT +# + +unity_inc += include_directories('.') +unity_src += files('unity.c') + +if not meson.is_subproject() + install_headers( + 'unity.h', + 'unity_internals.h', + subdir: meson.project_name() + ) +endif diff --git a/source_code/external_code/Unity/src/unity.c b/source_code/external_code/Unity/src/unity.c new file mode 100644 index 0000000..8be0d03 --- /dev/null +++ b/source_code/external_code/Unity/src/unity.c @@ -0,0 +1,2626 @@ +/* ========================================================================= + Unity - A Test Framework for C + ThrowTheSwitch.org + Copyright (c) 2007-25 Mike Karlesky, Mark VanderVoord, & Greg Williams + SPDX-License-Identifier: MIT +========================================================================= */ + +#include "unity.h" + +#ifndef UNITY_PROGMEM +#define UNITY_PROGMEM +#endif + +/* If omitted from header, declare overrideable prototypes here so they're ready for use */ +#ifdef UNITY_OMIT_OUTPUT_CHAR_HEADER_DECLARATION +void UNITY_OUTPUT_CHAR(int); +#endif + +/* Helpful macros for us to use here in Assert functions */ +#define UNITY_FAIL_AND_BAIL do { Unity.CurrentTestFailed = 1; UNITY_OUTPUT_FLUSH(); TEST_ABORT(); } while (0) +#define UNITY_IGNORE_AND_BAIL do { Unity.CurrentTestIgnored = 1; UNITY_OUTPUT_FLUSH(); TEST_ABORT(); } while (0) +#define RETURN_IF_FAIL_OR_IGNORE do { if (Unity.CurrentTestFailed || Unity.CurrentTestIgnored) { TEST_ABORT(); } } while (0) + +struct UNITY_STORAGE_T Unity; + +#ifdef UNITY_OUTPUT_COLOR +const char UNITY_PROGMEM UnityStrOk[] = "\033[42mOK\033[0m"; +const char UNITY_PROGMEM UnityStrPass[] = "\033[42mPASS\033[0m"; +const char UNITY_PROGMEM UnityStrFail[] = "\033[41mFAIL\033[0m"; +const char UNITY_PROGMEM UnityStrIgnore[] = "\033[43mIGNORE\033[0m"; +#else +const char UNITY_PROGMEM UnityStrOk[] = "OK"; +const char UNITY_PROGMEM UnityStrPass[] = "PASS"; +const char UNITY_PROGMEM UnityStrFail[] = "FAIL"; +const char UNITY_PROGMEM UnityStrIgnore[] = "IGNORE"; +#endif +static const char UNITY_PROGMEM UnityStrNull[] = "NULL"; +static const char UNITY_PROGMEM UnityStrSpacer[] = ". "; +static const char UNITY_PROGMEM UnityStrExpected[] = " Expected "; +static const char UNITY_PROGMEM UnityStrWas[] = " Was "; +static const char UNITY_PROGMEM UnityStrGt[] = " to be greater than "; +static const char UNITY_PROGMEM UnityStrLt[] = " to be less than "; +static const char UNITY_PROGMEM UnityStrOrEqual[] = "or equal to "; +static const char UNITY_PROGMEM UnityStrNotEqual[] = " to be not equal to "; +static const char UNITY_PROGMEM UnityStrElement[] = " Element "; +static const char UNITY_PROGMEM UnityStrByte[] = " Byte "; +static const char UNITY_PROGMEM UnityStrMemory[] = " Memory Mismatch."; +static const char UNITY_PROGMEM UnityStrDelta[] = " Values Not Within Delta "; +static const char UNITY_PROGMEM UnityStrPointless[] = " You Asked Me To Compare Nothing, Which Was Pointless."; +static const char UNITY_PROGMEM UnityStrNullPointerForExpected[] = " Expected pointer to be NULL"; +static const char UNITY_PROGMEM UnityStrNullPointerForActual[] = " Actual pointer was NULL"; +#ifndef UNITY_EXCLUDE_FLOAT +static const char UNITY_PROGMEM UnityStrNot[] = "Not "; +static const char UNITY_PROGMEM UnityStrInf[] = "Infinity"; +static const char UNITY_PROGMEM UnityStrNegInf[] = "Negative Infinity"; +static const char UNITY_PROGMEM UnityStrNaN[] = "NaN"; +static const char UNITY_PROGMEM UnityStrDet[] = "Determinate"; +static const char UNITY_PROGMEM UnityStrInvalidFloatTrait[] = "Invalid Float Trait"; +#endif +const char UNITY_PROGMEM UnityStrErrShorthand[] = "Unity Shorthand Support Disabled"; +const char UNITY_PROGMEM UnityStrErrFloat[] = "Unity Floating Point Disabled"; +const char UNITY_PROGMEM UnityStrErrDouble[] = "Unity Double Precision Disabled"; +const char UNITY_PROGMEM UnityStrErr64[] = "Unity 64-bit Support Disabled"; +const char UNITY_PROGMEM UnityStrErrDetailStack[] = "Unity Detail Stack Support Disabled"; +static const char UNITY_PROGMEM UnityStrBreaker[] = "-----------------------"; +static const char UNITY_PROGMEM UnityStrResultsTests[] = " Tests "; +static const char UNITY_PROGMEM UnityStrResultsFailures[] = " Failures "; +static const char UNITY_PROGMEM UnityStrResultsIgnored[] = " Ignored "; +#ifndef UNITY_EXCLUDE_DETAILS +#ifdef UNITY_DETAIL_STACK_SIZE +static const char* UNITY_PROGMEM UnityStrDetailLabels[] = UNITY_DETAIL_LABEL_NAMES; +static const UNITY_COUNTER_TYPE UNITY_PROGMEM UnityStrDetailLabelsCount = sizeof(UnityStrDetailLabels) / sizeof(const char*); +static const char UNITY_PROGMEM UnityStrErrDetailStackEmpty[] = " Detail Stack Empty"; +static const char UNITY_PROGMEM UnityStrErrDetailStackFull[] = " Detail Stack Full"; +static const char UNITY_PROGMEM UnityStrErrDetailStackLabel[] = " Detail Label Outside Of UNITY_DETAIL_LABEL_NAMES: "; +static const char UNITY_PROGMEM UnityStrErrDetailStackPop[] = " Detail Pop With Unexpected Arguments"; +#else +static const char UNITY_PROGMEM UnityStrDetail1Name[] = UNITY_DETAIL1_NAME " "; +static const char UNITY_PROGMEM UnityStrDetail2Name[] = " " UNITY_DETAIL2_NAME " "; +#endif +#endif +/*----------------------------------------------- + * Pretty Printers & Test Result Output Handlers + *-----------------------------------------------*/ + +/*-----------------------------------------------*/ +/* Local helper function to print characters. */ +static void UnityPrintChar(const char* pch) +{ + /* printable characters plus CR & LF are printed */ + if ((*pch <= 126) && (*pch >= 32)) + { + UNITY_OUTPUT_CHAR(*pch); + } + /* write escaped carriage returns */ + else if (*pch == 13) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('r'); + } + /* write escaped line feeds */ + else if (*pch == 10) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('n'); + } + /* unprintable characters are shown as codes */ + else + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)*pch, 2); + } +} + +/*-----------------------------------------------*/ +/* Local helper function to print ANSI escape strings e.g. "\033[42m". */ +#ifdef UNITY_OUTPUT_COLOR +static UNITY_UINT UnityPrintAnsiEscapeString(const char* string) +{ + const char* pch = string; + UNITY_UINT count = 0; + + while (*pch && (*pch != 'm')) + { + UNITY_OUTPUT_CHAR(*pch); + pch++; + count++; + } + UNITY_OUTPUT_CHAR('m'); + count++; + + return count; +} +#endif + +/*-----------------------------------------------*/ +void UnityPrint(const char* string) +{ + const char* pch = string; + + if (pch != NULL) + { + while (*pch) + { +#ifdef UNITY_OUTPUT_COLOR + /* print ANSI escape code */ + if ((*pch == 27) && (*(pch + 1) == '[')) + { + pch += UnityPrintAnsiEscapeString(pch); + continue; + } +#endif + UnityPrintChar(pch); + pch++; + } + } +} +/*-----------------------------------------------*/ +void UnityPrintLen(const char* string, const UNITY_UINT32 length) +{ + const char* pch = string; + + if (pch != NULL) + { + while (*pch && ((UNITY_UINT32)(pch - string) < length)) + { + /* printable characters plus CR & LF are printed */ + if ((*pch <= 126) && (*pch >= 32)) + { + UNITY_OUTPUT_CHAR(*pch); + } + /* write escaped carriage returns */ + else if (*pch == 13) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('r'); + } + /* write escaped line feeds */ + else if (*pch == 10) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('n'); + } + /* unprintable characters are shown as codes */ + else + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)*pch, 2); + } + pch++; + } + } +} + +/*-----------------------------------------------*/ +void UnityPrintIntNumberByStyle(const UNITY_INT number, const UNITY_DISPLAY_STYLE_T style) +{ + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + if (style == UNITY_DISPLAY_STYLE_CHAR) + { + /* printable characters plus CR & LF are printed */ + UNITY_OUTPUT_CHAR('\''); + if ((number <= 126) && (number >= 32)) + { + UNITY_OUTPUT_CHAR((int)number); + } + /* write escaped carriage returns */ + else if (number == 13) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('r'); + } + /* write escaped line feeds */ + else if (number == 10) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('n'); + } + /* unprintable characters are shown as codes */ + else + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)number, 2); + } + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrintNumber(number); + } + } +} + +void UnityPrintUintNumberByStyle(const UNITY_UINT number, const UNITY_DISPLAY_STYLE_T style) +{ + if ((style & UNITY_DISPLAY_RANGE_UINT) == UNITY_DISPLAY_RANGE_UINT) + { + UnityPrintNumberUnsigned(number); + } + else + { + UNITY_OUTPUT_CHAR('0'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)number, (char)((style & 0xF) * 2)); + } +} + +/*-----------------------------------------------*/ +void UnityPrintNumber(const UNITY_INT number_to_print) +{ + UNITY_UINT number = (UNITY_UINT)number_to_print; + + if (number_to_print < 0) + { + /* A negative number, including MIN negative */ + UNITY_OUTPUT_CHAR('-'); + number = (~number) + 1; + } + UnityPrintNumberUnsigned(number); +} + +/*----------------------------------------------- + * basically do an itoa using as little ram as possible */ +void UnityPrintNumberUnsigned(const UNITY_UINT number) +{ + UNITY_UINT divisor = 1; + + /* figure out initial divisor */ + while (number / divisor > 9) + { + divisor *= 10; + } + + /* now mod and print, then divide divisor */ + do + { + UNITY_OUTPUT_CHAR((char)('0' + (number / divisor % 10))); + divisor /= 10; + } while (divisor > 0); +} + +/*-----------------------------------------------*/ +void UnityPrintNumberHex(const UNITY_UINT number, const char nibbles_to_print) +{ + int nibble; + char nibbles = nibbles_to_print; + + if ((unsigned)nibbles > UNITY_MAX_NIBBLES) + { + nibbles = UNITY_MAX_NIBBLES; + } + + while (nibbles > 0) + { + nibbles--; + nibble = (int)(number >> (nibbles * 4)) & 0x0F; + if (nibble <= 9) + { + UNITY_OUTPUT_CHAR((char)('0' + nibble)); + } + else + { + UNITY_OUTPUT_CHAR((char)('A' - 10 + nibble)); + } + } +} + +/*-----------------------------------------------*/ +void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number) +{ + UNITY_UINT current_bit = (UNITY_UINT)1 << (UNITY_INT_WIDTH - 1); + UNITY_INT32 i; + + for (i = 0; i < UNITY_INT_WIDTH; i++) + { + if (current_bit & mask) + { + if (current_bit & number) + { + UNITY_OUTPUT_CHAR('1'); + } + else + { + UNITY_OUTPUT_CHAR('0'); + } + } + else + { + UNITY_OUTPUT_CHAR('X'); + } + current_bit = current_bit >> 1; + } +} + +/*-----------------------------------------------*/ +#ifndef UNITY_EXCLUDE_FLOAT_PRINT +/* + * This function prints a floating-point value in a format similar to + * printf("%.7g") on a single-precision machine or printf("%.9g") on a + * double-precision machine. The 7th digit won't always be totally correct + * in single-precision operation (for that level of accuracy, a more + * complicated algorithm would be needed). + */ +void UnityPrintFloat(const UNITY_DOUBLE input_number) +{ +#ifdef UNITY_INCLUDE_DOUBLE + static const int sig_digits = 9; + static const UNITY_INT32 min_scaled = 100000000; + static const UNITY_INT32 max_scaled = 1000000000; +#else + static const int sig_digits = 7; + static const UNITY_INT32 min_scaled = 1000000; + static const UNITY_INT32 max_scaled = 10000000; +#endif + + UNITY_DOUBLE number = input_number; + + /* print minus sign (does not handle negative zero) */ + if (number < 0.0f) + { + UNITY_OUTPUT_CHAR('-'); + number = -number; + } + + /* handle zero, NaN, and +/- infinity */ + if (number == 0.0f) + { + UnityPrint("0"); + } + else if (UNITY_IS_NAN(number)) + { + UnityPrint("nan"); + } + else if (UNITY_IS_INF(number)) + { + UnityPrint("inf"); + } + else + { + UNITY_INT32 n_int = 0; + UNITY_INT32 n; + int exponent = 0; + int decimals; + int digits; + char buf[16] = {0}; + + /* + * Scale up or down by powers of 10. To minimize rounding error, + * start with a factor/divisor of 10^10, which is the largest + * power of 10 that can be represented exactly. Finally, compute + * (exactly) the remaining power of 10 and perform one more + * multiplication or division. + */ + if (number < 1.0f) + { + UNITY_DOUBLE factor = 1.0f; + + while (number < (UNITY_DOUBLE)max_scaled / 1e10f) { number *= 1e10f; exponent -= 10; } + while (number * factor < (UNITY_DOUBLE)min_scaled) { factor *= 10.0f; exponent--; } + + number *= factor; + } + else if (number > (UNITY_DOUBLE)max_scaled) + { + UNITY_DOUBLE divisor = 1.0f; + + while (number > (UNITY_DOUBLE)min_scaled * 1e10f) { number /= 1e10f; exponent += 10; } + while (number / divisor > (UNITY_DOUBLE)max_scaled) { divisor *= 10.0f; exponent++; } + + number /= divisor; + } + else + { + /* + * In this range, we can split off the integer part before + * doing any multiplications. This reduces rounding error by + * freeing up significant bits in the fractional part. + */ + UNITY_DOUBLE factor = 1.0f; + n_int = (UNITY_INT32)number; + number -= (UNITY_DOUBLE)n_int; + + while (n_int < min_scaled) { n_int *= 10; factor *= 10.0f; exponent--; } + + number *= factor; + } + + /* round to nearest integer */ + n = ((UNITY_INT32)(number + number) + 1) / 2; + +#ifndef UNITY_ROUND_TIES_AWAY_FROM_ZERO + /* round to even if exactly between two integers */ + if ((n & 1) && (((UNITY_DOUBLE)n - number) == 0.5f)) + n--; +#endif + + n += n_int; + + if (n >= max_scaled) + { + n = min_scaled; + exponent++; + } + + /* determine where to place decimal point */ + decimals = ((exponent <= 0) && (exponent >= -(sig_digits + 3))) ? (-exponent) : (sig_digits - 1); + exponent += decimals; + + /* truncate trailing zeroes after decimal point */ + while ((decimals > 0) && ((n % 10) == 0)) + { + n /= 10; + decimals--; + } + + /* build up buffer in reverse order */ + digits = 0; + while ((n != 0) || (digits <= decimals)) + { + buf[digits++] = (char)('0' + n % 10); + n /= 10; + } + + /* print out buffer (backwards) */ + while (digits > 0) + { + if (digits == decimals) + { + UNITY_OUTPUT_CHAR('.'); + } + UNITY_OUTPUT_CHAR(buf[--digits]); + } + + /* print exponent if needed */ + if (exponent != 0) + { + UNITY_OUTPUT_CHAR('e'); + + if (exponent < 0) + { + UNITY_OUTPUT_CHAR('-'); + exponent = -exponent; + } + else + { + UNITY_OUTPUT_CHAR('+'); + } + + digits = 0; + while ((exponent != 0) || (digits < 2)) + { + buf[digits++] = (char)('0' + exponent % 10); + exponent /= 10; + } + while (digits > 0) + { + UNITY_OUTPUT_CHAR(buf[--digits]); + } + } + } +} +#endif /* ! UNITY_EXCLUDE_FLOAT_PRINT */ + +/*-----------------------------------------------*/ +static void UnityTestResultsBegin(const char* file, const UNITY_LINE_TYPE line) +{ +#ifdef UNITY_OUTPUT_FOR_ECLIPSE + UNITY_OUTPUT_CHAR('('); + UnityPrint(file); + UNITY_OUTPUT_CHAR(':'); + UnityPrintNumber((UNITY_INT)line); + UNITY_OUTPUT_CHAR(')'); + UNITY_OUTPUT_CHAR(' '); + UnityPrint(Unity.CurrentTestName); + UNITY_OUTPUT_CHAR(':'); +#else +#ifdef UNITY_OUTPUT_FOR_IAR_WORKBENCH + UnityPrint("'); + UnityPrint(Unity.CurrentTestName); + UnityPrint(" "); +#else +#ifdef UNITY_OUTPUT_FOR_QT_CREATOR + UnityPrint("file://"); + UnityPrint(file); + UNITY_OUTPUT_CHAR(':'); + UnityPrintNumber((UNITY_INT)line); + UNITY_OUTPUT_CHAR(' '); + UnityPrint(Unity.CurrentTestName); + UNITY_OUTPUT_CHAR(':'); +#else + UnityPrint(file); + UNITY_OUTPUT_CHAR(':'); + UnityPrintNumber((UNITY_INT)line); + UNITY_OUTPUT_CHAR(':'); + UnityPrint(Unity.CurrentTestName); + UNITY_OUTPUT_CHAR(':'); +#endif +#endif +#endif +} + +/*-----------------------------------------------*/ +static void UnityTestResultsFailBegin(const UNITY_LINE_TYPE line) +{ + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint(UnityStrFail); + UNITY_OUTPUT_CHAR(':'); +} + +/*-----------------------------------------------*/ +void UnityConcludeTest(void) +{ + if (Unity.CurrentTestIgnored) + { + Unity.TestIgnores++; + } + else if (!Unity.CurrentTestFailed) + { + UnityTestResultsBegin(Unity.TestFile, Unity.CurrentTestLineNumber); + UnityPrint(UnityStrPass); + } + else + { + Unity.TestFailures++; + } + + Unity.CurrentTestFailed = 0; + Unity.CurrentTestIgnored = 0; + UNITY_PRINT_EXEC_TIME(); + UNITY_PRINT_EOL(); + UNITY_FLUSH_CALL(); +} + +/*-----------------------------------------------*/ +static void UnityAddMsgIfSpecified(const char* msg) +{ +#ifdef UNITY_PRINT_TEST_CONTEXT + UnityPrint(UnityStrSpacer); + UNITY_PRINT_TEST_CONTEXT(); +#endif +#ifndef UNITY_EXCLUDE_DETAILS +#ifdef UNITY_DETAIL_STACK_SIZE + { + UNITY_COUNTER_TYPE c; + for (c = 0; (c < Unity.CurrentDetailStackSize) && (c < UNITY_DETAIL_STACK_SIZE); c++) { + const char* label; + if ((Unity.CurrentDetailStackLabels[c] == UNITY_DETAIL_NONE) || (Unity.CurrentDetailStackLabels[c] > UnityStrDetailLabelsCount)) { + break; + } + label = UnityStrDetailLabels[Unity.CurrentDetailStackLabels[c]]; + UnityPrint(UnityStrSpacer); + if ((label[0] == '#') && (label[1] != 0)) { + UnityPrint(label + 2); + UNITY_OUTPUT_CHAR(' '); + if ((label[1] & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) { + UnityPrintIntNumberByStyle((UNITY_INT)Unity.CurrentDetailStackValues[c], label[1]); + } else { + UnityPrintUintNumberByStyle((UNITY_UINT)Unity.CurrentDetailStackValues[c], label[1]); + } + } else if (Unity.CurrentDetailStackValues[c] != 0){ + UnityPrint(label); + UNITY_OUTPUT_CHAR(' '); + UnityPrint((const char*)Unity.CurrentDetailStackValues[c]); + } + } + } +#else + if (Unity.CurrentDetail1) + { + UnityPrint(UnityStrSpacer); + UnityPrint(UnityStrDetail1Name); + UnityPrint(Unity.CurrentDetail1); + if (Unity.CurrentDetail2) + { + UnityPrint(UnityStrDetail2Name); + UnityPrint(Unity.CurrentDetail2); + } + } +#endif +#endif + if (msg) + { + UnityPrint(UnityStrSpacer); + UnityPrint(msg); + } +} + +/*-----------------------------------------------*/ +static void UnityPrintExpectedAndActualStrings(const char* expected, const char* actual) +{ + UnityPrint(UnityStrExpected); + if (expected != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrint(expected); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } + UnityPrint(UnityStrWas); + if (actual != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrint(actual); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } +} + +/*-----------------------------------------------*/ +static void UnityPrintExpectedAndActualStringsLen(const char* expected, + const char* actual, + const UNITY_UINT32 length) +{ + UnityPrint(UnityStrExpected); + if (expected != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrintLen(expected, length); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } + UnityPrint(UnityStrWas); + if (actual != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrintLen(actual, length); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } +} + +/*----------------------------------------------- + * Assertion & Control Helpers + *-----------------------------------------------*/ + +/*-----------------------------------------------*/ +static int UnityIsOneArrayNull(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_LINE_TYPE lineNumber, + const char* msg) +{ + /* Both are NULL or same pointer */ + if (expected == actual) { return 0; } + + /* print and return true if just expected is NULL */ + if (expected == NULL) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrNullPointerForExpected); + UnityAddMsgIfSpecified(msg); + return 1; + } + + /* print and return true if just actual is NULL */ + if (actual == NULL) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrNullPointerForActual); + UnityAddMsgIfSpecified(msg); + return 1; + } + + return 0; /* return false if neither is NULL */ +} + +/*----------------------------------------------- + * Assertion Functions + *-----------------------------------------------*/ + +/*-----------------------------------------------*/ +void UnityAssertBits(const UNITY_INT mask, + const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if ((mask & expected) != (mask & actual)) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintMask((UNITY_UINT)mask, (UNITY_UINT)expected); + UnityPrint(UnityStrWas); + UnityPrintMask((UNITY_UINT)mask, (UNITY_UINT)actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualIntNumber(const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if (expected != actual) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintIntNumberByStyle(expected, style); + UnityPrint(UnityStrWas); + UnityPrintIntNumberByStyle(actual, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +void UnityAssertEqualUintNumber(const UNITY_UINT expected, + const UNITY_UINT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if (expected != actual) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintUintNumberByStyle(expected, style); + UnityPrint(UnityStrWas); + UnityPrintUintNumberByStyle(actual, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} +/*-----------------------------------------------*/ +void UnityAssertIntGreaterOrLessOrEqualNumber(const UNITY_INT threshold, + const UNITY_INT actual, + const UNITY_COMPARISON_T compare, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style) +{ + int failed = 0; + RETURN_IF_FAIL_OR_IGNORE; + + if ((threshold == actual) && !(compare & UNITY_EQUAL_TO)) { failed = 1; } + + if ((actual > threshold) && (compare & UNITY_SMALLER_THAN)) { failed = 1; } + if ((actual < threshold) && (compare & UNITY_GREATER_THAN)) { failed = 1; } + + if (failed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintIntNumberByStyle(actual, style); + if (compare & UNITY_GREATER_THAN) { UnityPrint(UnityStrGt); } + if (compare & UNITY_SMALLER_THAN) { UnityPrint(UnityStrLt); } + if (compare & UNITY_EQUAL_TO) { UnityPrint(UnityStrOrEqual); } + if (compare == UNITY_NOT_EQUAL) { UnityPrint(UnityStrNotEqual); } + UnityPrintIntNumberByStyle(threshold, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +void UnityAssertUintGreaterOrLessOrEqualNumber(const UNITY_UINT threshold, + const UNITY_UINT actual, + const UNITY_COMPARISON_T compare, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style) +{ + int failed = 0; + RETURN_IF_FAIL_OR_IGNORE; + + if ((threshold == actual) && !(compare & UNITY_EQUAL_TO)) { failed = 1; } + + /* UINT or HEX */ + if ((actual > threshold) && (compare & UNITY_SMALLER_THAN)) { failed = 1; } + if ((actual < threshold) && (compare & UNITY_GREATER_THAN)) { failed = 1; } + + if (failed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintUintNumberByStyle(actual, style); + if (compare & UNITY_GREATER_THAN) { UnityPrint(UnityStrGt); } + if (compare & UNITY_SMALLER_THAN) { UnityPrint(UnityStrLt); } + if (compare & UNITY_EQUAL_TO) { UnityPrint(UnityStrOrEqual); } + if (compare == UNITY_NOT_EQUAL) { UnityPrint(UnityStrNotEqual); } + UnityPrintUintNumberByStyle(threshold, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +#define UnityPrintPointlessAndBail() \ +do { \ + UnityTestResultsFailBegin(lineNumber); \ + UnityPrint(UnityStrPointless); \ + UnityAddMsgIfSpecified(msg); \ + UNITY_FAIL_AND_BAIL; \ +} while (0) + +/*-----------------------------------------------*/ +void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 elements = num_elements; + unsigned int length = style & 0xF; + unsigned int increment = 0; + + RETURN_IF_FAIL_OR_IGNORE; + + if (num_elements == 0) + { +#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY + UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg); +#else + UnityPrintPointlessAndBail(); +#endif + } + + if (expected == actual) + { + return; /* Both are NULL or same pointer */ + } + + if (UnityIsOneArrayNull(expected, actual, lineNumber, msg)) + { + UNITY_FAIL_AND_BAIL; + } + + while ((elements > 0) && (elements--)) + { + UNITY_INT expect_val; + UNITY_INT actual_val; + + switch (length) + { + case 1: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)actual; + if (style & (UNITY_DISPLAY_RANGE_UINT | UNITY_DISPLAY_RANGE_HEX)) + { + expect_val &= 0x000000FF; + actual_val &= 0x000000FF; + } + increment = sizeof(UNITY_INT8); + break; + + case 2: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)actual; + if (style & (UNITY_DISPLAY_RANGE_UINT | UNITY_DISPLAY_RANGE_HEX)) + { + expect_val &= 0x0000FFFF; + actual_val &= 0x0000FFFF; + } + increment = sizeof(UNITY_INT16); + break; + +#ifdef UNITY_SUPPORT_64 + case 8: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)actual; + increment = sizeof(UNITY_INT64); + break; +#endif + + default: /* default is length 4 bytes */ + case 4: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)actual; +#ifdef UNITY_SUPPORT_64 + if (style & (UNITY_DISPLAY_RANGE_UINT | UNITY_DISPLAY_RANGE_HEX)) + { + expect_val &= 0x00000000FFFFFFFF; + actual_val &= 0x00000000FFFFFFFF; + } +#endif + increment = sizeof(UNITY_INT32); + length = 4; + break; + } + + if (expect_val != actual_val) + { + if ((style & UNITY_DISPLAY_RANGE_UINT) && (length < (UNITY_INT_WIDTH / 8))) + { /* For UINT, remove sign extension (padding 1's) from signed type casts above */ + UNITY_INT mask = 1; + mask = (mask << 8 * length) - 1; + expect_val &= mask; + actual_val &= mask; + } + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + UnityPrint(UnityStrExpected); + UnityPrintIntNumberByStyle(expect_val, style); + UnityPrint(UnityStrWas); + UnityPrintIntNumberByStyle(actual_val, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + /* Walk through array by incrementing the pointers */ + if (flags == UNITY_ARRAY_TO_ARRAY) + { + expected = (UNITY_INTERNAL_PTR)((const char*)expected + increment); + } + actual = (UNITY_INTERNAL_PTR)((const char*)actual + increment); + } +} + +/*-----------------------------------------------*/ +#ifndef UNITY_EXCLUDE_FLOAT +/* Wrap this define in a function with variable types as float or double */ +#define UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff) \ + if (UNITY_IS_INF(expected) && UNITY_IS_INF(actual) && (((expected) < 0) == ((actual) < 0))) return 1; \ + if (UNITY_NAN_CHECK) return 1; \ + (diff) = (actual) - (expected); \ + if ((diff) < 0) (diff) = -(diff); \ + if ((delta) < 0) (delta) = -(delta); \ + return !(UNITY_IS_NAN(diff) || UNITY_IS_INF(diff) || ((diff) > (delta))) + /* This first part of this condition will catch any NaN or Infinite values */ +#ifndef UNITY_NAN_NOT_EQUAL_NAN + #define UNITY_NAN_CHECK UNITY_IS_NAN(expected) && UNITY_IS_NAN(actual) +#else + #define UNITY_NAN_CHECK 0 +#endif + +#ifndef UNITY_EXCLUDE_FLOAT_PRINT + #define UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual) \ + do { \ + UnityPrint(UnityStrExpected); \ + UnityPrintFloat(expected); \ + UnityPrint(UnityStrWas); \ + UnityPrintFloat(actual); \ + } while (0) +#else + #define UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual) \ + UnityPrint(UnityStrDelta) +#endif /* UNITY_EXCLUDE_FLOAT_PRINT */ + +/*-----------------------------------------------*/ +static int UnityFloatsWithin(UNITY_FLOAT delta, UNITY_FLOAT expected, UNITY_FLOAT actual) +{ + UNITY_FLOAT diff; + UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff); +} + +/*-----------------------------------------------*/ +void UnityAssertWithinFloatArray(const UNITY_FLOAT delta, + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* expected, + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 elements = num_elements; + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* ptr_expected = expected; + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* ptr_actual = actual; + UNITY_FLOAT in_delta = delta; + UNITY_FLOAT current_element_delta = delta; + + RETURN_IF_FAIL_OR_IGNORE; + + if (elements == 0) + { +#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY + UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg); +#else + UnityPrintPointlessAndBail(); +#endif + } + + if (UNITY_IS_INF(in_delta)) + { + return; /* Arrays will be force equal with infinite delta */ + } + + if (UNITY_IS_NAN(in_delta)) + { + /* Delta must be correct number */ + UnityPrintPointlessAndBail(); + } + + if (expected == actual) + { + return; /* Both are NULL or same pointer */ + } + + if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) + { + UNITY_FAIL_AND_BAIL; + } + + /* fix delta sign if need */ + if (in_delta < 0) + { + in_delta = -in_delta; + } + + while (elements--) + { + current_element_delta = *ptr_expected * UNITY_FLOAT_PRECISION; + + if (current_element_delta < 0) + { + /* fix delta sign for correct calculations */ + current_element_delta = -current_element_delta; + } + + if (!UnityFloatsWithin(in_delta + current_element_delta, *ptr_expected, *ptr_actual)) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)*ptr_expected, (UNITY_DOUBLE)*ptr_actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + if (flags == UNITY_ARRAY_TO_ARRAY) + { + ptr_expected++; + } + ptr_actual++; + } +} + +/*-----------------------------------------------*/ +void UnityAssertFloatsWithin(const UNITY_FLOAT delta, + const UNITY_FLOAT expected, + const UNITY_FLOAT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + RETURN_IF_FAIL_OR_IGNORE; + + + if (!UnityFloatsWithin(delta, expected, actual)) + { + UnityTestResultsFailBegin(lineNumber); + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)expected, (UNITY_DOUBLE)actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +#ifndef UNITY_EXCLUDE_FLOAT_PRINT +/*-----------------------------------------------*/ +void UnityAssertFloatsNotWithin(const UNITY_FLOAT delta, + const UNITY_FLOAT expected, + const UNITY_FLOAT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if (UnityFloatsWithin(delta, expected, actual)) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintFloat((UNITY_DOUBLE)expected); + UnityPrint(UnityStrNotEqual); + UnityPrintFloat((UNITY_DOUBLE)actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertGreaterOrLessFloat(const UNITY_FLOAT threshold, + const UNITY_FLOAT actual, + const UNITY_COMPARISON_T compare, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + int failed; + + RETURN_IF_FAIL_OR_IGNORE; + + failed = 0; + + /* Checking for "not success" rather than failure to get the right result for NaN */ + if (!(actual < threshold) && (compare & UNITY_SMALLER_THAN)) { failed = 1; } + if (!(actual > threshold) && (compare & UNITY_GREATER_THAN)) { failed = 1; } + + if ((compare & UNITY_EQUAL_TO) && UnityFloatsWithin(threshold * UNITY_FLOAT_PRECISION, threshold, actual)) { failed = 0; } + + if (failed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintFloat(actual); + if (compare & UNITY_GREATER_THAN) { UnityPrint(UnityStrGt); } + if (compare & UNITY_SMALLER_THAN) { UnityPrint(UnityStrLt); } + if (compare & UNITY_EQUAL_TO) { UnityPrint(UnityStrOrEqual); } + UnityPrintFloat(threshold); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} +#endif /* ! UNITY_EXCLUDE_FLOAT_PRINT */ + +/*-----------------------------------------------*/ +void UnityAssertFloatSpecial(const UNITY_FLOAT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style) +{ + const char* trait_names[] = {UnityStrInf, UnityStrNegInf, UnityStrNaN, UnityStrDet}; + UNITY_INT should_be_trait = ((UNITY_INT)style & 1); + UNITY_INT is_trait = !should_be_trait; + UNITY_INT trait_index = (UNITY_INT)(style >> 1); + + RETURN_IF_FAIL_OR_IGNORE; + + switch (style) + { + case UNITY_FLOAT_IS_INF: + case UNITY_FLOAT_IS_NOT_INF: + is_trait = UNITY_IS_INF(actual) && (actual > 0); + break; + case UNITY_FLOAT_IS_NEG_INF: + case UNITY_FLOAT_IS_NOT_NEG_INF: + is_trait = UNITY_IS_INF(actual) && (actual < 0); + break; + + case UNITY_FLOAT_IS_NAN: + case UNITY_FLOAT_IS_NOT_NAN: + is_trait = UNITY_IS_NAN(actual) ? 1 : 0; + break; + + case UNITY_FLOAT_IS_DET: /* A determinate number is non infinite and not NaN. */ + case UNITY_FLOAT_IS_NOT_DET: + is_trait = !UNITY_IS_INF(actual) && !UNITY_IS_NAN(actual); + break; + + case UNITY_FLOAT_INVALID_TRAIT: /* Supress warning */ + default: /* including UNITY_FLOAT_INVALID_TRAIT */ + trait_index = 0; + trait_names[0] = UnityStrInvalidFloatTrait; + break; + } + + if (is_trait != should_be_trait) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + if (!should_be_trait) + { + UnityPrint(UnityStrNot); + } + UnityPrint(trait_names[trait_index]); + UnityPrint(UnityStrWas); +#ifndef UNITY_EXCLUDE_FLOAT_PRINT + UnityPrintFloat((UNITY_DOUBLE)actual); +#else + if (should_be_trait) + { + UnityPrint(UnityStrNot); + } + UnityPrint(trait_names[trait_index]); +#endif + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +#endif /* not UNITY_EXCLUDE_FLOAT */ + +/*-----------------------------------------------*/ +#ifndef UNITY_EXCLUDE_DOUBLE +static int UnityDoublesWithin(UNITY_DOUBLE delta, UNITY_DOUBLE expected, UNITY_DOUBLE actual) +{ + UNITY_DOUBLE diff; + UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff); +} + +/*-----------------------------------------------*/ +void UnityAssertWithinDoubleArray(const UNITY_DOUBLE delta, + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* expected, + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 elements = num_elements; + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* ptr_expected = expected; + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* ptr_actual = actual; + UNITY_DOUBLE in_delta = delta; + UNITY_DOUBLE current_element_delta = delta; + + RETURN_IF_FAIL_OR_IGNORE; + + if (elements == 0) + { +#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY + UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg); +#else + UnityPrintPointlessAndBail(); +#endif + } + + if (UNITY_IS_INF(in_delta)) + { + return; /* Arrays will be force equal with infinite delta */ + } + + if (UNITY_IS_NAN(in_delta)) + { + /* Delta must be correct number */ + UnityPrintPointlessAndBail(); + } + + if (expected == actual) + { + return; /* Both are NULL or same pointer */ + } + + if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) + { + UNITY_FAIL_AND_BAIL; + } + + /* fix delta sign if need */ + if (in_delta < 0) + { + in_delta = -in_delta; + } + + while (elements--) + { + current_element_delta = *ptr_expected * UNITY_DOUBLE_PRECISION; + + if (current_element_delta < 0) + { + /* fix delta sign for correct calculations */ + current_element_delta = -current_element_delta; + } + + if (!UnityDoublesWithin(in_delta + current_element_delta, *ptr_expected, *ptr_actual)) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(*ptr_expected, *ptr_actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + if (flags == UNITY_ARRAY_TO_ARRAY) + { + ptr_expected++; + } + ptr_actual++; + } +} + +/*-----------------------------------------------*/ +void UnityAssertDoublesWithin(const UNITY_DOUBLE delta, + const UNITY_DOUBLE expected, + const UNITY_DOUBLE actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if (!UnityDoublesWithin(delta, expected, actual)) + { + UnityTestResultsFailBegin(lineNumber); + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +#ifndef UNITY_EXCLUDE_FLOAT_PRINT +/*-----------------------------------------------*/ +void UnityAssertDoublesNotWithin(const UNITY_DOUBLE delta, + const UNITY_DOUBLE expected, + const UNITY_DOUBLE actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if (UnityDoublesWithin(delta, expected, actual)) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintFloat((UNITY_DOUBLE)expected); + UnityPrint(UnityStrNotEqual); + UnityPrintFloat((UNITY_DOUBLE)actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertGreaterOrLessDouble(const UNITY_DOUBLE threshold, + const UNITY_DOUBLE actual, + const UNITY_COMPARISON_T compare, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + int failed; + + RETURN_IF_FAIL_OR_IGNORE; + + failed = 0; + + /* Checking for "not success" rather than failure to get the right result for NaN */ + if (!(actual < threshold) && (compare & UNITY_SMALLER_THAN)) { failed = 1; } + if (!(actual > threshold) && (compare & UNITY_GREATER_THAN)) { failed = 1; } + + if ((compare & UNITY_EQUAL_TO) && UnityDoublesWithin(threshold * UNITY_DOUBLE_PRECISION, threshold, actual)) { failed = 0; } + + if (failed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintFloat(actual); + if (compare & UNITY_GREATER_THAN) { UnityPrint(UnityStrGt); } + if (compare & UNITY_SMALLER_THAN) { UnityPrint(UnityStrLt); } + if (compare & UNITY_EQUAL_TO) { UnityPrint(UnityStrOrEqual); } + UnityPrintFloat(threshold); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} +#endif /* ! UNITY_EXCLUDE_FLOAT_PRINT */ + +/*-----------------------------------------------*/ +void UnityAssertDoubleSpecial(const UNITY_DOUBLE actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style) +{ + const char* trait_names[] = {UnityStrInf, UnityStrNegInf, UnityStrNaN, UnityStrDet}; + UNITY_INT should_be_trait = ((UNITY_INT)style & 1); + UNITY_INT is_trait = !should_be_trait; + UNITY_INT trait_index = (UNITY_INT)(style >> 1); + + RETURN_IF_FAIL_OR_IGNORE; + + switch (style) + { + case UNITY_FLOAT_IS_INF: + case UNITY_FLOAT_IS_NOT_INF: + is_trait = UNITY_IS_INF(actual) && (actual > 0); + break; + case UNITY_FLOAT_IS_NEG_INF: + case UNITY_FLOAT_IS_NOT_NEG_INF: + is_trait = UNITY_IS_INF(actual) && (actual < 0); + break; + + case UNITY_FLOAT_IS_NAN: + case UNITY_FLOAT_IS_NOT_NAN: + is_trait = UNITY_IS_NAN(actual) ? 1 : 0; + break; + + case UNITY_FLOAT_IS_DET: /* A determinate number is non infinite and not NaN. */ + case UNITY_FLOAT_IS_NOT_DET: + is_trait = !UNITY_IS_INF(actual) && !UNITY_IS_NAN(actual); + break; + + case UNITY_FLOAT_INVALID_TRAIT: /* Supress warning */ + default: /* including UNITY_FLOAT_INVALID_TRAIT */ + trait_index = 0; + trait_names[0] = UnityStrInvalidFloatTrait; + break; + } + + if (is_trait != should_be_trait) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + if (!should_be_trait) + { + UnityPrint(UnityStrNot); + } + UnityPrint(trait_names[trait_index]); + UnityPrint(UnityStrWas); +#ifndef UNITY_EXCLUDE_FLOAT_PRINT + UnityPrintFloat(actual); +#else + if (should_be_trait) + { + UnityPrint(UnityStrNot); + } + UnityPrint(trait_names[trait_index]); +#endif + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +#endif /* not UNITY_EXCLUDE_DOUBLE */ + +/*-----------------------------------------------*/ +void UnityAssertIntNumbersWithin(const UNITY_UINT delta, + const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if (actual > expected) + { + Unity.CurrentTestFailed = (((UNITY_UINT)actual - (UNITY_UINT)expected) > delta); + } + else + { + Unity.CurrentTestFailed = (((UNITY_UINT)expected - (UNITY_UINT)actual) > delta); + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrDelta); + UnityPrintIntNumberByStyle((UNITY_INT)delta, style); + UnityPrint(UnityStrExpected); + UnityPrintIntNumberByStyle(expected, style); + UnityPrint(UnityStrWas); + UnityPrintIntNumberByStyle(actual, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +void UnityAssertUintNumbersWithin(const UNITY_UINT delta, + const UNITY_UINT expected, + const UNITY_UINT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if (actual > expected) + { + Unity.CurrentTestFailed = ((actual - expected) > delta); + } + else + { + Unity.CurrentTestFailed = ((expected - actual) > delta); + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrDelta); + UnityPrintUintNumberByStyle(delta, style); + UnityPrint(UnityStrExpected); + UnityPrintUintNumberByStyle(expected, style); + UnityPrint(UnityStrWas); + UnityPrintUintNumberByStyle(actual, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertNumbersArrayWithin(const UNITY_UINT delta, + UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 elements = num_elements; + unsigned int length = style & 0xF; + unsigned int increment = 0; + + RETURN_IF_FAIL_OR_IGNORE; + + if (num_elements == 0) + { +#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY + UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg); +#else + UnityPrintPointlessAndBail(); +#endif + } + + if (expected == actual) + { + return; /* Both are NULL or same pointer */ + } + + if (UnityIsOneArrayNull(expected, actual, lineNumber, msg)) + { + UNITY_FAIL_AND_BAIL; + } + + while ((elements > 0) && (elements--)) + { + UNITY_INT expect_val; + UNITY_INT actual_val; + + switch (length) + { + case 1: + /* fixing problems with signed overflow on unsigned numbers */ + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)actual; + increment = sizeof(UNITY_INT8); + } + else + { + expect_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT8*)expected; + actual_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT8*)actual; + increment = sizeof(UNITY_UINT8); + } + break; + + case 2: + /* fixing problems with signed overflow on unsigned numbers */ + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)actual; + increment = sizeof(UNITY_INT16); + } + else + { + expect_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT16*)expected; + actual_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT16*)actual; + increment = sizeof(UNITY_UINT16); + } + break; + +#ifdef UNITY_SUPPORT_64 + case 8: + /* fixing problems with signed overflow on unsigned numbers */ + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)actual; + increment = sizeof(UNITY_INT64); + } + else + { + expect_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT64*)expected; + actual_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT64*)actual; + increment = sizeof(UNITY_UINT64); + } + break; +#endif + + default: /* default is length 4 bytes */ + case 4: + /* fixing problems with signed overflow on unsigned numbers */ + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)actual; + increment = sizeof(UNITY_INT32); + } + else + { + expect_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT32*)expected; + actual_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT32*)actual; + increment = sizeof(UNITY_UINT32); + } + length = 4; + break; + } + + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + if (actual_val > expect_val) + { + Unity.CurrentTestFailed = (((UNITY_UINT)actual_val - (UNITY_UINT)expect_val) > delta); + } + else + { + Unity.CurrentTestFailed = (((UNITY_UINT)expect_val - (UNITY_UINT)actual_val) > delta); + } + } + else + { + if ((UNITY_UINT)actual_val > (UNITY_UINT)expect_val) + { + Unity.CurrentTestFailed = (((UNITY_UINT)actual_val - (UNITY_UINT)expect_val) > delta); + } + else + { + Unity.CurrentTestFailed = (((UNITY_UINT)expect_val - (UNITY_UINT)actual_val) > delta); + } + } + + if (Unity.CurrentTestFailed) + { + if ((style & UNITY_DISPLAY_RANGE_UINT) && (length < (UNITY_INT_WIDTH / 8))) + { /* For UINT, remove sign extension (padding 1's) from signed type casts above */ + UNITY_INT mask = 1; + mask = (mask << 8 * length) - 1; + expect_val &= mask; + actual_val &= mask; + } + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrDelta); + UnityPrintIntNumberByStyle((UNITY_INT)delta, style); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + UnityPrint(UnityStrExpected); + UnityPrintIntNumberByStyle(expect_val, style); + UnityPrint(UnityStrWas); + UnityPrintIntNumberByStyle(actual_val, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + /* Walk through array by incrementing the pointers */ + if (flags == UNITY_ARRAY_TO_ARRAY) + { + expected = (UNITY_INTERNAL_PTR)((const char*)expected + increment); + } + actual = (UNITY_INTERNAL_PTR)((const char*)actual + increment); + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualString(const char* expected, + const char* actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + UNITY_UINT32 i; + + RETURN_IF_FAIL_OR_IGNORE; + + /* if both pointers not null compare the strings */ + if (expected && actual) + { + for (i = 0; expected[i] || actual[i]; i++) + { + if (expected[i] != actual[i]) + { + Unity.CurrentTestFailed = 1; + break; + } + } + } + else + { /* fail if either null but not if both */ + if (expected || actual) + { + Unity.CurrentTestFailed = 1; + } + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrintExpectedAndActualStrings(expected, actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualStringLen(const char* expected, + const char* actual, + const UNITY_UINT32 length, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + UNITY_UINT32 i; + + RETURN_IF_FAIL_OR_IGNORE; + + /* if both pointers not null compare the strings */ + if (expected && actual) + { + for (i = 0; (i < length) && (expected[i] || actual[i]); i++) + { + if (expected[i] != actual[i]) + { + Unity.CurrentTestFailed = 1; + break; + } + } + } + else + { /* fail if either null but not if both */ + if (expected || actual) + { + Unity.CurrentTestFailed = 1; + } + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrintExpectedAndActualStringsLen(expected, actual, length); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualStringArray(UNITY_INTERNAL_PTR expected, + const char** actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 i = 0; + UNITY_UINT32 j = 0; + const char* expd = NULL; + const char* act = NULL; + + RETURN_IF_FAIL_OR_IGNORE; + + /* if no elements, it's an error */ + if (num_elements == 0) + { +#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY + UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg); +#else + UnityPrintPointlessAndBail(); +#endif + } + + if ((const void*)expected == (const void*)actual) + { + return; /* Both are NULL or same pointer */ + } + + if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) + { + UNITY_FAIL_AND_BAIL; + } + + if (flags != UNITY_ARRAY_TO_ARRAY) + { + expd = (const char*)expected; + } + + do + { + act = actual[j]; + if (flags == UNITY_ARRAY_TO_ARRAY) + { + expd = ((const char* const*)expected)[j]; + } + + /* if both pointers not null compare the strings */ + if (expd && act) + { + for (i = 0; expd[i] || act[i]; i++) + { + if (expd[i] != act[i]) + { + Unity.CurrentTestFailed = 1; + break; + } + } + } + else + { /* handle case of one pointers being null (if both null, test should pass) */ + if (expd != act) + { + Unity.CurrentTestFailed = 1; + } + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + if (num_elements > 1) + { + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(j); + } + UnityPrintExpectedAndActualStrings(expd, act); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + } while (++j < num_elements); +} + +/*-----------------------------------------------*/ +void UnityAssertEqualMemory(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 length, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags) +{ + UNITY_PTR_ATTRIBUTE const unsigned char* ptr_exp = (UNITY_PTR_ATTRIBUTE const unsigned char*)expected; + UNITY_PTR_ATTRIBUTE const unsigned char* ptr_act = (UNITY_PTR_ATTRIBUTE const unsigned char*)actual; + UNITY_UINT32 elements = num_elements; + UNITY_UINT32 bytes; + + RETURN_IF_FAIL_OR_IGNORE; + + if (elements == 0) + { +#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY + UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg); +#else + UnityPrintPointlessAndBail(); +#endif + } + if (length == 0) + { + UnityPrintPointlessAndBail(); + } + + if (expected == actual) + { + return; /* Both are NULL or same pointer */ + } + + if (UnityIsOneArrayNull(expected, actual, lineNumber, msg)) + { + UNITY_FAIL_AND_BAIL; + } + + while (elements--) + { + bytes = length; + while (bytes--) + { + if (*ptr_exp != *ptr_act) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrMemory); + if (num_elements > 1) + { + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + } + UnityPrint(UnityStrByte); + UnityPrintNumberUnsigned(length - bytes - 1); + UnityPrint(UnityStrExpected); + UnityPrintIntNumberByStyle(*ptr_exp, UNITY_DISPLAY_STYLE_HEX8); + UnityPrint(UnityStrWas); + UnityPrintIntNumberByStyle(*ptr_act, UNITY_DISPLAY_STYLE_HEX8); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + ptr_exp++; + ptr_act++; + } + if (flags == UNITY_ARRAY_TO_VAL) + { + ptr_exp = (UNITY_PTR_ATTRIBUTE const unsigned char*)expected; + } + } +} + +/*-----------------------------------------------*/ + +static union +{ + UNITY_INT8 i8; + UNITY_INT16 i16; + UNITY_INT32 i32; +#ifdef UNITY_SUPPORT_64 + UNITY_INT64 i64; +#endif +#ifndef UNITY_EXCLUDE_FLOAT + float f; +#endif +#ifndef UNITY_EXCLUDE_DOUBLE + double d; +#endif +} UnityQuickCompare; + +UNITY_INTERNAL_PTR UnityNumToPtr(const UNITY_INT num, const UNITY_UINT8 size) +{ + switch(size) + { + case 1: + UnityQuickCompare.i8 = (UNITY_INT8)num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i8); + + case 2: + UnityQuickCompare.i16 = (UNITY_INT16)num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i16); + +#ifdef UNITY_SUPPORT_64 + case 8: + UnityQuickCompare.i64 = (UNITY_INT64)num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i64); +#endif + + default: /* 4 bytes */ + UnityQuickCompare.i32 = (UNITY_INT32)num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i32); + } +} + +#ifndef UNITY_EXCLUDE_FLOAT +/*-----------------------------------------------*/ +UNITY_INTERNAL_PTR UnityFloatToPtr(const float num) +{ + UnityQuickCompare.f = num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.f); +} +#endif + +#ifndef UNITY_EXCLUDE_DOUBLE +/*-----------------------------------------------*/ +UNITY_INTERNAL_PTR UnityDoubleToPtr(const double num) +{ + UnityQuickCompare.d = num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.d); +} +#endif + +#ifdef UNITY_INCLUDE_PRINT_FORMATTED + +/*----------------------------------------------- + * printf length modifier helpers + *-----------------------------------------------*/ + +enum UnityLengthModifier { + UNITY_LENGTH_MODIFIER_NONE, + UNITY_LENGTH_MODIFIER_LONG_LONG, + UNITY_LENGTH_MODIFIER_LONG, +}; + +#define UNITY_EXTRACT_ARG(NUMBER_T, NUMBER, LENGTH_MOD, VA, ARG_T) \ +do { \ + switch (LENGTH_MOD) \ + { \ + case UNITY_LENGTH_MODIFIER_LONG_LONG: \ + { \ + NUMBER = (NUMBER_T)va_arg(VA, long long ARG_T); \ + break; \ + } \ + case UNITY_LENGTH_MODIFIER_LONG: \ + { \ + NUMBER = (NUMBER_T)va_arg(VA, long ARG_T); \ + break; \ + } \ + case UNITY_LENGTH_MODIFIER_NONE: \ + default: \ + { \ + NUMBER = (NUMBER_T)va_arg(VA, ARG_T); \ + break; \ + } \ + } \ +} while (0) + +static enum UnityLengthModifier UnityLengthModifierGet(const char *pch, int *length) +{ + enum UnityLengthModifier length_mod; + switch (pch[0]) + { + case 'l': + { + if (pch[1] == 'l') + { + *length = 2; + length_mod = UNITY_LENGTH_MODIFIER_LONG_LONG; + } + else + { + *length = 1; + length_mod = UNITY_LENGTH_MODIFIER_LONG; + } + break; + } + case 'h': + { + /* short and char are converted to int */ + length_mod = UNITY_LENGTH_MODIFIER_NONE; + if (pch[1] == 'h') + { + *length = 2; + } + else + { + *length = 1; + } + break; + } + case 'j': + case 'z': + case 't': + case 'L': + { + /* Not supported, but should gobble up the length specifier anyway */ + length_mod = UNITY_LENGTH_MODIFIER_NONE; + *length = 1; + break; + } + default: + { + length_mod = UNITY_LENGTH_MODIFIER_NONE; + *length = 0; + } + } + return length_mod; +} + +/*----------------------------------------------- + * printf helper function + *-----------------------------------------------*/ +static void UnityPrintFVA(const char* format, va_list va) +{ + const char* pch = format; + if (pch != NULL) + { + while (*pch) + { + /* format identification character */ + if (*pch == '%') + { + pch++; + + if (pch != NULL) + { + int length_mod_size; + enum UnityLengthModifier length_mod = UnityLengthModifierGet(pch, &length_mod_size); + pch += length_mod_size; + + switch (*pch) + { + case 'd': + case 'i': + { + UNITY_INT number; + UNITY_EXTRACT_ARG(UNITY_INT, number, length_mod, va, int); + UnityPrintNumber((UNITY_INT)number); + break; + } +#ifndef UNITY_EXCLUDE_FLOAT_PRINT + case 'f': + case 'g': + { + const double number = va_arg(va, double); + UnityPrintFloat((UNITY_DOUBLE)number); + break; + } +#endif + case 'u': + { + UNITY_UINT number; + UNITY_EXTRACT_ARG(UNITY_UINT, number, length_mod, va, unsigned int); + UnityPrintNumberUnsigned(number); + break; + } + case 'b': + { + UNITY_UINT number; + UNITY_EXTRACT_ARG(UNITY_UINT, number, length_mod, va, unsigned int); + const UNITY_UINT mask = (UNITY_UINT)0 - (UNITY_UINT)1; + UNITY_OUTPUT_CHAR('0'); + UNITY_OUTPUT_CHAR('b'); + UnityPrintMask(mask, number); + break; + } + case 'x': + case 'X': + { + UNITY_UINT number; + UNITY_EXTRACT_ARG(UNITY_UINT, number, length_mod, va, unsigned int); + UNITY_OUTPUT_CHAR('0'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex(number, UNITY_MAX_NIBBLES); + break; + } + case 'p': + { + UNITY_UINT number; + char nibbles_to_print = 8; + if (UNITY_POINTER_WIDTH == 64) + { + length_mod = UNITY_LENGTH_MODIFIER_LONG_LONG; + nibbles_to_print = 16; + } + UNITY_EXTRACT_ARG(UNITY_UINT, number, length_mod, va, unsigned int); + UNITY_OUTPUT_CHAR('0'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)number, nibbles_to_print); + break; + } + case 'c': + { + const int ch = va_arg(va, int); + UnityPrintChar((const char *)&ch); + break; + } + case 's': + { + const char * string = va_arg(va, const char *); + UnityPrint(string); + break; + } + case '%': + { + UnityPrintChar(pch); + break; + } + default: + { + /* print the unknown format character */ + UNITY_OUTPUT_CHAR('%'); + UnityPrintChar(pch); + break; + } + } + } + } +#ifdef UNITY_OUTPUT_COLOR + /* print ANSI escape code */ + else if ((*pch == 27) && (*(pch + 1) == '[')) + { + pch += UnityPrintAnsiEscapeString(pch); + continue; + } +#endif + else if (*pch == '\n') + { + UNITY_PRINT_EOL(); + } + else + { + UnityPrintChar(pch); + } + + pch++; + } + } +} + +void UnityPrintF(const UNITY_LINE_TYPE line, const char* format, ...) +{ + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint("INFO"); + if(format != NULL) + { + UnityPrint(": "); + va_list va; + va_start(va, format); + UnityPrintFVA(format, va); + va_end(va); + } + UNITY_PRINT_EOL(); +} +#endif /* ! UNITY_INCLUDE_PRINT_FORMATTED */ + + +/*----------------------------------------------- + * Control Functions + *-----------------------------------------------*/ + +/*-----------------------------------------------*/ +void UnityFail(const char* msg, const UNITY_LINE_TYPE line) +{ + RETURN_IF_FAIL_OR_IGNORE; + + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint(UnityStrFail); + UnityAddMsgIfSpecified(msg); + + UNITY_FAIL_AND_BAIL; +} + +/*-----------------------------------------------*/ +void UnityIgnore(const char* msg, const UNITY_LINE_TYPE line) +{ + RETURN_IF_FAIL_OR_IGNORE; + + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint(UnityStrIgnore); + if (msg != NULL) + { + UNITY_OUTPUT_CHAR(':'); + UNITY_OUTPUT_CHAR(' '); + UnityPrint(msg); + } + UNITY_IGNORE_AND_BAIL; +} + +/*-----------------------------------------------*/ +void UnityMessage(const char* msg, const UNITY_LINE_TYPE line) +{ + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint("INFO"); + if (msg != NULL) + { + UNITY_OUTPUT_CHAR(':'); + UNITY_OUTPUT_CHAR(' '); + UnityPrint(msg); + } + UNITY_PRINT_EOL(); +} + +/*-----------------------------------------------*/ +/* If we have not defined our own test runner, then include our default test runner to make life easier */ +#ifndef UNITY_SKIP_DEFAULT_RUNNER +void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum) +{ + Unity.CurrentTestName = FuncName; + Unity.CurrentTestLineNumber = (UNITY_LINE_TYPE)FuncLineNum; + Unity.NumberOfTests++; + #ifndef UNITY_EXCLUDE_DETAILS + #ifdef UNITY_DETAIL_STACK_SIZE + Unity.CurrentDetailStackSize = 0; + #else + UNITY_CLR_DETAILS(); + #endif + #endif + UNITY_EXEC_TIME_START(); + if (TEST_PROTECT()) + { + setUp(); + Func(); + } + if (TEST_PROTECT()) + { + tearDown(); + } + UNITY_EXEC_TIME_STOP(); + UnityConcludeTest(); +} +#endif + +/*-----------------------------------------------*/ +void UnitySetTestFile(const char* filename) +{ + Unity.TestFile = filename; +} + +/*-----------------------------------------------*/ +void UnityBegin(const char* filename) +{ + Unity.TestFile = filename; + Unity.CurrentTestName = NULL; + Unity.CurrentTestLineNumber = 0; + Unity.NumberOfTests = 0; + Unity.TestFailures = 0; + Unity.TestIgnores = 0; + Unity.CurrentTestFailed = 0; + Unity.CurrentTestIgnored = 0; + + UNITY_CLR_DETAILS(); + UNITY_OUTPUT_START(); +} + +/*-----------------------------------------------*/ +int UnityEnd(void) +{ + UNITY_PRINT_EOL(); + UnityPrint(UnityStrBreaker); + UNITY_PRINT_EOL(); + UnityPrintNumber((UNITY_INT)(Unity.NumberOfTests)); + UnityPrint(UnityStrResultsTests); + UnityPrintNumber((UNITY_INT)(Unity.TestFailures)); + UnityPrint(UnityStrResultsFailures); + UnityPrintNumber((UNITY_INT)(Unity.TestIgnores)); + UnityPrint(UnityStrResultsIgnored); + UNITY_PRINT_EOL(); + if (Unity.TestFailures == 0U) + { + UnityPrint(UnityStrOk); + } + else + { + UnityPrint(UnityStrFail); +#ifdef UNITY_DIFFERENTIATE_FINAL_FAIL + UNITY_OUTPUT_CHAR('E'); UNITY_OUTPUT_CHAR('D'); +#endif + } + UNITY_PRINT_EOL(); + UNITY_FLUSH_CALL(); + UNITY_OUTPUT_COMPLETE(); + return (int)(Unity.TestFailures); +} + +/*----------------------------------------------- + * Details Stack + *-----------------------------------------------*/ +#ifndef UNITY_EXCLUDE_DETAILS +#ifdef UNITY_DETAIL_STACK_SIZE +void UnityPushDetail(UNITY_DETAIL_LABEL_TYPE label, UNITY_DETAIL_VALUE_TYPE value, const UNITY_LINE_TYPE line) { + if (Unity.CurrentDetailStackSize >= UNITY_DETAIL_STACK_SIZE) { + UnityTestResultsFailBegin(line); + UnityPrint(UnityStrErrDetailStackFull); + UnityAddMsgIfSpecified(NULL); + UNITY_FAIL_AND_BAIL; + } + if (label >= UnityStrDetailLabelsCount) { + UnityTestResultsFailBegin(line); + UnityPrint(UnityStrErrDetailStackLabel); + UnityPrintNumberUnsigned(label); + UnityAddMsgIfSpecified(NULL); + UNITY_FAIL_AND_BAIL; + } + Unity.CurrentDetailStackLabels[Unity.CurrentDetailStackSize] = label; + Unity.CurrentDetailStackValues[Unity.CurrentDetailStackSize++] = value; +} +void UnityPopDetail(UNITY_DETAIL_LABEL_TYPE label, UNITY_DETAIL_VALUE_TYPE value, const UNITY_LINE_TYPE line) { + if (Unity.CurrentDetailStackSize == 0) { + UnityTestResultsFailBegin(line); + UnityPrint(UnityStrErrDetailStackEmpty); + UnityAddMsgIfSpecified(NULL); + UNITY_FAIL_AND_BAIL; + } + if ((Unity.CurrentDetailStackLabels[Unity.CurrentDetailStackSize-1] != label) || (Unity.CurrentDetailStackValues[Unity.CurrentDetailStackSize-1] != value)) { + UnityTestResultsFailBegin(line); + UnityPrint(UnityStrErrDetailStackPop); + UnityAddMsgIfSpecified(NULL); + UNITY_FAIL_AND_BAIL; + } + Unity.CurrentDetailStackSize--; +} +#endif +#endif + +/*----------------------------------------------- + * Command Line Argument Support + *-----------------------------------------------*/ +#ifdef UNITY_USE_COMMAND_LINE_ARGS + +char* UnityOptionIncludeNamed = NULL; +char* UnityOptionExcludeNamed = NULL; +int UnityVerbosity = 1; +int UnityStrictMatch = 0; + +/*-----------------------------------------------*/ +int UnityParseOptions(int argc, char** argv) +{ + int i; + UnityOptionIncludeNamed = NULL; + UnityOptionExcludeNamed = NULL; + UnityStrictMatch = 0; + + for (i = 1; i < argc; i++) + { + if (argv[i][0] == '-') + { + switch (argv[i][1]) + { + case 'l': /* list tests */ + return -1; + case 'n': /* include tests with name including this string */ + case 'f': /* an alias for -n */ + UnityStrictMatch = (argv[i][1] == 'n'); /* strictly match this string if -n */ + if (argv[i][2] == '=') + { + UnityOptionIncludeNamed = &argv[i][3]; + } + else if (++i < argc) + { + UnityOptionIncludeNamed = argv[i]; + } + else + { + UnityPrint("ERROR: No Test String to Include Matches For"); + UNITY_PRINT_EOL(); + return 1; + } + break; + case 'q': /* quiet */ + UnityVerbosity = 0; + break; + case 'v': /* verbose */ + UnityVerbosity = 2; + break; + case 'x': /* exclude tests with name including this string */ + if (argv[i][2] == '=') + { + UnityOptionExcludeNamed = &argv[i][3]; + } + else if (++i < argc) + { + UnityOptionExcludeNamed = argv[i]; + } + else + { + UnityPrint("ERROR: No Test String to Exclude Matches For"); + UNITY_PRINT_EOL(); + return 1; + } + break; + default: + UnityPrint("ERROR: Unknown Option "); + UNITY_OUTPUT_CHAR(argv[i][1]); + UNITY_PRINT_EOL(); + /* Now display help */ + /* FALLTHRU */ + case 'h': + UnityPrint("Options: "); UNITY_PRINT_EOL(); + UnityPrint("-l List all tests and exit"); UNITY_PRINT_EOL(); + UnityPrint("-f NAME Filter to run only tests whose name includes NAME"); UNITY_PRINT_EOL(); + UnityPrint("-n NAME Run only the test named NAME"); UNITY_PRINT_EOL(); + UnityPrint("-h show this Help menu"); UNITY_PRINT_EOL(); + UnityPrint("-q Quiet/decrease verbosity"); UNITY_PRINT_EOL(); + UnityPrint("-v increase Verbosity"); UNITY_PRINT_EOL(); + UnityPrint("-x NAME eXclude tests whose name includes NAME"); UNITY_PRINT_EOL(); + UNITY_OUTPUT_FLUSH(); + return 1; + } + } + } + + return 0; +} + +/*-----------------------------------------------*/ +static int IsStringInBiggerString(const char* longstring, const char* shortstring) +{ + const char* lptr = longstring; + const char* sptr = shortstring; + const char* lnext = lptr; + + if (*sptr == '*') + { + return UnityStrictMatch ? 0 : 1; + } + + while (*lptr) + { + lnext = lptr + 1; + + /* If they current bytes match, go on to the next bytes */ + while (*lptr && *sptr && (*lptr == *sptr)) + { + lptr++; + sptr++; + + switch (*sptr) + { + case '*': /* we encountered a wild-card */ + return UnityStrictMatch ? 0 : 1; + + case ',': /* we encountered the end of match string */ + case '"': + case '\'': + case 0: + return (!UnityStrictMatch || (*lptr == 0)) ? 1 : 0; + + case ':': /* we encountered the end of a partial match */ + return 2; + + default: + break; + } + } + + /* If we didn't match and we're on strict matching, we already know we failed */ + if (UnityStrictMatch) + { + return 0; + } + + /* Otherwise we start in the long pointer 1 character further and try again */ + lptr = lnext; + sptr = shortstring; + } + + return 0; +} + +/*-----------------------------------------------*/ +static int UnityStringArgumentMatches(const char* str) +{ + int retval; + const char* ptr1; + const char* ptr2; + const char* ptrf; + + /* Go through the options and get the substrings for matching one at a time */ + ptr1 = str; + while (ptr1[0] != 0) + { + if ((ptr1[0] == '"') || (ptr1[0] == '\'')) + { + ptr1++; + } + + /* look for the start of the next partial */ + ptr2 = ptr1; + ptrf = 0; + do + { + ptr2++; + if ((ptr2[0] == ':') && (ptr2[1] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && (ptr2[0] != ',')) + { + ptrf = &ptr2[1]; + } + } while ((ptr2[0] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && (ptr2[0] != ',')); + + while ((ptr2[0] != 0) && ((ptr2[0] == ':') || (ptr2[0] == '\'') || (ptr2[0] == '"') || (ptr2[0] == ','))) + { + ptr2++; + } + + /* done if complete filename match */ + retval = IsStringInBiggerString(Unity.TestFile, ptr1); + if (retval == 1) + { + return retval; + } + + /* done if testname match after filename partial match */ + if ((retval == 2) && (ptrf != 0)) + { + if (IsStringInBiggerString(Unity.CurrentTestName, ptrf)) + { + return 1; + } + } + + /* done if complete testname match */ + if (IsStringInBiggerString(Unity.CurrentTestName, ptr1) == 1) + { + return 1; + } + + ptr1 = ptr2; + } + + /* we couldn't find a match for any substrings */ + return 0; +} + +/*-----------------------------------------------*/ +int UnityTestMatches(void) +{ + /* Check if this test name matches the included test pattern */ + int retval; + if (UnityOptionIncludeNamed) + { + retval = UnityStringArgumentMatches(UnityOptionIncludeNamed); + } + else + { + retval = 1; + } + + /* Check if this test name matches the excluded test pattern */ + if (UnityOptionExcludeNamed) + { + if (UnityStringArgumentMatches(UnityOptionExcludeNamed)) + { + retval = 0; + } + } + + return retval; +} + +#endif /* UNITY_USE_COMMAND_LINE_ARGS */ +/*-----------------------------------------------*/ diff --git a/source_code/external_code/Unity/src/unity.h b/source_code/external_code/Unity/src/unity.h new file mode 100644 index 0000000..7c749c2 --- /dev/null +++ b/source_code/external_code/Unity/src/unity.h @@ -0,0 +1,698 @@ +/* ========================================================================= + Unity - A Test Framework for C + ThrowTheSwitch.org + Copyright (c) 2007-25 Mike Karlesky, Mark VanderVoord, & Greg Williams + SPDX-License-Identifier: MIT +========================================================================= */ + +#ifndef UNITY_FRAMEWORK_H +#define UNITY_FRAMEWORK_H +#define UNITY + +#define UNITY_VERSION_MAJOR 2 +#define UNITY_VERSION_MINOR 6 +#define UNITY_VERSION_BUILD 2 +#define UNITY_VERSION ((UNITY_VERSION_MAJOR << 16) | (UNITY_VERSION_MINOR << 8) | UNITY_VERSION_BUILD) + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "unity_internals.h" + +/*------------------------------------------------------- + * Test Setup / Teardown + *-------------------------------------------------------*/ + +/* These functions are intended to be called before and after each test. + * If using unity directly, these will need to be provided for each test + * executable built. If you are using the test runner generator and/or + * Ceedling, these are optional. */ +void setUp(void); +void tearDown(void); + +/* These functions are intended to be called at the beginning and end of an + * entire test suite. suiteTearDown() is passed the number of tests that + * failed, and its return value becomes the exit code of main(). If using + * Unity directly, you're in charge of calling these if they are desired. + * If using Ceedling or the test runner generator, these will be called + * automatically if they exist. */ +void suiteSetUp(void); +int suiteTearDown(int num_failures); + +/*------------------------------------------------------- + * Test Reset and Verify + *-------------------------------------------------------*/ + +/* These functions are intended to be called before or during tests in order + * to support complex test loops, etc. Both are NOT built into Unity. Instead + * the test runner generator will create them. resetTest will run teardown and + * setup again, verifying any end-of-test needs between. verifyTest will only + * run the verification. */ +void resetTest(void); +void verifyTest(void); + +/*------------------------------------------------------- + * Configuration Options + *------------------------------------------------------- + * All options described below should be passed as a compiler flag to all files using Unity. If you must add #defines, place them BEFORE the #include above. + + * Integers/longs/pointers + * - Unity attempts to automatically discover your integer sizes + * - define UNITY_EXCLUDE_STDINT_H to stop attempting to look in + * - define UNITY_EXCLUDE_LIMITS_H to stop attempting to look in + * - If you cannot use the automatic methods above, you can force Unity by using these options: + * - define UNITY_SUPPORT_64 + * - set UNITY_INT_WIDTH + * - set UNITY_LONG_WIDTH + * - set UNITY_POINTER_WIDTH + + * Floats + * - define UNITY_EXCLUDE_FLOAT to disallow floating point comparisons + * - define UNITY_FLOAT_PRECISION to specify the precision to use when doing TEST_ASSERT_EQUAL_FLOAT + * - define UNITY_FLOAT_TYPE to specify doubles instead of single precision floats + * - define UNITY_INCLUDE_DOUBLE to allow double floating point comparisons + * - define UNITY_EXCLUDE_DOUBLE to disallow double floating point comparisons (default) + * - define UNITY_DOUBLE_PRECISION to specify the precision to use when doing TEST_ASSERT_EQUAL_DOUBLE + * - define UNITY_DOUBLE_TYPE to specify something other than double + * - define UNITY_EXCLUDE_FLOAT_PRINT to trim binary size, won't print floating point values in errors + + * Output + * - by default, Unity prints to standard out with putchar. define UNITY_OUTPUT_CHAR(a) with a different function if desired + * - define UNITY_DIFFERENTIATE_FINAL_FAIL to print FAILED (vs. FAIL) at test end summary - for automated search for failure + + * Optimization + * - by default, line numbers are stored in unsigned shorts. Define UNITY_LINE_TYPE with a different type if your files are huge + * - by default, test and failure counters are unsigned shorts. Define UNITY_COUNTER_TYPE with a different type if you want to save space or have more than 65535 Tests. + + * Test Cases + * - define UNITY_SUPPORT_TEST_CASES to include the TEST_CASE macro, though really it's mostly about the runner generator script + + * Parameterized Tests + * - you'll want to create a define of TEST_CASE(...), TEST_RANGE(...) and/or TEST_MATRIX(...) which basically evaluates to nothing + + * Tests with Arguments + * - you'll want to define UNITY_USE_COMMAND_LINE_ARGS if you have the test runner passing arguments to Unity + + *------------------------------------------------------- + * Basic Fail and Ignore + *-------------------------------------------------------*/ + +#define TEST_FAIL_MESSAGE(message) UNITY_TEST_FAIL(__LINE__, (message)) +#define TEST_FAIL() UNITY_TEST_FAIL(__LINE__, NULL) +#define TEST_IGNORE_MESSAGE(message) UNITY_TEST_IGNORE(__LINE__, (message)) +#define TEST_IGNORE() UNITY_TEST_IGNORE(__LINE__, NULL) +#define TEST_MESSAGE(message) UnityMessage((message), __LINE__) +#define TEST_ONLY() +#ifdef UNITY_INCLUDE_PRINT_FORMATTED +#define TEST_PRINTF(message, ...) UnityPrintF(__LINE__, (message), ##__VA_ARGS__) +#endif + +/* It is not necessary for you to call PASS. A PASS condition is assumed if nothing fails. + * This method allows you to abort a test immediately with a PASS state, ignoring the remainder of the test. */ +#define TEST_PASS() TEST_ABORT() +#define TEST_PASS_MESSAGE(message) do { UnityMessage((message), __LINE__); TEST_ABORT(); } while (0) + +/*------------------------------------------------------- + * Build Directives + *------------------------------------------------------- + + * These macros do nothing, but they are useful for additional build context. + * Tools (like Ceedling) can scan for these directives and make use of them for + * per-test-executable #include search paths and linking. */ + +/* Add source files to a test executable's compilation and linking. Ex: TEST_SOURCE_FILE("sandwiches.c") */ +#define TEST_SOURCE_FILE(a) + +/* Customize #include search paths for a test executable's compilation. Ex: TEST_INCLUDE_PATH("src/module_a/inc") */ +#define TEST_INCLUDE_PATH(a) + +/*------------------------------------------------------- + * Test Asserts (simple) + *-------------------------------------------------------*/ + +/* Boolean */ +#define TEST_ASSERT(condition) UNITY_TEST_ASSERT( (condition), __LINE__, " Expression Evaluated To FALSE") +#define TEST_ASSERT_TRUE(condition) UNITY_TEST_ASSERT( (condition), __LINE__, " Expected TRUE Was FALSE") +#define TEST_ASSERT_UNLESS(condition) UNITY_TEST_ASSERT( !(condition), __LINE__, " Expression Evaluated To TRUE") +#define TEST_ASSERT_FALSE(condition) UNITY_TEST_ASSERT( !(condition), __LINE__, " Expected FALSE Was TRUE") +#define TEST_ASSERT_NULL(pointer) UNITY_TEST_ASSERT_NULL( (pointer), __LINE__, " Expected NULL") +#define TEST_ASSERT_NOT_NULL(pointer) UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, " Expected Non-NULL") +#define TEST_ASSERT_EMPTY(pointer) UNITY_TEST_ASSERT_EMPTY( (pointer), __LINE__, " Expected Empty") +#define TEST_ASSERT_NOT_EMPTY(pointer) UNITY_TEST_ASSERT_NOT_EMPTY((pointer), __LINE__, " Expected Non-Empty") + +/* Integers (of all sizes) */ +#define TEST_ASSERT_EQUAL_INT(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT8(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT8((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT16(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT16((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT32(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT32((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT64(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT64((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT8(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT8( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT16(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT16( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT32(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT32( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT64(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT64( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_size_t(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX8(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX8( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX16(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX16((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX32(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX64(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_CHAR(expected, actual) UNITY_TEST_ASSERT_EQUAL_CHAR((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_BITS(mask, expected, actual) UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_BITS_HIGH(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT)(-1), (actual), __LINE__, NULL) +#define TEST_ASSERT_BITS_LOW(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT)(0), (actual), __LINE__, NULL) +#define TEST_ASSERT_BIT_HIGH(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT)1 << (bit)), (UNITY_UINT)(-1), (actual), __LINE__, NULL) +#define TEST_ASSERT_BIT_LOW(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT)1 << (bit)), (UNITY_UINT)(0), (actual), __LINE__, NULL) + +/* Integer Not Equal To (of all sizes) */ +#define TEST_ASSERT_NOT_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_size_t(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_CHAR(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_CHAR((threshold), (actual), __LINE__, NULL) + +/* Integer Greater Than/ Less Than (of all sizes) */ +#define TEST_ASSERT_GREATER_THAN(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_size_t(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_CHAR(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_CHAR((threshold), (actual), __LINE__, NULL) + +#define TEST_ASSERT_LESS_THAN(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_size_t(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_CHAR(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_CHAR((threshold), (actual), __LINE__, NULL) + +#define TEST_ASSERT_GREATER_OR_EQUAL(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_size_t(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_CHAR(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, NULL) + +#define TEST_ASSERT_LESS_OR_EQUAL(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_size_t(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_CHAR(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, NULL) + +/* Integer Ranges (of all sizes) */ +#define TEST_ASSERT_INT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT8_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT16_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT64_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT8_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT16_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT64_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_size_t_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX8_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX16_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX64_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_CHAR_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_CHAR_WITHIN((delta), (expected), (actual), __LINE__, NULL) + +/* Integer Array Ranges (of all sizes) */ +#define TEST_ASSERT_INT_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_INT8_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_INT16_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_INT32_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_INT64_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_UINT_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_UINT8_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_UINT16_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_UINT32_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_UINT64_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_size_t_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_HEX_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_HEX8_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_HEX16_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_HEX32_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_HEX64_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_CHAR_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_CHAR_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) + + +/* Structs and Strings */ +#define TEST_ASSERT_EQUAL_PTR(expected, actual) UNITY_TEST_ASSERT_EQUAL_PTR((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_STRING(expected, actual) UNITY_TEST_ASSERT_EQUAL_STRING((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len) UNITY_TEST_ASSERT_EQUAL_STRING_LEN((expected), (actual), (len), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_MEMORY(expected, actual, len) UNITY_TEST_ASSERT_EQUAL_MEMORY((expected), (actual), (len), __LINE__, NULL) + +/* Arrays */ +#define TEST_ASSERT_EQUAL_INT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_size_t_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_PTR_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_STRING_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_MEMORY_ARRAY(expected, actual, len, num_elements) UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((expected), (actual), (len), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_CHAR_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_CHAR_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) + +/* Arrays Compared To Single Value */ +#define TEST_ASSERT_EACH_EQUAL_INT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_INT8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT8((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_INT16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT16((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_INT32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT32((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_INT64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT64((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT8((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT16((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT32((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT64((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_size_t(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX8((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX16((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX64((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_PTR(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_PTR((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_STRING(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_STRING((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_MEMORY(expected, actual, len, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY((expected), (actual), (len), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_CHAR(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_CHAR((expected), (actual), (num_elements), __LINE__, NULL) + +/* Floating Point (If Enabled) */ +#define TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_FLOAT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_NOT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_FLOAT_NOT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_FLOAT(expected, actual) UNITY_TEST_ASSERT_EQUAL_FLOAT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_FLOAT(expected, actual) UNITY_TEST_ASSERT_NOT_EQUAL_FLOAT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_FLOAT_ARRAY_WITHIN((delta), (expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_FLOAT(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_FLOAT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_FLOAT(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_FLOAT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_FLOAT(threshold, actual) UNITY_TEST_ASSERT_LESS_THAN_FLOAT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_FLOAT(threshold, actual) UNITY_TEST_ASSERT_LESS_OR_EQUAL_FLOAT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NEG_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NAN(actual) UNITY_TEST_ASSERT_FLOAT_IS_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_DETERMINATE(actual) UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_NAN(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE((actual), __LINE__, NULL) + +/* Double (If Enabled) */ +#define TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_DOUBLE_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_NOT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_DOUBLE_NOT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_DOUBLE(expected, actual) UNITY_TEST_ASSERT_EQUAL_DOUBLE((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_DOUBLE(expected, actual) UNITY_TEST_ASSERT_NOT_EQUAL_DOUBLE((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_DOUBLE_ARRAY_WITHIN((delta), (expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_DOUBLE(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_DOUBLE((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_DOUBLE(threshold, actual) UNITY_TEST_ASSERT_LESS_THAN_DOUBLE((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_DOUBLE(threshold, actual) UNITY_TEST_ASSERT_LESS_OR_EQUAL_DOUBLE((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NEG_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NAN(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual) UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE((actual), __LINE__, NULL) + +/* Shorthand */ +#ifdef UNITY_SHORTHAND_AS_OLD +#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, " Expected Not-Equal") +#endif +#ifdef UNITY_SHORTHAND_AS_INT +#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#endif +#ifdef UNITY_SHORTHAND_AS_MEM +#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT_EQUAL_MEMORY((&expected), (&actual), sizeof(expected), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#endif +#ifdef UNITY_SHORTHAND_AS_RAW +#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT(((expected) == (actual)), __LINE__, " Expected Equal") +#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, " Expected Not-Equal") +#endif +#ifdef UNITY_SHORTHAND_AS_NONE +#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#endif + +/*------------------------------------------------------- + * Test Asserts (with additional messages) + *-------------------------------------------------------*/ + +/* Boolean */ +#define TEST_ASSERT_MESSAGE(condition, message) UNITY_TEST_ASSERT( (condition), __LINE__, (message)) +#define TEST_ASSERT_TRUE_MESSAGE(condition, message) UNITY_TEST_ASSERT( (condition), __LINE__, (message)) +#define TEST_ASSERT_UNLESS_MESSAGE(condition, message) UNITY_TEST_ASSERT( !(condition), __LINE__, (message)) +#define TEST_ASSERT_FALSE_MESSAGE(condition, message) UNITY_TEST_ASSERT( !(condition), __LINE__, (message)) +#define TEST_ASSERT_NULL_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NULL( (pointer), __LINE__, (message)) +#define TEST_ASSERT_NOT_NULL_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, (message)) +#define TEST_ASSERT_EMPTY_MESSAGE(pointer, message) UNITY_TEST_ASSERT_EMPTY( (pointer), __LINE__, (message)) +#define TEST_ASSERT_NOT_EMPTY_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NOT_EMPTY((pointer), __LINE__, (message)) + +/* Integers (of all sizes) */ +#define TEST_ASSERT_EQUAL_INT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT8((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT16((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT32((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT64((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT8( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT16( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT32( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT64( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_size_t_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX8( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX16((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_BITS_MESSAGE(mask, expected, actual, message) UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_BITS_HIGH_MESSAGE(mask, actual, message) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(-1), (actual), __LINE__, (message)) +#define TEST_ASSERT_BITS_LOW_MESSAGE(mask, actual, message) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(0), (actual), __LINE__, (message)) +#define TEST_ASSERT_BIT_HIGH_MESSAGE(bit, actual, message) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(-1), (actual), __LINE__, (message)) +#define TEST_ASSERT_BIT_LOW_MESSAGE(bit, actual, message) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(0), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_CHAR_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_CHAR((expected), (actual), __LINE__, (message)) + +/* Integer Not Equal To (of all sizes) */ +#define TEST_ASSERT_NOT_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_CHAR((threshold), (actual), __LINE__, (message)) + + +/* Integer Greater Than/ Less Than (of all sizes) */ +#define TEST_ASSERT_GREATER_THAN_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_CHAR((threshold), (actual), __LINE__, (message)) + +#define TEST_ASSERT_LESS_THAN_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_CHAR((threshold), (actual), __LINE__, (message)) + +#define TEST_ASSERT_GREATER_OR_EQUAL_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, (message)) + +#define TEST_ASSERT_LESS_OR_EQUAL_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, (message)) + +/* Integer Ranges (of all sizes) */ +#define TEST_ASSERT_INT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT8_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT16_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT64_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT8_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT16_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT64_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_size_t_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX8_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX16_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX64_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_CHAR_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_CHAR_WITHIN((delta), (expected), (actual), __LINE__, (message)) + +/* Integer Array Ranges (of all sizes) */ +#define TEST_ASSERT_INT_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_INT8_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_INT16_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_INT32_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_INT64_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_UINT_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_UINT8_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_UINT16_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_UINT32_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_UINT64_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_size_t_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_HEX_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_HEX8_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_HEX16_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_HEX32_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_HEX64_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_CHAR_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_CHAR_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) + + +/* Structs and Strings */ +#define TEST_ASSERT_EQUAL_PTR_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_PTR((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_STRING((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_STRING_LEN_MESSAGE(expected, actual, len, message) UNITY_TEST_ASSERT_EQUAL_STRING_LEN((expected), (actual), (len), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_MEMORY_MESSAGE(expected, actual, len, message) UNITY_TEST_ASSERT_EQUAL_MEMORY((expected), (actual), (len), __LINE__, (message)) + +/* Arrays */ +#define TEST_ASSERT_EQUAL_INT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_size_t_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_PTR_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_STRING_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_MEMORY_ARRAY_MESSAGE(expected, actual, len, num_elements, message) UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((expected), (actual), (len), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_CHAR_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_CHAR_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) + +/* Arrays Compared To Single Value*/ +#define TEST_ASSERT_EACH_EQUAL_INT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_INT8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT8((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_INT16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT16((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_INT32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT32((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_INT64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT64((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT8((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT16((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT32((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT64((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_size_t_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX8((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX16((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX64((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_PTR_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_PTR((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_STRING_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_STRING((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_MEMORY_MESSAGE(expected, actual, len, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY((expected), (actual), (len), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_CHAR_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_CHAR((expected), (actual), (num_elements), __LINE__, (message)) + +/* Floating Point (If Enabled) */ +#define TEST_ASSERT_FLOAT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_FLOAT_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_FLOAT((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_FLOAT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_FLOAT((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_FLOAT_ARRAY_WITHIN((delta), (expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_FLOAT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_FLOAT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_FLOAT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_FLOAT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_FLOAT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_FLOAT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_FLOAT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_LESS_THAN_FLOAT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_FLOAT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_LESS_OR_EQUAL_FLOAT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE((actual), __LINE__, (message)) + +/* Double (If Enabled) */ +#define TEST_ASSERT_DOUBLE_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_DOUBLE_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_DOUBLE_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_DOUBLE((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_DOUBLE_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_DOUBLE((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_DOUBLE_ARRAY_WITHIN((delta), (expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_DOUBLE_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_DOUBLE_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_DOUBLE_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_DOUBLE((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_DOUBLE_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_LESS_THAN_DOUBLE((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_DOUBLE_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_LESS_OR_EQUAL_DOUBLE((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE((actual), __LINE__, (message)) + +/* Shorthand */ +#ifdef UNITY_SHORTHAND_AS_OLD +#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, (message)) +#endif +#ifdef UNITY_SHORTHAND_AS_INT +#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, message) +#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#endif +#ifdef UNITY_SHORTHAND_AS_MEM +#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_MEMORY((&expected), (&actual), sizeof(expected), __LINE__, message) +#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#endif +#ifdef UNITY_SHORTHAND_AS_RAW +#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT(((expected) == (actual)), __LINE__, message) +#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, message) +#endif +#ifdef UNITY_SHORTHAND_AS_NONE +#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#endif + +/* end of UNITY_FRAMEWORK_H */ +#ifdef __cplusplus +} +#endif +#endif diff --git a/source_code/external_code/Unity/src/unity_internals.h b/source_code/external_code/Unity/src/unity_internals.h new file mode 100644 index 0000000..a66859a --- /dev/null +++ b/source_code/external_code/Unity/src/unity_internals.h @@ -0,0 +1,1271 @@ +/* ========================================================================= + Unity - A Test Framework for C + ThrowTheSwitch.org + Copyright (c) 2007-25 Mike Karlesky, Mark VanderVoord, & Greg Williams + SPDX-License-Identifier: MIT +========================================================================= */ + +#ifndef UNITY_INTERNALS_H +#define UNITY_INTERNALS_H + +#ifdef UNITY_INCLUDE_CONFIG_H +#include "unity_config.h" +#endif + +#ifndef UNITY_EXCLUDE_SETJMP_H +#include +#endif + +#ifndef UNITY_EXCLUDE_MATH_H +#include +#endif + +#ifndef UNITY_EXCLUDE_STDDEF_H +#include +#endif + +#ifdef UNITY_INCLUDE_PRINT_FORMATTED +#include +#endif + +/* Unity Attempts to Auto-Detect Integer Types + * Attempt 1: UINT_MAX, ULONG_MAX in , or default to 32 bits + * Attempt 2: UINTPTR_MAX in , or default to same size as long + * The user may override any of these derived constants: + * UNITY_INT_WIDTH, UNITY_LONG_WIDTH, UNITY_POINTER_WIDTH */ +#ifndef UNITY_EXCLUDE_STDINT_H +#include +#endif + +#ifndef UNITY_EXCLUDE_LIMITS_H +#include +#endif + +#if defined(__GNUC__) || defined(__clang__) + #define UNITY_FUNCTION_ATTR(a) __attribute__((a)) +#else + #define UNITY_FUNCTION_ATTR(a) /* ignore */ +#endif + +/* UNITY_NORETURN is only required if we have setjmp.h. */ +#ifndef UNITY_EXCLUDE_SETJMP_H + #ifndef UNITY_NORETURN + #if defined(__cplusplus) + #if __cplusplus >= 201103L + #define UNITY_NORETURN [[ noreturn ]] + #endif + #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && __STDC_VERSION__ < 202311L + /* _Noreturn keyword is used from C11 but deprecated in C23. */ + #if defined(_WIN32) && defined(_MSC_VER) + /* We are using MSVC compiler on Windows platform. */ + /* Not all Windows SDKs supports , but compiler can support C11: */ + /* https://devblogs.microsoft.com/cppblog/c11-and-c17-standard-support-arriving-in-msvc/ */ + /* Not sure, that Mingw compilers has Windows SDK headers at all. */ + #include + #endif + + /* Using Windows SDK predefined macro for detecting supported SDK with MSVC compiler. */ + /* Mingw GCC should work without that fixes. */ + /* Based on: */ + /* https://docs.microsoft.com/en-us/cpp/porting/modifying-winver-and-win32-winnt?view=msvc-170 */ + /* NTDDI_WIN10_FE is equal to Windows 10 SDK 2104 */ + #if defined(_MSC_VER) && ((!defined(NTDDI_WIN10_FE)) || WDK_NTDDI_VERSION < NTDDI_WIN10_FE) + /* Based on tests and: */ + /* https://docs.microsoft.com/en-us/cpp/c-language/noreturn?view=msvc-170 */ + /* https://en.cppreference.com/w/c/language/_Noreturn */ + #define UNITY_NORETURN _Noreturn + #else /* Using newer Windows SDK or not MSVC compiler */ + #if defined(__GNUC__) + /* The header collides with __attribute(noreturn)__ from GCC. */ + #define UNITY_NORETURN _Noreturn + #else + #include + #define UNITY_NORETURN noreturn + #endif + #endif + #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L + /* Since C23, the keyword _Noreturn has been replaced by the attribute noreturn, based on: */ + /* https://en.cppreference.com/w/c/language/attributes/noreturn */ + #define UNITY_NORETURN [[ noreturn ]] + #elif defined(__IAR_SYSTEMS_ICC__) && (__IAR_SYSTEMS_ICC__ >= 8) + /* For IAR compilers supporting at least C99 use the IAR specific '__noreturn' keyword */ + /* Based on tests and: */ + /* https://wwwfiles.iar.com/arm/webic/doc/EWARM_DevelopmentGuide.ENU.pdf */ + /* https://wwwfiles.iar.com/AVR/webic/doc/EWAVR_CompilerGuide.pdf */ + /* https://wwwfiles.iar.com/msp430/webic/doc/EW430_CompilerReference.pdf */ + #define UNITY_NORETURN __noreturn + #endif + #endif + #ifndef UNITY_NORETURN + #define UNITY_NORETURN UNITY_FUNCTION_ATTR(__noreturn__) + #endif +#endif + +/*------------------------------------------------------- + * Guess Widths If Not Specified + *-------------------------------------------------------*/ + +/* Determine the size of an int, if not already specified. + * We cannot use sizeof(int), because it is not yet defined + * at this stage in the translation of the C program. + * Also sizeof(int) does return the size in addressable units on all platforms, + * which may not necessarily be the size in bytes. + * Therefore, infer it from UINT_MAX if possible. */ +#ifndef UNITY_INT_WIDTH + #ifdef UINT_MAX + #if (UINT_MAX == 0xFFFF) + #define UNITY_INT_WIDTH (16) + #elif (UINT_MAX == 0xFFFFFFFF) + #define UNITY_INT_WIDTH (32) + #elif (UINT_MAX == 0xFFFFFFFFFFFFFFFF) + #define UNITY_INT_WIDTH (64) + #endif + #else /* Set to default */ + #define UNITY_INT_WIDTH (32) + #endif /* UINT_MAX */ +#endif + +/* Determine the size of a long, if not already specified. */ +#ifndef UNITY_LONG_WIDTH + #ifdef ULONG_MAX + #if (ULONG_MAX == 0xFFFF) + #define UNITY_LONG_WIDTH (16) + #elif (ULONG_MAX == 0xFFFFFFFF) + #define UNITY_LONG_WIDTH (32) + #elif (ULONG_MAX == 0xFFFFFFFFFFFFFFFF) + #define UNITY_LONG_WIDTH (64) + #endif + #else /* Set to default */ + #define UNITY_LONG_WIDTH (32) + #endif /* ULONG_MAX */ +#endif + +/* Determine the size of a pointer, if not already specified. */ +#ifndef UNITY_POINTER_WIDTH + #ifdef UINTPTR_MAX + #if (UINTPTR_MAX <= 0xFFFF) + #define UNITY_POINTER_WIDTH (16) + #elif (UINTPTR_MAX <= 0xFFFFFFFF) + #define UNITY_POINTER_WIDTH (32) + #elif (UINTPTR_MAX <= 0xFFFFFFFFFFFFFFFF) + #define UNITY_POINTER_WIDTH (64) + #endif + #else /* Set to default */ + #define UNITY_POINTER_WIDTH UNITY_LONG_WIDTH + #endif /* UINTPTR_MAX */ +#endif + +/*------------------------------------------------------- + * Int Support (Define types based on detected sizes) + *-------------------------------------------------------*/ + +#if (UNITY_INT_WIDTH == 32) + typedef unsigned char UNITY_UINT8; + typedef unsigned short UNITY_UINT16; + typedef unsigned int UNITY_UINT32; + typedef signed char UNITY_INT8; + typedef signed short UNITY_INT16; + typedef signed int UNITY_INT32; +#elif (UNITY_INT_WIDTH == 16) + typedef unsigned char UNITY_UINT8; + typedef unsigned int UNITY_UINT16; + typedef unsigned long UNITY_UINT32; + typedef signed char UNITY_INT8; + typedef signed int UNITY_INT16; + typedef signed long UNITY_INT32; +#else + #error Invalid UNITY_INT_WIDTH specified! (16 or 32 are supported) +#endif + +/*------------------------------------------------------- + * 64-bit Support + *-------------------------------------------------------*/ + +/* Auto-detect 64 Bit Support */ +#ifndef UNITY_SUPPORT_64 + #if UNITY_LONG_WIDTH == 64 || UNITY_POINTER_WIDTH == 64 + #define UNITY_SUPPORT_64 + #endif +#endif + +/* 64-Bit Support Dependent Configuration */ +#ifndef UNITY_SUPPORT_64 + /* No 64-bit Support */ + typedef UNITY_UINT32 UNITY_UINT; + typedef UNITY_INT32 UNITY_INT; + #define UNITY_MAX_NIBBLES (8) /* Maximum number of nibbles in a UNITY_(U)INT */ +#else + /* 64-bit Support */ + #if (UNITY_LONG_WIDTH == 32) + typedef unsigned long long UNITY_UINT64; + typedef signed long long UNITY_INT64; + #elif (UNITY_LONG_WIDTH == 64) + typedef unsigned long UNITY_UINT64; + typedef signed long UNITY_INT64; + #else + #error Invalid UNITY_LONG_WIDTH specified! (32 or 64 are supported) + #endif + typedef UNITY_UINT64 UNITY_UINT; + typedef UNITY_INT64 UNITY_INT; + #define UNITY_MAX_NIBBLES (16) /* Maximum number of nibbles in a UNITY_(U)INT */ +#endif + +/*------------------------------------------------------- + * Pointer Support + *-------------------------------------------------------*/ + +#if (UNITY_POINTER_WIDTH == 32) + #define UNITY_PTR_TO_INT UNITY_INT32 + #define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX32 +#elif (UNITY_POINTER_WIDTH == 64) + #define UNITY_PTR_TO_INT UNITY_INT64 + #define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX64 +#elif (UNITY_POINTER_WIDTH == 16) + #define UNITY_PTR_TO_INT UNITY_INT16 + #define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX16 +#else + #error Invalid UNITY_POINTER_WIDTH specified! (16, 32 or 64 are supported) +#endif + +#ifndef UNITY_PTR_ATTRIBUTE + #define UNITY_PTR_ATTRIBUTE +#endif + +#ifndef UNITY_INTERNAL_PTR + #define UNITY_INTERNAL_PTR UNITY_PTR_ATTRIBUTE const void* +#endif + +/* optionally define UNITY_COMPARE_PTRS_ON_ZERO_ARRAY */ + +/*------------------------------------------------------- + * Float Support + *-------------------------------------------------------*/ + +#ifdef UNITY_EXCLUDE_FLOAT + +/* No Floating Point Support */ +#ifndef UNITY_EXCLUDE_DOUBLE +#define UNITY_EXCLUDE_DOUBLE /* Remove double when excluding float support */ +#endif +#ifndef UNITY_EXCLUDE_FLOAT_PRINT +#define UNITY_EXCLUDE_FLOAT_PRINT +#endif + +#else + +/* Floating Point Support */ +#ifndef UNITY_FLOAT_PRECISION +#define UNITY_FLOAT_PRECISION (0.00001f) +#endif +#ifndef UNITY_FLOAT_TYPE +#define UNITY_FLOAT_TYPE float +#endif +typedef UNITY_FLOAT_TYPE UNITY_FLOAT; + +/* isnan macro should be provided by math.h. Override if not macro */ +#ifndef UNITY_IS_NAN +#ifndef isnan +/* NaN is the only floating point value that does NOT equal itself. + * Therefore if n != n, then it is NaN. */ +#define UNITY_IS_NAN(n) ((n != n) ? 1 : 0) +#else +#define UNITY_IS_NAN(n) isnan(n) +#endif +#endif + +/* isinf macro should be provided by math.h. Override if not macro */ +#ifndef UNITY_IS_INF +#ifndef isinf +/* The value of Inf - Inf is NaN */ +#define UNITY_IS_INF(n) (UNITY_IS_NAN((n) - (n)) && !UNITY_IS_NAN(n)) +#else +#define UNITY_IS_INF(n) isinf(n) +#endif +#endif + +#endif + +/*------------------------------------------------------- + * Double Float Support + *-------------------------------------------------------*/ + +/* unlike float, we DON'T include by default */ +#if defined(UNITY_EXCLUDE_DOUBLE) || !defined(UNITY_INCLUDE_DOUBLE) + + /* No Floating Point Support */ + #ifndef UNITY_EXCLUDE_DOUBLE + #define UNITY_EXCLUDE_DOUBLE + #else + #undef UNITY_INCLUDE_DOUBLE + #endif + + #ifndef UNITY_EXCLUDE_FLOAT + #ifndef UNITY_DOUBLE_TYPE + #define UNITY_DOUBLE_TYPE double + #endif + typedef UNITY_FLOAT UNITY_DOUBLE; + /* For parameter in UnityPrintFloat(UNITY_DOUBLE), which aliases to double or float */ + #endif + +#else + + /* Double Floating Point Support */ + #ifndef UNITY_DOUBLE_PRECISION + #define UNITY_DOUBLE_PRECISION (1e-12) + #endif + + #ifndef UNITY_DOUBLE_TYPE + #define UNITY_DOUBLE_TYPE double + #endif + typedef UNITY_DOUBLE_TYPE UNITY_DOUBLE; + +#endif + +/*------------------------------------------------------- + * Output Method: stdout (DEFAULT) + *-------------------------------------------------------*/ +#ifndef UNITY_OUTPUT_CHAR + /* Default to using putchar, which is defined in stdio.h */ + #include + #define UNITY_OUTPUT_CHAR(a) (void)putchar(a) +#else + /* If defined as something else, make sure we declare it here so it's ready for use */ + #ifdef UNITY_OUTPUT_CHAR_HEADER_DECLARATION + extern void UNITY_OUTPUT_CHAR_HEADER_DECLARATION; + #endif +#endif + +#ifndef UNITY_OUTPUT_FLUSH + #ifdef UNITY_USE_FLUSH_STDOUT + /* We want to use the stdout flush utility */ + #include + #define UNITY_OUTPUT_FLUSH() (void)fflush(stdout) + #else + /* We've specified nothing, therefore flush should just be ignored */ + #define UNITY_OUTPUT_FLUSH() (void)0 + #endif +#else + /* If defined as something else, make sure we declare it here so it's ready for use */ + #ifdef UNITY_OUTPUT_FLUSH_HEADER_DECLARATION + extern void UNITY_OUTPUT_FLUSH_HEADER_DECLARATION; + #endif +#endif + +#ifndef UNITY_OUTPUT_FLUSH +#define UNITY_FLUSH_CALL() +#else +#define UNITY_FLUSH_CALL() UNITY_OUTPUT_FLUSH() +#endif + +#ifndef UNITY_PRINT_EOL +#define UNITY_PRINT_EOL() UNITY_OUTPUT_CHAR('\n') +#endif + +#ifndef UNITY_OUTPUT_START +#define UNITY_OUTPUT_START() +#endif + +#ifndef UNITY_OUTPUT_COMPLETE +#define UNITY_OUTPUT_COMPLETE() +#endif + +#ifdef UNITY_INCLUDE_EXEC_TIME + #if !defined(UNITY_EXEC_TIME_START) && \ + !defined(UNITY_EXEC_TIME_STOP) && \ + !defined(UNITY_PRINT_EXEC_TIME) && \ + !defined(UNITY_TIME_TYPE) + /* If none any of these macros are defined then try to provide a default implementation */ + + #if defined(UNITY_CLOCK_MS) + /* This is a simple way to get a default implementation on platforms that support getting a millisecond counter */ + #define UNITY_TIME_TYPE UNITY_UINT + #define UNITY_EXEC_TIME_START() Unity.CurrentTestStartTime = UNITY_CLOCK_MS() + #define UNITY_EXEC_TIME_STOP() Unity.CurrentTestStopTime = UNITY_CLOCK_MS() + #define UNITY_PRINT_EXEC_TIME() { \ + UNITY_UINT execTimeMs = (Unity.CurrentTestStopTime - Unity.CurrentTestStartTime); \ + UnityPrint(" ("); \ + UnityPrintNumberUnsigned(execTimeMs); \ + UnityPrint(" ms)"); \ + } + #elif defined(_WIN32) + #include + #define UNITY_TIME_TYPE clock_t + #define UNITY_GET_TIME(t) t = (clock_t)((clock() * 1000) / CLOCKS_PER_SEC) + #define UNITY_EXEC_TIME_START() UNITY_GET_TIME(Unity.CurrentTestStartTime) + #define UNITY_EXEC_TIME_STOP() UNITY_GET_TIME(Unity.CurrentTestStopTime) + #define UNITY_PRINT_EXEC_TIME() { \ + UNITY_UINT execTimeMs = (Unity.CurrentTestStopTime - Unity.CurrentTestStartTime); \ + UnityPrint(" ("); \ + UnityPrintNumberUnsigned(execTimeMs); \ + UnityPrint(" ms)"); \ + } + #elif defined(__unix__) || defined(__APPLE__) + #include + #define UNITY_TIME_TYPE struct timespec + #define UNITY_GET_TIME(t) clock_gettime(CLOCK_MONOTONIC, &t) + #define UNITY_EXEC_TIME_START() UNITY_GET_TIME(Unity.CurrentTestStartTime) + #define UNITY_EXEC_TIME_STOP() UNITY_GET_TIME(Unity.CurrentTestStopTime) + #define UNITY_PRINT_EXEC_TIME() { \ + UNITY_UINT execTimeMs = ((Unity.CurrentTestStopTime.tv_sec - Unity.CurrentTestStartTime.tv_sec) * 1000L); \ + execTimeMs += ((Unity.CurrentTestStopTime.tv_nsec - Unity.CurrentTestStartTime.tv_nsec) / 1000000L); \ + UnityPrint(" ("); \ + UnityPrintNumberUnsigned(execTimeMs); \ + UnityPrint(" ms)"); \ + } + #endif + #endif +#endif + +#ifndef UNITY_EXEC_TIME_START +#define UNITY_EXEC_TIME_START() do { /* nothing*/ } while (0) +#endif + +#ifndef UNITY_EXEC_TIME_STOP +#define UNITY_EXEC_TIME_STOP() do { /* nothing*/ } while (0) +#endif + +#ifndef UNITY_TIME_TYPE +#define UNITY_TIME_TYPE UNITY_UINT +#endif + +#ifndef UNITY_PRINT_EXEC_TIME +#define UNITY_PRINT_EXEC_TIME() do { /* nothing*/ } while (0) +#endif + +/*------------------------------------------------------- + * Footprint + *-------------------------------------------------------*/ + +#ifndef UNITY_LINE_TYPE +#define UNITY_LINE_TYPE UNITY_UINT +#endif + +#ifndef UNITY_COUNTER_TYPE +#define UNITY_COUNTER_TYPE UNITY_UINT +#endif + +/*------------------------------------------------------- + * Internal Structs Needed + *-------------------------------------------------------*/ + +typedef void (*UnityTestFunction)(void); + +#define UNITY_DISPLAY_RANGE_INT (0x10) +#define UNITY_DISPLAY_RANGE_UINT (0x20) +#define UNITY_DISPLAY_RANGE_HEX (0x40) +#define UNITY_DISPLAY_RANGE_CHAR (0x80) + +typedef enum +{ + UNITY_DISPLAY_STYLE_INT = (UNITY_INT_WIDTH / 8) + UNITY_DISPLAY_RANGE_INT, + UNITY_DISPLAY_STYLE_INT8 = 1 + UNITY_DISPLAY_RANGE_INT, + UNITY_DISPLAY_STYLE_INT16 = 2 + UNITY_DISPLAY_RANGE_INT, + UNITY_DISPLAY_STYLE_INT32 = 4 + UNITY_DISPLAY_RANGE_INT, +#ifdef UNITY_SUPPORT_64 + UNITY_DISPLAY_STYLE_INT64 = 8 + UNITY_DISPLAY_RANGE_INT, +#endif + + UNITY_DISPLAY_STYLE_UINT = (UNITY_INT_WIDTH / 8) + UNITY_DISPLAY_RANGE_UINT, + UNITY_DISPLAY_STYLE_UINT8 = 1 + UNITY_DISPLAY_RANGE_UINT, + UNITY_DISPLAY_STYLE_UINT16 = 2 + UNITY_DISPLAY_RANGE_UINT, + UNITY_DISPLAY_STYLE_UINT32 = 4 + UNITY_DISPLAY_RANGE_UINT, +#ifdef UNITY_SUPPORT_64 + UNITY_DISPLAY_STYLE_UINT64 = 8 + UNITY_DISPLAY_RANGE_UINT, +#endif + + UNITY_DISPLAY_STYLE_HEX8 = 1 + UNITY_DISPLAY_RANGE_HEX, + UNITY_DISPLAY_STYLE_HEX16 = 2 + UNITY_DISPLAY_RANGE_HEX, + UNITY_DISPLAY_STYLE_HEX32 = 4 + UNITY_DISPLAY_RANGE_HEX, +#ifdef UNITY_SUPPORT_64 + UNITY_DISPLAY_STYLE_HEX64 = 8 + UNITY_DISPLAY_RANGE_HEX, +#endif + + UNITY_DISPLAY_STYLE_CHAR = 1 + UNITY_DISPLAY_RANGE_CHAR + UNITY_DISPLAY_RANGE_INT, + + UNITY_DISPLAY_STYLE_UNKNOWN +} UNITY_DISPLAY_STYLE_T; + +typedef enum +{ + UNITY_EQUAL_TO = 0x1, + UNITY_GREATER_THAN = 0x2, + UNITY_GREATER_OR_EQUAL = 0x2 + UNITY_EQUAL_TO, + UNITY_SMALLER_THAN = 0x4, + UNITY_SMALLER_OR_EQUAL = 0x4 + UNITY_EQUAL_TO, + UNITY_NOT_EQUAL = 0x8 +} UNITY_COMPARISON_T; + +#ifndef UNITY_EXCLUDE_FLOAT +typedef enum UNITY_FLOAT_TRAIT +{ + UNITY_FLOAT_IS_NOT_INF = 0, + UNITY_FLOAT_IS_INF, + UNITY_FLOAT_IS_NOT_NEG_INF, + UNITY_FLOAT_IS_NEG_INF, + UNITY_FLOAT_IS_NOT_NAN, + UNITY_FLOAT_IS_NAN, + UNITY_FLOAT_IS_NOT_DET, + UNITY_FLOAT_IS_DET, + UNITY_FLOAT_INVALID_TRAIT +} UNITY_FLOAT_TRAIT_T; +#endif + +typedef enum +{ + UNITY_ARRAY_TO_VAL = 0, + UNITY_ARRAY_TO_ARRAY, + UNITY_ARRAY_UNKNOWN +} UNITY_FLAGS_T; + +#ifndef UNITY_EXCLUDE_DETAILS +#ifdef UNITY_DETAIL_STACK_SIZE +#ifndef UNITY_DETAIL_LABEL_TYPE +#define UNITY_DETAIL_LABEL_TYPE uint8_t +#endif +#ifndef UNITY_DETAIL_VALUE_TYPE +#define UNITY_DETAIL_VALUE_TYPE UNITY_PTR_TO_INT +#endif +#endif +#endif + +struct UNITY_STORAGE_T +{ + const char* TestFile; + const char* CurrentTestName; +#ifndef UNITY_EXCLUDE_DETAILS +#ifdef UNITY_DETAIL_STACK_SIZE + UNITY_DETAIL_LABEL_TYPE CurrentDetailStackLabels[UNITY_DETAIL_STACK_SIZE]; + UNITY_DETAIL_VALUE_TYPE CurrentDetailStackValues[UNITY_DETAIL_STACK_SIZE]; + UNITY_COUNTER_TYPE CurrentDetailStackSize; +#else + const char* CurrentDetail1; + const char* CurrentDetail2; +#endif +#endif + UNITY_LINE_TYPE CurrentTestLineNumber; + UNITY_COUNTER_TYPE NumberOfTests; + UNITY_COUNTER_TYPE TestFailures; + UNITY_COUNTER_TYPE TestIgnores; + UNITY_COUNTER_TYPE CurrentTestFailed; + UNITY_COUNTER_TYPE CurrentTestIgnored; +#ifdef UNITY_INCLUDE_EXEC_TIME + UNITY_TIME_TYPE CurrentTestStartTime; + UNITY_TIME_TYPE CurrentTestStopTime; +#endif +#ifndef UNITY_EXCLUDE_SETJMP_H + jmp_buf AbortFrame; +#endif +}; + +extern struct UNITY_STORAGE_T Unity; + +/*------------------------------------------------------- + * Test Suite Management + *-------------------------------------------------------*/ + +void UnityBegin(const char* filename); +int UnityEnd(void); +void UnitySetTestFile(const char* filename); +void UnityConcludeTest(void); + +#ifndef RUN_TEST +void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum); +#else +#define UNITY_SKIP_DEFAULT_RUNNER +#endif + +/*------------------------------------------------------- + * Details Support + *-------------------------------------------------------*/ + +#ifdef UNITY_EXCLUDE_DETAILS +#define UNITY_CLR_DETAILS() +#define UNITY_SET_DETAIL(d1) +#define UNITY_SET_DETAILS(d1,d2) +#else +#ifndef UNITY_DETAIL1_NAME +#define UNITY_DETAIL1_NAME "Function" +#endif + +#ifndef UNITY_DETAIL2_NAME +#define UNITY_DETAIL2_NAME "Argument" +#endif + +#ifdef UNITY_DETAIL_STACK_SIZE +/* stack based implementation */ +#ifndef UNITY_DETAIL_LABEL_NAMES +/* Note: If the label name string starts with '#', the second byte is interpreted as UNITY_DISPLAY_STYLE_T, + * and the detail value will be printed as number (e.g. "#\x24Line" to output "Line "). + * Otherwise, the detail value must be a pointer to a string that is valid until it is pop'ed. + */ +#define UNITY_DETAIL_LABEL_NAMES {0, UNITY_DETAIL1_NAME, UNITY_DETAIL2_NAME} +typedef enum +{ + UNITY_DETAIL_NONE = 0, + UNITY_DETAIL_D1 = 1, + UNITY_DETAIL_D2 = 2 +} UNITY_DETAIL_LABEL_T; +#endif +void UnityPushDetail(UNITY_DETAIL_LABEL_TYPE label, UNITY_DETAIL_VALUE_TYPE value, const UNITY_LINE_TYPE line); +void UnityPopDetail(UNITY_DETAIL_LABEL_TYPE label, UNITY_DETAIL_VALUE_TYPE value, const UNITY_LINE_TYPE line); + +#define UNITY_CLR_DETAILS() do { \ + if(Unity.CurrentDetailStackSize && \ + Unity.CurrentDetailStackLabels[Unity.CurrentDetailStackSize - 1] == UNITY_DETAIL_D2) { \ + Unity.CurrentDetailStackLabels[--Unity.CurrentDetailStackSize] = UNITY_DETAIL_NONE;} \ + if(Unity.CurrentDetailStackSize && \ + Unity.CurrentDetailStackLabels[Unity.CurrentDetailStackSize - 1] == UNITY_DETAIL_D1) { \ + Unity.CurrentDetailStackLabels[--Unity.CurrentDetailStackSize] = UNITY_DETAIL_NONE;} \ + } while (0) +#define UNITY_SET_DETAIL(d1) do { UNITY_CLR_DETAILS(); \ + UnityPushDetail(UNITY_DETAIL_D1, (UNITY_DETAIL_VALUE_TYPE)(d1), __LINE__); \ + } while (0) +#define UNITY_SET_DETAILS(d1,d2) do { UNITY_CLR_DETAILS(); \ + UnityPushDetail(UNITY_DETAIL_D1, (UNITY_DETAIL_VALUE_TYPE)(d1), __LINE__); \ + UnityPushDetail(UNITY_DETAIL_D2, (UNITY_DETAIL_VALUE_TYPE)(d2), __LINE__); \ + } while (0) + +#else +/* just two hardcoded slots */ +#define UNITY_CLR_DETAILS() do { Unity.CurrentDetail1 = 0; Unity.CurrentDetail2 = 0; } while (0) +#define UNITY_SET_DETAIL(d1) do { Unity.CurrentDetail1 = (d1); Unity.CurrentDetail2 = 0; } while (0) +#define UNITY_SET_DETAILS(d1,d2) do { Unity.CurrentDetail1 = (d1); Unity.CurrentDetail2 = (d2); } while (0) +#endif +#endif + +#ifdef UNITY_PRINT_TEST_CONTEXT +void UNITY_PRINT_TEST_CONTEXT(void); +#endif + +/*------------------------------------------------------- + * Test Output + *-------------------------------------------------------*/ + +void UnityPrint(const char* string); + +#ifdef UNITY_INCLUDE_PRINT_FORMATTED +void UnityPrintF(const UNITY_LINE_TYPE line, const char* format, ...); +#endif + +void UnityPrintLen(const char* string, const UNITY_UINT32 length); +void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number); +void UnityPrintIntNumberByStyle(const UNITY_INT number, const UNITY_DISPLAY_STYLE_T style); +void UnityPrintUintNumberByStyle(const UNITY_UINT number, const UNITY_DISPLAY_STYLE_T style); +void UnityPrintNumber(const UNITY_INT number_to_print); +void UnityPrintNumberUnsigned(const UNITY_UINT number); +void UnityPrintNumberHex(const UNITY_UINT number, const char nibbles_to_print); + +#ifndef UNITY_EXCLUDE_FLOAT_PRINT +void UnityPrintFloat(const UNITY_DOUBLE input_number); +#endif + +/*------------------------------------------------------- + * Test Assertion Functions + *------------------------------------------------------- + * Use the macros below this section instead of calling + * these directly. The macros have a consistent naming + * convention and will pull in file and line information + * for you. */ + +void UnityAssertEqualIntNumber(const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style); + +void UnityAssertEqualUintNumber(const UNITY_UINT expected, + const UNITY_UINT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style); + +void UnityAssertIntGreaterOrLessOrEqualNumber(const UNITY_INT threshold, + const UNITY_INT actual, + const UNITY_COMPARISON_T compare, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style); + +void UnityAssertUintGreaterOrLessOrEqualNumber(const UNITY_UINT threshold, + const UNITY_UINT actual, + const UNITY_COMPARISON_T compare, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style); + +void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style, + const UNITY_FLAGS_T flags); + +void UnityAssertBits(const UNITY_INT mask, + const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualString(const char* expected, + const char* actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualStringLen(const char* expected, + const char* actual, + const UNITY_UINT32 length, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualStringArray( UNITY_INTERNAL_PTR expected, + const char** actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags); + +void UnityAssertEqualMemory( UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 length, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags); + +void UnityAssertIntNumbersWithin(const UNITY_UINT delta, + const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style); + +void UnityAssertUintNumbersWithin(const UNITY_UINT delta, + const UNITY_UINT expected, + const UNITY_UINT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style); + +void UnityAssertNumbersArrayWithin(const UNITY_UINT delta, + UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style, + const UNITY_FLAGS_T flags); + +#ifndef UNITY_EXCLUDE_SETJMP_H +UNITY_NORETURN void UnityFail(const char* message, const UNITY_LINE_TYPE line); +UNITY_NORETURN void UnityIgnore(const char* message, const UNITY_LINE_TYPE line); +#else +void UnityFail(const char* message, const UNITY_LINE_TYPE line); +void UnityIgnore(const char* message, const UNITY_LINE_TYPE line); +#endif + +void UnityMessage(const char* message, const UNITY_LINE_TYPE line); + +#ifndef UNITY_EXCLUDE_FLOAT +void UnityAssertFloatsWithin(const UNITY_FLOAT delta, + const UNITY_FLOAT expected, + const UNITY_FLOAT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertFloatsNotWithin(const UNITY_FLOAT delta, + const UNITY_FLOAT expected, + const UNITY_FLOAT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertGreaterOrLessFloat(const UNITY_FLOAT threshold, + const UNITY_FLOAT actual, + const UNITY_COMPARISON_T compare, + const char* msg, + const UNITY_LINE_TYPE linenumber); + +void UnityAssertWithinFloatArray(const UNITY_FLOAT delta, + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* expected, + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags); + +void UnityAssertFloatSpecial(const UNITY_FLOAT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style); +#endif + +#ifndef UNITY_EXCLUDE_DOUBLE +void UnityAssertDoublesWithin(const UNITY_DOUBLE delta, + const UNITY_DOUBLE expected, + const UNITY_DOUBLE actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertDoublesNotWithin(const UNITY_DOUBLE delta, + const UNITY_DOUBLE expected, + const UNITY_DOUBLE actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertGreaterOrLessDouble(const UNITY_DOUBLE threshold, + const UNITY_DOUBLE actual, + const UNITY_COMPARISON_T compare, + const char* msg, + const UNITY_LINE_TYPE linenumber); + +void UnityAssertWithinDoubleArray(const UNITY_DOUBLE delta, + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* expected, + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags); + +void UnityAssertDoubleSpecial(const UNITY_DOUBLE actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style); +#endif + +/*------------------------------------------------------- + * Helpers + *-------------------------------------------------------*/ + +UNITY_INTERNAL_PTR UnityNumToPtr(const UNITY_INT num, const UNITY_UINT8 size); +#ifndef UNITY_EXCLUDE_FLOAT +UNITY_INTERNAL_PTR UnityFloatToPtr(const float num); +#endif +#ifndef UNITY_EXCLUDE_DOUBLE +UNITY_INTERNAL_PTR UnityDoubleToPtr(const double num); +#endif + +/*------------------------------------------------------- + * Error Strings We Might Need + *-------------------------------------------------------*/ + +extern const char UnityStrOk[]; +extern const char UnityStrPass[]; +extern const char UnityStrFail[]; +extern const char UnityStrIgnore[]; + +extern const char UnityStrErrFloat[]; +extern const char UnityStrErrDouble[]; +extern const char UnityStrErr64[]; +extern const char UnityStrErrShorthand[]; + +/*------------------------------------------------------- + * Test Running Macros + *-------------------------------------------------------*/ + +#ifdef UNITY_TEST_PROTECT +#define TEST_PROTECT() UNITY_TEST_PROTECT() +#else +#ifndef UNITY_EXCLUDE_SETJMP_H +#define TEST_PROTECT() (setjmp(Unity.AbortFrame) == 0) +#else +#define TEST_PROTECT() 1 +#endif +#endif + +#ifdef UNITY_TEST_ABORT +#define TEST_ABORT() UNITY_TEST_ABORT() +#else +#ifndef UNITY_EXCLUDE_SETJMP_H +#define TEST_ABORT() longjmp(Unity.AbortFrame, 1) +#else +#define TEST_ABORT() return +#endif +#endif + +/* Automatically enable variadic macros support, if it not enabled before */ +#ifndef UNITY_SUPPORT_VARIADIC_MACROS + #ifdef __STDC_VERSION__ + #if __STDC_VERSION__ >= 199901L + #define UNITY_SUPPORT_VARIADIC_MACROS + #endif + #endif +#endif + +/* This tricky series of macros gives us an optional line argument to treat it as RUN_TEST(func, num=__LINE__) */ +#ifndef RUN_TEST +#ifdef UNITY_SUPPORT_VARIADIC_MACROS +#define RUN_TEST(...) RUN_TEST_AT_LINE(__VA_ARGS__, __LINE__, throwaway) +#define RUN_TEST_AT_LINE(func, line, ...) UnityDefaultTestRun(func, #func, line) +#endif +#endif + +/* Enable default macros for masking param tests test cases */ +#ifdef UNITY_SUPPORT_TEST_CASES + #ifdef UNITY_SUPPORT_VARIADIC_MACROS + #if !defined(TEST_CASE) && !defined(UNITY_EXCLUDE_TEST_CASE) + #define TEST_CASE(...) + #endif + #if !defined(TEST_RANGE) && !defined(UNITY_EXCLUDE_TEST_RANGE) + #define TEST_RANGE(...) + #endif + #if !defined(TEST_MATRIX) && !defined(UNITY_EXCLUDE_TEST_MATRIX) + #define TEST_MATRIX(...) + #endif + #endif +#endif + +/* If we can't do the tricky version, we'll just have to require them to always include the line number */ +#ifndef RUN_TEST +#ifdef CMOCK +#define RUN_TEST(func, num) UnityDefaultTestRun(func, #func, num) +#else +#define RUN_TEST(func) UnityDefaultTestRun(func, #func, __LINE__) +#endif +#endif + +#define TEST_LINE_NUM (Unity.CurrentTestLineNumber) +#define TEST_IS_IGNORED (Unity.CurrentTestIgnored) +#define UNITY_NEW_TEST(a) \ + Unity.CurrentTestName = (a); \ + Unity.CurrentTestLineNumber = (UNITY_LINE_TYPE)(__LINE__); \ + Unity.NumberOfTests++; + +#ifndef UNITY_BEGIN +#define UNITY_BEGIN() UnityBegin(__FILE__) +#endif + +#ifndef UNITY_END +#define UNITY_END() UnityEnd() +#endif + +#ifndef UNITY_SHORTHAND_AS_INT +#ifndef UNITY_SHORTHAND_AS_MEM +#ifndef UNITY_SHORTHAND_AS_NONE +#ifndef UNITY_SHORTHAND_AS_RAW +#define UNITY_SHORTHAND_AS_OLD +#endif +#endif +#endif +#endif + +/*----------------------------------------------- + * Command Line Argument Support + *-----------------------------------------------*/ + +#ifdef UNITY_USE_COMMAND_LINE_ARGS +int UnityParseOptions(int argc, char** argv); +int UnityTestMatches(void); +#endif + +/*------------------------------------------------------- + * Basic Fail and Ignore + *-------------------------------------------------------*/ + +#define UNITY_TEST_FAIL(line, message) UnityFail( (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_IGNORE(line, message) UnityIgnore( (message), (UNITY_LINE_TYPE)(line)) + +/*------------------------------------------------------- + * Test Asserts + *-------------------------------------------------------*/ + +#define UNITY_TEST_ASSERT(condition, line, message) do { if (condition) { /* nothing*/ } else { UNITY_TEST_FAIL((line), (message)); } } while (0) +#define UNITY_TEST_ASSERT_NULL(pointer, line, message) UNITY_TEST_ASSERT(((pointer) == NULL), (line), (message)) +#define UNITY_TEST_ASSERT_NOT_NULL(pointer, line, message) UNITY_TEST_ASSERT(((pointer) != NULL), (line), (message)) +#define UNITY_TEST_ASSERT_EMPTY(pointer, line, message) UNITY_TEST_ASSERT(((pointer[0]) == 0), (line), (message)) +#define UNITY_TEST_ASSERT_NOT_EMPTY(pointer, line, message) UNITY_TEST_ASSERT(((pointer[0]) != 0), (line), (message)) + +#define UNITY_TEST_ASSERT_EQUAL_INT(expected, actual, line, message) UnityAssertEqualIntNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_EQUAL_INT8(expected, actual, line, message) UnityAssertEqualIntNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_EQUAL_INT16(expected, actual, line, message) UnityAssertEqualIntNumber((UNITY_INT)(UNITY_INT16)(expected), (UNITY_INT)(UNITY_INT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_EQUAL_INT32(expected, actual, line, message) UnityAssertEqualIntNumber((UNITY_INT)(UNITY_INT32)(expected), (UNITY_INT)(UNITY_INT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_EQUAL_UINT(expected, actual, line, message) UnityAssertEqualUintNumber((UNITY_UINT)(expected), (UNITY_UINT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_EQUAL_UINT8(expected, actual, line, message) UnityAssertEqualUintNumber((UNITY_UINT)(UNITY_UINT8)(expected), (UNITY_UINT)(UNITY_UINT8)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_EQUAL_UINT16(expected, actual, line, message) UnityAssertEqualUintNumber((UNITY_UINT)(UNITY_UINT16)(expected), (UNITY_UINT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_EQUAL_UINT32(expected, actual, line, message) UnityAssertEqualUintNumber((UNITY_UINT)(UNITY_UINT32)(expected), (UNITY_UINT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_EQUAL_HEX8(expected, actual, line, message) UnityAssertEqualUintNumber((UNITY_UINT)(UNITY_UINT8)(expected), (UNITY_UINT)(UNITY_UINT8)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_EQUAL_HEX16(expected, actual, line, message) UnityAssertEqualUintNumber((UNITY_UINT)(UNITY_UINT16)(expected), (UNITY_UINT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_EQUAL_HEX32(expected, actual, line, message) UnityAssertEqualUintNumber((UNITY_UINT)(UNITY_UINT32)(expected), (UNITY_UINT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_EQUAL_CHAR(expected, actual, line, message) UnityAssertEqualIntNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) +#define UNITY_TEST_ASSERT_BITS(mask, expected, actual, line, message) UnityAssertBits((UNITY_INT)(mask), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line)) + +#define UNITY_TEST_ASSERT_NOT_EQUAL_INT(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_NOT_EQUAL_INT8(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_NOT_EQUAL_INT16(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_NOT_EQUAL_INT32(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(threshold), (UNITY_UINT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT8(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT8 )(threshold), (UNITY_UINT)(UNITY_UINT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT16(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT16)(threshold), (UNITY_UINT)(UNITY_UINT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT32(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT32)(threshold), (UNITY_UINT)(UNITY_UINT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX8(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT8 )(threshold), (UNITY_UINT)(UNITY_UINT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX16(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT16)(threshold), (UNITY_UINT)(UNITY_UINT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX32(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT32)(threshold), (UNITY_UINT)(UNITY_UINT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_NOT_EQUAL_CHAR(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) + +#define UNITY_TEST_ASSERT_GREATER_THAN_INT(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT8(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT16(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT32(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(threshold), (UNITY_UINT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT8(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT8 )(threshold), (UNITY_UINT)(UNITY_UINT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT16(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT16)(threshold), (UNITY_UINT)(UNITY_UINT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT32(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT32)(threshold), (UNITY_UINT)(UNITY_UINT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX8(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT8 )(threshold), (UNITY_UINT)(UNITY_UINT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX16(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT16)(threshold), (UNITY_UINT)(UNITY_UINT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX32(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT32)(threshold), (UNITY_UINT)(UNITY_UINT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_GREATER_THAN_CHAR(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) + +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT8(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT16(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT32(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(threshold), (UNITY_UINT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT8(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT8 )(threshold), (UNITY_UINT)(UNITY_UINT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT16(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT16)(threshold), (UNITY_UINT)(UNITY_UINT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT32(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT32)(threshold), (UNITY_UINT)(UNITY_UINT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX8(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT8 )(threshold), (UNITY_UINT)(UNITY_UINT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX16(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT16)(threshold), (UNITY_UINT)(UNITY_UINT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX32(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT32)(threshold), (UNITY_UINT)(UNITY_UINT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_SMALLER_THAN_CHAR(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) + +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT) (threshold), (UNITY_INT) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 ) (threshold), (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16) (threshold), (UNITY_INT)(UNITY_INT16) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32) (threshold), (UNITY_INT)(UNITY_INT32) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT) (threshold), (UNITY_UINT) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT8 )(threshold), (UNITY_UINT)(UNITY_UINT8 )(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT16)(threshold), (UNITY_UINT)(UNITY_UINT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT32)(threshold), (UNITY_UINT)(UNITY_UINT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT8 )(threshold), (UNITY_UINT)(UNITY_UINT8 )(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT16)(threshold), (UNITY_UINT)(UNITY_UINT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT32)(threshold), (UNITY_UINT)(UNITY_UINT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_CHAR(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 ) (threshold), (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) + +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT) (threshold), (UNITY_INT) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT) (threshold), (UNITY_UINT) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT8 )(threshold), (UNITY_UINT)(UNITY_UINT8 )(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT16)(threshold), (UNITY_UINT)(UNITY_UINT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT32)(threshold), (UNITY_UINT)(UNITY_UINT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT8 )(threshold), (UNITY_UINT)(UNITY_UINT8 )(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT16)(threshold), (UNITY_UINT)(UNITY_UINT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(UNITY_UINT32)(threshold), (UNITY_UINT)(UNITY_UINT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_CHAR(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) + +#define UNITY_TEST_ASSERT_INT_WITHIN(delta, expected, actual, line, message) UnityAssertIntNumbersWithin( (delta), (UNITY_INT) (expected), (UNITY_INT) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_INT8_WITHIN(delta, expected, actual, line, message) UnityAssertIntNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_INT8 ) (expected), (UNITY_INT)(UNITY_INT8 ) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_INT16_WITHIN(delta, expected, actual, line, message) UnityAssertIntNumbersWithin((UNITY_UINT16 )(delta), (UNITY_INT)(UNITY_INT16) (expected), (UNITY_INT)(UNITY_INT16) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_INT32_WITHIN(delta, expected, actual, line, message) UnityAssertIntNumbersWithin((UNITY_UINT32 )(delta), (UNITY_INT)(UNITY_INT32) (expected), (UNITY_INT)(UNITY_INT32) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_UINT_WITHIN(delta, expected, actual, line, message) UnityAssertUintNumbersWithin( (delta), (UNITY_UINT) (expected), (UNITY_UINT) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_UINT8_WITHIN(delta, expected, actual, line, message) UnityAssertUintNumbersWithin((UNITY_UINT8 )(delta), (UNITY_UINT)(UNITY_UINT)(UNITY_UINT8 )(expected), (UNITY_UINT)(UNITY_UINT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_UINT16_WITHIN(delta, expected, actual, line, message) UnityAssertUintNumbersWithin((UNITY_UINT16)(delta), (UNITY_UINT)(UNITY_UINT)(UNITY_UINT16)(expected), (UNITY_UINT)(UNITY_UINT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_UINT32_WITHIN(delta, expected, actual, line, message) UnityAssertUintNumbersWithin((UNITY_UINT32)(delta), (UNITY_UINT)(UNITY_UINT)(UNITY_UINT32)(expected), (UNITY_UINT)(UNITY_UINT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_HEX8_WITHIN(delta, expected, actual, line, message) UnityAssertUintNumbersWithin((UNITY_UINT8 )(delta), (UNITY_UINT)(UNITY_UINT)(UNITY_UINT8 )(expected), (UNITY_UINT)(UNITY_UINT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_HEX16_WITHIN(delta, expected, actual, line, message) UnityAssertUintNumbersWithin((UNITY_UINT16)(delta), (UNITY_UINT)(UNITY_UINT)(UNITY_UINT16)(expected), (UNITY_UINT)(UNITY_UINT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_HEX32_WITHIN(delta, expected, actual, line, message) UnityAssertUintNumbersWithin((UNITY_UINT32)(delta), (UNITY_UINT)(UNITY_UINT)(UNITY_UINT32)(expected), (UNITY_UINT)(UNITY_UINT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_CHAR_WITHIN(delta, expected, actual, line, message) UnityAssertIntNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_INT8 ) (expected), (UNITY_INT)(UNITY_INT8 ) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) + +#define UNITY_TEST_ASSERT_INT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin( (delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_INT8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT8 )(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin( (delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT8 )(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT8 )(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_HEX16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_CHAR_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT8)( delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR, UNITY_ARRAY_TO_ARRAY) + + +#define UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, line, message) UnityAssertEqualIntNumber((UNITY_PTR_TO_INT)(expected), (UNITY_PTR_TO_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER) +#define UNITY_TEST_ASSERT_EQUAL_STRING(expected, actual, line, message) UnityAssertEqualString((const char*)(expected), (const char*)(actual), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len, line, message) UnityAssertEqualStringLen((const char*)(expected), (const char*)(actual), (UNITY_UINT32)(len), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_EQUAL_MEMORY(expected, actual, len, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), 1, (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) + +#define UNITY_TEST_ASSERT_EQUAL_INT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualStringArray((UNITY_INTERNAL_PTR)(expected), (const char**)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY(expected, actual, len, num_elements, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_CHAR_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR, UNITY_ARRAY_TO_ARRAY) + +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT) (expected), (UNITY_INT_WIDTH / 8)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8 )(expected), 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT16 )(expected), 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT32 )(expected), 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT) (expected), (UNITY_INT_WIDTH / 8)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT8 )(expected), 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT16)(expected), 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT32)(expected), 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8 )(expected), 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT16 )(expected), 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT32 )(expected), 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_PTR(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_PTR_TO_INT) (expected), (UNITY_POINTER_WIDTH / 8)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_STRING(expected, actual, num_elements, line, message) UnityAssertEqualStringArray((UNITY_INTERNAL_PTR)(expected), (const char**)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY(expected, actual, len, num_elements, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_CHAR(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8 )(expected), 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR, UNITY_ARRAY_TO_VAL) + +#ifdef UNITY_SUPPORT_64 +#define UNITY_TEST_ASSERT_EQUAL_INT64(expected, actual, line, message) UnityAssertEqualIntNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_EQUAL_UINT64(expected, actual, line, message) UnityAssertEqualIntNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_EQUAL_HEX64(expected, actual, line, message) UnityAssertEqualIntNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)(expected), 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT64)(expected), 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)(expected), 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message) UnityAssertIntNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message) UnityAssertUintNumbersWithin((delta), (UNITY_UINT)(expected), (UNITY_UINT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message) UnityAssertUintNumbersWithin((delta), (UNITY_UINT)(expected), (UNITY_UINT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_NOT_EQUAL_INT64(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT64(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(threshold), (UNITY_UINT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX64(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(threshold), (UNITY_UINT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(threshold), (UNITY_UINT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(threshold), (UNITY_UINT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(threshold), (UNITY_UINT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(threshold), (UNITY_UINT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT64(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT64(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(threshold), (UNITY_UINT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX64(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(threshold), (UNITY_UINT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64(threshold, actual, line, message) UnityAssertIntGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(threshold), (UNITY_UINT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64(threshold, actual, line, message) UnityAssertUintGreaterOrLessOrEqualNumber((UNITY_UINT)(threshold), (UNITY_UINT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT64)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT64)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT64)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_ARRAY) +#else +#define UNITY_TEST_ASSERT_EQUAL_INT64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_UINT64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_HEX64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#endif + +#ifdef UNITY_EXCLUDE_FLOAT +#define UNITY_TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_NOT_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_NOT_EQUAL_FLOAT(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_GREATER_THAN_FLOAT(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_FLOAT(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_LESS_THAN_FLOAT(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_LESS_OR_EQUAL_FLOAT(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#else +#define UNITY_TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual, line, message) UnityAssertFloatsWithin((UNITY_FLOAT)(delta), (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_FLOAT_NOT_WITHIN(delta, expected, actual, line, message) UnityAssertFloatsNotWithin((UNITY_FLOAT)(delta), (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT(expected, actual, line, message) UNITY_TEST_ASSERT_FLOAT_WITHIN((UNITY_FLOAT)(expected) * (UNITY_FLOAT)UNITY_FLOAT_PRECISION, (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_NOT_EQUAL_FLOAT(expected, actual, line, message) UNITY_TEST_ASSERT_FLOAT_NOT_WITHIN((UNITY_FLOAT)(expected) * (UNITY_FLOAT)UNITY_FLOAT_PRECISION, (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_FLOAT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertWithinFloatArray((UNITY_FLOAT)(delta), (const UNITY_FLOAT*)(expected), (const UNITY_FLOAT*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements, line, message) UnityAssertWithinFloatArray((UNITY_FLOAT)0, (const UNITY_FLOAT*)(expected), (const UNITY_FLOAT*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements, line, message) UnityAssertWithinFloatArray((UNITY_FLOAT)0, UnityFloatToPtr(expected), (const UNITY_FLOAT*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_GREATER_THAN_FLOAT(threshold, actual, line, message) UnityAssertGreaterOrLessFloat((UNITY_FLOAT)(threshold), (UNITY_FLOAT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_FLOAT(threshold, actual, line, message) UnityAssertGreaterOrLessFloat((UNITY_FLOAT)(threshold), (UNITY_FLOAT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_LESS_THAN_FLOAT(threshold, actual, line, message) UnityAssertGreaterOrLessFloat((UNITY_FLOAT)(threshold), (UNITY_FLOAT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_LESS_OR_EQUAL_FLOAT(threshold, actual, line, message) UnityAssertGreaterOrLessFloat((UNITY_FLOAT)(threshold), (UNITY_FLOAT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_FLOAT_IS_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NEG_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NAN(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NAN) +#define UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_DET) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NEG_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NAN) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_DET) +#endif + +#ifdef UNITY_EXCLUDE_DOUBLE +#define UNITY_TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_NOT_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_NOT_EQUAL_DOUBLE(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_GREATER_THAN_DOUBLE(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_LESS_THAN_DOUBLE(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_LESS_OR_EQUAL_DOUBLE(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#else +#define UNITY_TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual, line, message) UnityAssertDoublesWithin((UNITY_DOUBLE)(delta), (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_DOUBLE_NOT_WITHIN(delta, expected, actual, line, message) UnityAssertDoublesNotWithin((UNITY_DOUBLE)(delta), (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE(expected, actual, line, message) UNITY_TEST_ASSERT_DOUBLE_WITHIN((UNITY_DOUBLE)(expected) * (UNITY_DOUBLE)UNITY_DOUBLE_PRECISION, (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_NOT_EQUAL_DOUBLE(expected, actual, line, message) UNITY_TEST_ASSERT_DOUBLE_NOT_WITHIN((UNITY_DOUBLE)(expected) * (UNITY_DOUBLE)UNITY_DOUBLE_PRECISION, (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_DOUBLE_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertWithinDoubleArray((UNITY_DOUBLE)(delta), (const UNITY_DOUBLE*)(expected), (const UNITY_DOUBLE*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements, line, message) UnityAssertWithinDoubleArray((UNITY_DOUBLE)0, (const UNITY_DOUBLE*)(expected), (const UNITY_DOUBLE*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements, line, message) UnityAssertWithinDoubleArray((UNITY_DOUBLE)0, UnityDoubleToPtr(expected), (const UNITY_DOUBLE*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_GREATER_THAN_DOUBLE(threshold, actual, line, message) UnityAssertGreaterOrLessDouble((UNITY_DOUBLE)(threshold), (UNITY_DOUBLE)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE(threshold, actual, line, message) UnityAssertGreaterOrLessDouble((UNITY_DOUBLE)(threshold), (UNITY_DOUBLE)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_LESS_THAN_DOUBLE(threshold, actual, line, message) UnityAssertGreaterOrLessDouble((UNITY_DOUBLE)(threshold), (UNITY_DOUBLE)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_LESS_OR_EQUAL_DOUBLE(threshold, actual, line, message) UnityAssertGreaterOrLessDouble((UNITY_DOUBLE)(threshold), (UNITY_DOUBLE)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_DOUBLE_IS_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NEG_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NAN(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NAN) +#define UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_DET) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NEG_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NAN) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_DET) +#endif + +#if !defined(UNITY_EXCLUDE_DETAILS) && defined(UNITY_DETAIL_STACK_SIZE) +#define UNITY_DETAIL_PUSH(label, value) UnityPushDetail((UNITY_DETAIL_LABEL_TYPE)(label), (UNITY_DETAIL_VALUE_TYPE)(value), __LINE__) +#define UNITY_DETAIL_POP(label, value) UnityPopDetail((UNITY_DETAIL_LABEL_TYPE)(label), (UNITY_DETAIL_VALUE_TYPE)(value), __LINE__) +#else +#define UNITY_DETAIL_PUSH(label, value) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDetailStack) +#define UNITY_DETAIL_POP(label, value) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDetailStack) +#endif + +/* End of UNITY_INTERNALS_H */ +#endif diff --git a/source_code/external_code/dynamic_list/LICENSE b/source_code/external_code/dynamic_list/LICENSE new file mode 100644 index 0000000..74d1d7c --- /dev/null +++ b/source_code/external_code/dynamic_list/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2026 Epochryphon + +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/source_code/external_code/dynamic_list/README.md b/source_code/external_code/dynamic_list/README.md new file mode 100644 index 0000000..451c1fc --- /dev/null +++ b/source_code/external_code/dynamic_list/README.md @@ -0,0 +1,184 @@ +# Dynamic List Library in C + +This project provides a robust, generic **Dynamic List** implementation in C. It is designed to be versatile, supporting three distinct behavioral modes—**List, Stack, and Queue**—while handling memory management and resizing automatically. + +--- + +## 🛠 Features + +* **Generic Data Support**: Stores any data type by managing raw memory based on `member_size`. +* **Multiple Modes**: Switch between standard list operations, LIFO (Stack), or FIFO (Queue) logic. +* **Automatic Resizing**: Dynamically doubles capacity when the list is full to maintain amortized insertions. +* **Memory Efficiency**: Includes a "shrink-to-fit" feature (`free_excess_space`) to reclaim unused memory. +* **Safe API**: Includes extensive NULL checks and bounds protection to prevent segmentation faults. + +--- + +## 🏗 Data Structure Logic + +The library revolves around a central structure that tracks the state of the buffer: + +| Field | Description | +| --- | --- | +| `pointer_to_data` | A generic pointer (`void*`) to the contiguous memory block. | +| `member_size` | The size in bytes of a single element (e.g., `sizeof(int)`). | +| `current_occupancy` | The number of elements currently stored. | +| `maximum_occupancy` | The total capacity of the allocated buffer. | +| `mode` | Determines which operations (Push, Pop, Insert) are permitted. | + +--- + +## 🚦 Operational Modes + +The library uses a mode-based system to enforce specific data structure behaviors: + +### 1. List Mode (`dynamic_list_mode_list`) + +Allows full access to the data. You can append, prepend, insert at specific indices, replace items, or remove items from any position. + +### 2. Stack Mode (`dynamic_list_mode_stack`) + +Enforces **Last-In, First-Out (LIFO)** behavior. + +* **Push**: Items are added to the end. +* **Pop**: Items are removed from the end. + +### 3. Queue Mode (`dynamic_list_mode_queue`) + +Enforces **First-In, First-Out (FIFO)** behavior. + +* **Push**: Items are added to the end. +* **Pop**: Items are removed from the front (index 0), shifting remaining items forward. + +--- + +## 📖 Key Functions + +### Initialization and Destruction + +* `dynamic_list_get_new(...)`: Allocates the list structure and the initial data buffer. +* `dynamic_list_destroy(...)`: Safely frees all internal memory and nullifies the pointer. + +### Item Manipulation + +* `dynamic_list_append_item(...)`: Adds an item to the end (List mode). +* `dynamic_list_prepend_item(...)`: Shifts all items right and places the new item at index 0. +* `dynamic_list_insert_item(...)`: Places an item at a specific index, shifting existing items to the right. + +### Stack/Queue Interface + +* `dynamic_list_push(...)`: High-level entry point for adding data based on the current mode. +* `dynamic_list_pop(...)`: Removes and returns data from either the front or back depending on the mode. + +### Utilities + +* `dynamic_list_free_excess_space(...)`: Reallocates the buffer to match exactly the number of items stored. +* `dynamic_list_clear(...)`: Resets the count to 0 without deallocating memory (fast reset). + +--- + +## ⚠️ Implementation Notes + +1. **Memory Management**: The internal `_double_dynamic_list_size` function handles growth. If a `realloc` fails, the list maintains its original state to prevent data loss. +2. **Type Safety**: Because the library uses `void*`, users must ensure that the `member_size` provided during initialization matches the size of the data being pushed/popped. +3. **Internal Helpers**: Functions prefixed with `_` (like `_set_list_mode`) are intended for internal use to allow the Stack/Queue wrappers to reuse List logic without triggering mode-restriction errors. + +EXAMPLES: + +1. Float Queue (FIFO) + +In this mode, the list acts as a pipe: data enters at the back and is retrieved from the front. + + +```c +#include "dynamic_list.h" +#include + +void float_queue_example() { + // Initialize a queue for floats with an initial capacity of 4 + struct dynamic_list_struct* queue = dynamic_list_get_new(sizeof(float), 4, dynamic_list_mode_queue); + + float values[] = {1.5f, 2.7f, 3.14f}; + + // Push items onto the queue + for(int i = 0; i < 3; i++) { + dynamic_list_push(queue, &values[i]); + } + + // Pop items from the queue (FIFO: 1.5 will come out first) + float out; + while(dynamic_list_current_occupancy(queue) > 0) { + dynamic_list_pop(queue, &out); + printf("Popped from Queue: %.2f\n", out); + } + + dynamic_list_destroy(&queue); +} +``` + +2. Uint32 Stack (LIFO) + +In this mode, the list acts as a vertical pile: the last item added is the first one removed. + + +```c +#include "dynamic_list.h" +#include +#include + +void uint32_t_stack_example() { + // Initialize a stack for uint32_t + struct dynamic_list_struct* stack = dynamic_list_get_new(sizeof(uint32_t), 10, dynamic_list_mode_stack); + + uint32_t val1 = 100, val2 = 200, val3 = 300; + + dynamic_list_push(stack, &val1); + dynamic_list_push(stack, &val2); + dynamic_list_push(stack, &val3); + + // Pop items from the stack (LIFO: 300 will come out first) + uint32_t popped_val; + for(int i = 0; i < 3; i++) { + dynamic_list_pop(stack, &popped_val); + printf("Popped from Stack: %u\n", popped_val); + } + + dynamic_list_destroy(&stack); +} +``` +3. Custom Struct List (Random Access) + +This example uses a custom vector_3 struct. In List Mode, you can insert or remove items at any specific index. + +```c +#include "dynamic_list.h" +#include + +typedef struct { + float x, y, z; +} vector_3; + +void vector_list_example() { + // Initialize a list for vector_3 structs + struct dynamic_list_struct* list = dynamic_list_get_new(sizeof(vector_3), 2, dynamic_list_mode_list); + + vector_3 v1 = {1.0f, 0.0f, 0.0f}; + vector_3 v2 = {0.0f, 1.0f, 0.0f}; + vector_3 v3 = {0.0f, 0.0f, 1.0f}; // This will trigger a resize + + // Use List-specific operations + dynamic_list_append_item(list, &v1); + dynamic_list_append_item(list, &v3); + + // Insert v2 at index 1 (between v1 and v3) + dynamic_list_insert_item(list, &v2, 1); + + // Access by index + for(uint32_t i = 0; i < dynamic_list_current_occupancy(list); i++) { + vector_3* v = (vector_3*)dynamic_list_get_item(list, i); + printf("Vector at index %u: {%.1f, %.1f, %.1f}\n", i, v->x, v->y, v->z); + } + + dynamic_list_destroy(&list); +} +``` diff --git a/source_code/external_code/dynamic_list/_internal/private_dynamic_list.c b/source_code/external_code/dynamic_list/_internal/private_dynamic_list.c new file mode 100644 index 0000000..4d58211 --- /dev/null +++ b/source_code/external_code/dynamic_list/_internal/private_dynamic_list.c @@ -0,0 +1,69 @@ +#include "private_dynamic_list.h" +#include "dynamic_list.h" + +#include +#include +#include + +bool _double_dynamic_list_size(struct dynamic_list_struct* dynamic_list) +{ + if (dynamic_list == NULL) + { + return false; + } + + size_t new_max; + if (dynamic_list->maximum_occupancy == 0) + { + new_max = 1; + } + else + { + new_max = (size_t)dynamic_list->maximum_occupancy * 2; + } + + void* new_data; + if (dynamic_list->pointer_to_data == NULL) + { + new_data = calloc(new_max, dynamic_list->member_size); + if (new_data == NULL) + { + return false; + } + } + else + { + new_data = realloc(dynamic_list->pointer_to_data, + new_max * dynamic_list->member_size); + + if (new_data == NULL) + { + return false; + } + + memset + ( + (uint8_t*)new_data + dynamic_list->maximum_occupancy * + dynamic_list->member_size, + 0, + + (new_max - dynamic_list->maximum_occupancy) * + dynamic_list->member_size + ); + } + + if (new_data == NULL) + { + return false; + } + + dynamic_list->pointer_to_data = new_data; + dynamic_list->maximum_occupancy = (uint32_t)new_max; + return true; +} + +void _set_list_mode(struct dynamic_list_struct* dynamic_list, + dynamic_list_modes_enum new_mode) +{ + dynamic_list->mode = new_mode; +} diff --git a/source_code/external_code/dynamic_list/_internal/private_dynamic_list.h b/source_code/external_code/dynamic_list/_internal/private_dynamic_list.h new file mode 100644 index 0000000..8bf4bfc --- /dev/null +++ b/source_code/external_code/dynamic_list/_internal/private_dynamic_list.h @@ -0,0 +1,38 @@ +#ifndef private_dynamic_list_h +#define private_dynamic_list_h + +#include "dynamic_list.h" + +#include + +struct dynamic_list_struct; + +/** + * @brief Doubles the allocated size of a dynamic list's internal storage. + * + * This function reallocates memory for the list's data array to twice + * its current maximum occupancy. If the pointer to data is NULL, a fresh + * allocation is made. Newly allocated memory is zero-initialized. + * + * @param dynamic_list Pointer to the dynamic list to expand. + * + * Note: If memory allocation fails, the list remains unchanged. + */ +bool _double_dynamic_list_size(struct dynamic_list_struct* dynamic_list); + +/** + * @brief Sets the mode of a dynamic list. + * + * This is a private utility function intended for internal use + * to switch a list between list, stack, or queue modes. + * + * @param dynamic_list Pointer to the dynamic list whose mode is to be changed. + * @param new_mode The new mode to assign to the list. + * + * Note: No validation is performed; caller must ensure the mode is valid. + */ +void _set_list_mode(struct dynamic_list_struct* dynamic_list, + dynamic_list_modes_enum new_mode); + +#endif /* private_dynamic_list_h */ + diff --git a/source_code/external_code/dynamic_list/dynamic_list.c b/source_code/external_code/dynamic_list/dynamic_list.c new file mode 100644 index 0000000..681eef3 --- /dev/null +++ b/source_code/external_code/dynamic_list/dynamic_list.c @@ -0,0 +1,428 @@ +#include "dynamic_list.h" +#include "_internal/private_dynamic_list.h" + +#include +#include + +struct dynamic_list_struct* dynamic_list_get_new(size_t member_size, + uint32_t initial_capacity, + dynamic_list_modes_enum mode) +{ + if (member_size == 0 || initial_capacity == 0 || mode == dynamic_list_mode_none) + { + return NULL; + } + + dynamic_list_struct* new_list = calloc(1, sizeof(dynamic_list_struct)); + + if (new_list == NULL) + { + return NULL; + } + + new_list->pointer_to_data = calloc(initial_capacity, member_size); + + if (new_list->pointer_to_data == NULL) + { + return NULL; + } + + new_list->member_size = member_size; + new_list->original_max_occupancy = initial_capacity; + new_list->maximum_occupancy = initial_capacity; + new_list->current_occupancy = 0; + new_list->mode = mode; + + return new_list; + +} + +uint32_t dynamic_list_current_occupancy(const struct dynamic_list_struct* dynamic_list) +{ + if (dynamic_list == NULL) + { + return 0; + } + + return dynamic_list->current_occupancy; +} + +uint32_t dynamic_list_maximum_occupancy(const struct dynamic_list_struct* dynamic_list) +{ + if (dynamic_list == NULL) + { + return 0; + } + + return dynamic_list->maximum_occupancy; +} + +void* dynamic_list_get_item(const struct dynamic_list_struct* dynamic_list, + uint32_t index) +{ + if (dynamic_list == NULL) + { + return NULL; + } + + if (index >= dynamic_list->current_occupancy) + { + return NULL; + } + + return (uint8_t*)dynamic_list->pointer_to_data + + (index * dynamic_list->member_size); +} + +void dynamic_list_free_excess_space(struct dynamic_list_struct* dynamic_list) +{ + if (dynamic_list == NULL) + { + return; + } + + if (dynamic_list->current_occupancy == 0) + { + free(dynamic_list->pointer_to_data); + dynamic_list->pointer_to_data = NULL; + dynamic_list->current_occupancy = 0; + dynamic_list->maximum_occupancy = 0; + return; + } + + size_t new_size = (size_t)dynamic_list->current_occupancy * + dynamic_list->member_size; + + + void* new_data = realloc(dynamic_list->pointer_to_data, new_size); + + if (new_data == NULL) + { + return; + } + + dynamic_list->pointer_to_data = new_data; + dynamic_list->maximum_occupancy = dynamic_list->current_occupancy; +} + +void dynamic_list_push(struct dynamic_list_struct* dynamic_list, const void* new_occupant) +{ + if (dynamic_list == NULL) + { + return; + } + + if(dynamic_list->mode == dynamic_list_mode_none) + { + return; + } + + if(dynamic_list->mode == dynamic_list_mode_list) + { + return; + } + + if(dynamic_list->mode == dynamic_list_mode_stack) + { + _set_list_mode(dynamic_list, dynamic_list_mode_list); + dynamic_list_append_item(dynamic_list, new_occupant); + _set_list_mode(dynamic_list, dynamic_list_mode_stack); + } + + if(dynamic_list->mode == dynamic_list_mode_queue) + { + _set_list_mode(dynamic_list, dynamic_list_mode_list); + dynamic_list_append_item(dynamic_list, new_occupant); + _set_list_mode(dynamic_list, dynamic_list_mode_queue); + } + +} + +bool dynamic_list_pop(struct dynamic_list_struct* dynamic_list, + void* popped_value_storage) +{ + if (dynamic_list == NULL) + { + return false; + } + + if (popped_value_storage == NULL) + { + return false; + } + + + if(dynamic_list->mode == dynamic_list_mode_none) + { + return false; + } + + if(dynamic_list->mode == dynamic_list_mode_list) + { + return false; + } + + void* popped_value = NULL; + + if(dynamic_list->mode == dynamic_list_mode_stack) + { + _set_list_mode(dynamic_list, dynamic_list_mode_list); + + popped_value = dynamic_list_get_item(dynamic_list, + dynamic_list->current_occupancy - 1); + + if(popped_value == NULL) + { + return false; + } + memcpy(popped_value_storage, popped_value, dynamic_list->member_size); + + dynamic_list_remove_item(dynamic_list, + dynamic_list->current_occupancy - 1); + + _set_list_mode(dynamic_list, dynamic_list_mode_stack); + + } + + if(dynamic_list->mode == dynamic_list_mode_queue) + { + _set_list_mode(dynamic_list, dynamic_list_mode_list); + popped_value = dynamic_list_get_item(dynamic_list, 0); + if(popped_value == NULL) + { + return false; + } + memcpy(popped_value_storage, popped_value, dynamic_list->member_size); + dynamic_list_remove_item(dynamic_list, 0); + _set_list_mode(dynamic_list, dynamic_list_mode_queue); + } + + return true; +} + +void dynamic_list_append_item(struct dynamic_list_struct* dynamic_list, + const void* new_occupant) +{ + if (dynamic_list == NULL || new_occupant == NULL) + { + return; + } + + if(dynamic_list->mode != dynamic_list_mode_list) + { + return; + } + + if (dynamic_list->current_occupancy == dynamic_list->maximum_occupancy) + { + if (!_double_dynamic_list_size(dynamic_list)) + { + return; + } + } + + uint8_t* next_index_in_list = (uint8_t*)dynamic_list->pointer_to_data + + (dynamic_list->current_occupancy * + dynamic_list->member_size); + + memcpy(next_index_in_list, new_occupant, dynamic_list->member_size); + + dynamic_list->current_occupancy++; +} + + +void dynamic_list_prepend_item(struct dynamic_list_struct* dynamic_list, + const void* new_occupant) +{ + if (dynamic_list == NULL || new_occupant == NULL) + { + return; + } + + if(dynamic_list->mode != dynamic_list_mode_list) + { + return; + } + + if (dynamic_list->current_occupancy == dynamic_list->maximum_occupancy) + { + if (!_double_dynamic_list_size(dynamic_list)) + { + return; + } + } + + + if (dynamic_list->current_occupancy > 0) + { + memmove + ( + (uint8_t*)dynamic_list->pointer_to_data + + dynamic_list->member_size, + dynamic_list->pointer_to_data, + + (size_t)dynamic_list->current_occupancy * + dynamic_list->member_size + ); + } + + memcpy(dynamic_list->pointer_to_data, new_occupant, dynamic_list->member_size); + dynamic_list->current_occupancy++; +} + +void dynamic_list_insert_item(struct dynamic_list_struct* dynamic_list, + const void* new_occupant, + const uint32_t index) +{ + if (dynamic_list == NULL || new_occupant == NULL) + { + return; + } + + if(dynamic_list->mode != dynamic_list_mode_list) + { + return; + } + + if (dynamic_list->current_occupancy == dynamic_list->maximum_occupancy) + { + _double_dynamic_list_size(dynamic_list); + } + + + if (dynamic_list->current_occupancy > index) + { + memmove + ( + (uint8_t*)dynamic_list->pointer_to_data + + (index + 1) * dynamic_list->member_size, + + (uint8_t*)dynamic_list->pointer_to_data + + index * dynamic_list->member_size, + + (size_t)(dynamic_list->current_occupancy - index) * + dynamic_list->member_size + ); + } + + memcpy + ( + (uint8_t*)dynamic_list->pointer_to_data + index * + dynamic_list->member_size, + new_occupant, + dynamic_list->member_size + ); + + dynamic_list->current_occupancy++; +} + +void dynamic_list_replace_item(struct dynamic_list_struct* dynamic_list, + const void* new_occupant, + const uint32_t index) +{ + if (dynamic_list == NULL || new_occupant == NULL) + { + return; + } + + if(dynamic_list->mode != dynamic_list_mode_list) + { + return; + } + + if (index >= dynamic_list->current_occupancy) + { + return; + } + + memcpy + ( + (uint8_t*)dynamic_list->pointer_to_data + index * + dynamic_list->member_size, + new_occupant, + dynamic_list->member_size + ); +} + + +void dynamic_list_remove_item(struct dynamic_list_struct* dynamic_list, + uint32_t index) +{ + if (dynamic_list == NULL) + { + return; + } + + if(dynamic_list->mode != dynamic_list_mode_list) + { + return; + } + + if (index >= dynamic_list->current_occupancy) + { + return; + } + + if (index < dynamic_list->current_occupancy - 1) + { + memmove( + (uint8_t*)dynamic_list->pointer_to_data + + index * dynamic_list->member_size, + + (uint8_t*)dynamic_list->pointer_to_data + + (index + 1) * dynamic_list->member_size, + + (size_t)(dynamic_list->current_occupancy - index - 1) * + dynamic_list->member_size + ); + } + uint8_t* vacated_slot = (uint8_t*)dynamic_list->pointer_to_data + + (dynamic_list->current_occupancy - 1) * dynamic_list->member_size; + memset(vacated_slot, 0, dynamic_list->member_size); + + dynamic_list->current_occupancy--; +} + +void dynamic_list_reset(struct dynamic_list_struct* dynamic_list) +{ + if (dynamic_list == NULL) + { + return; + } + + free(dynamic_list->pointer_to_data); + dynamic_list->pointer_to_data = NULL; + dynamic_list->current_occupancy = 0; + dynamic_list->maximum_occupancy = dynamic_list->original_max_occupancy; +} + +void dynamic_list_clear(struct dynamic_list_struct* dynamic_list) +{ + if (dynamic_list == NULL) + { + return; + } + + dynamic_list->current_occupancy = 0; +} + + +void dynamic_list_destroy(struct dynamic_list_struct** dynamic_list_pointer) +{ + if (dynamic_list_pointer == NULL || *dynamic_list_pointer == NULL) + { + return; + } + + struct dynamic_list_struct* dynamic_list = *dynamic_list_pointer; + + free(dynamic_list->pointer_to_data); + dynamic_list->pointer_to_data = NULL; + dynamic_list->current_occupancy = 0; + dynamic_list->maximum_occupancy = 0; + dynamic_list->member_size = 0; + dynamic_list->mode = dynamic_list_mode_none; + free(dynamic_list); + + *dynamic_list_pointer = NULL; +} diff --git a/source_code/external_code/dynamic_list/dynamic_list.h b/source_code/external_code/dynamic_list/dynamic_list.h new file mode 100644 index 0000000..3ddb4b5 --- /dev/null +++ b/source_code/external_code/dynamic_list/dynamic_list.h @@ -0,0 +1,165 @@ +#ifndef dynamic_list_h +#define dynamic_list_h + +#include +#include +#include + +/** Enum representing the mode of a dynamic list */ +typedef enum +{ + dynamic_list_mode_none = 0, + dynamic_list_mode_list, + dynamic_list_mode_stack, + dynamic_list_mode_queue +} dynamic_list_modes_enum; + +/** Structure representing a dynamic list */ +typedef struct dynamic_list_struct +{ + void* pointer_to_data; /**< Pointer to internal data array */ + size_t member_size; /**< Size of each element in bytes */ + uint32_t current_occupancy; /**< Number of elements currently stored */ + uint32_t maximum_occupancy; /**< Current allocated capacity */ + uint32_t original_max_occupancy; /**< Initial allocated capacity */ + dynamic_list_modes_enum mode; /**< Mode of the dynamic list */ +} dynamic_list_struct; + +/** + * @brief Creates a new dynamic list. + * + * Allocates and initializes a new dynamic list with the specified + * member size, initial capacity, and mode. + * + * @param member_size Size in bytes of a single element. + * @param initial_capacity Initial number of elements to allocate space for. + * @param mode Mode of the list (list, stack, or queue). + * @return Pointer to the newly allocated list, or NULL on failure. + */ +struct dynamic_list_struct* dynamic_list_get_new(size_t member_size, + uint32_t initial_capacity, + dynamic_list_modes_enum mode); + +/** + * @brief Returns the current number of elements in the list. + * + * @param dynamic_list Pointer to the dynamic list. + * @return Current occupancy count, or 0 if the list is NULL. + */ +uint32_t dynamic_list_current_occupancy(const struct dynamic_list_struct* dynamic_list); + +/** + * @brief Returns the maximum allocated capacity of the list. + * + * @param dynamic_list Pointer to the dynamic list. + * @return Maximum occupancy, or 0 if the list is NULL. + */ +uint32_t dynamic_list_maximum_occupancy(const struct dynamic_list_struct* dynamic_list); + +/** + * @brief Returns a pointer to the element at the given index. + * + * @param dynamic_list Pointer to the dynamic list. + * @param index Index of the element. + * @return Pointer to the element, or NULL if invalid. + */ +void* dynamic_list_get_item(const struct dynamic_list_struct* dynamic_list, + uint32_t index); + +/** + * @brief Frees unused memory in the list, shrinking it to fit current occupancy. + * + * @param dynamic_list Pointer to the dynamic list. + */ +void dynamic_list_free_excess_space(struct dynamic_list_struct* dynamic_list); + +/** + * @brief Pushes a new element onto the dynamic list if it is in stack or queue mode. + * + * @param dynamic_list Pointer to the dynamic list. + * @param new_occupant Pointer to the element to add. + */ +void dynamic_list_push(struct dynamic_list_struct* dynamic_list, + const void* new_occupant); + +/** + * @brief Pops an element from the dynamic list (stack or queue mode). + * + * Copivoides the element into the provided storage buffer. + * + * @param dynamic_list Pointer to the dynamic list. + * @param popped_value_storage Pointer to a buffer large enough to hold one element. + */ +bool dynamic_list_pop(struct dynamic_list_struct* dynamic_list, + void* popped_value_storage); + +/** + * @brief Appends a new element to a list-mode dynamic list. + * + * @param dynamic_list Pointer to the dynamic list. + * @param new_occupant Pointer to the element to add. + */ +void dynamic_list_append_item(struct dynamic_list_struct* dynamic_list, + const void* new_occupant); + +/** + * @brief Prepends a new element to a list-mode dynamic list. + * + * @param dynamic_list Pointer to the dynamic list. + * @param new_occupant Pointer to the element to add. + */ +void dynamic_list_prepend_item(struct dynamic_list_struct* dynamic_list, const void* new_occupant); + +/** + * @brief Inserts a new element at a specified index in a list-mode dynamic list. + * + * @param dynamic_list Pointer to the dynamic list. + * @param new_occupant Pointer to the element to insert. + * @param index Index at which to insert the element. + */ +void dynamic_list_insert_item(struct dynamic_list_struct* dynamic_list, + const void* new_occupant, + uint32_t index); + +/** + * @brief Replaces an element at a specified index in a list-mode dynamic list. + * + * @param dynamic_list Pointer to the dynamic list. + * @param new_occupant Pointer to the new element. + * @param index Index of the element to replace. + */ +void dynamic_list_replace_item(struct dynamic_list_struct* dynamic_list, + const void* new_occupant, + uint32_t index); + +/** + * @brief Removes an element at a specified index in a list-mode dynamic list. + * + * @param dynamic_list Pointer to the dynamic list. + * @param index Index of the element to remove. + */ +void dynamic_list_remove_item(struct dynamic_list_struct* dynamic_list, + uint32_t index); + +/** + * @brief Resets the list to its original capacity and clears all elements. + * + * @param dynamic_list Pointer to the dynamic list. + */ +void dynamic_list_reset(struct dynamic_list_struct* dynamic_list); + +/** + * @brief Clears all elements in the list but retains current allocated capacity. + * + * @param dynamic_list Pointer to the dynamic list. + */ +void dynamic_list_clear(struct dynamic_list_struct* dynamic_list); + +/** + * @brief Destroys a dynamic list and frees all allocated memory. + * + * @param dynamic_list_pointer Pointer to the list pointer. + */ +void dynamic_list_destroy(struct dynamic_list_struct** dynamic_list_pointer); + +#endif /* dynamic_list_h */ diff --git a/source_code/external_code/hash_map/LICENSE b/source_code/external_code/hash_map/LICENSE new file mode 100644 index 0000000..541ae74 --- /dev/null +++ b/source_code/external_code/hash_map/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2020 Joshua J Baker + +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. \ No newline at end of file diff --git a/source_code/external_code/hash_map/addendum.txt b/source_code/external_code/hash_map/addendum.txt new file mode 100644 index 0000000..df46fcc --- /dev/null +++ b/source_code/external_code/hash_map/addendum.txt @@ -0,0 +1,10 @@ +I have renamed the static functions + +__malloc +__remalloc + +and + +__free + +in the .c file, in order to respect the C standard and hopefully get it working on openBSD diff --git a/source_code/external_code/hash_map/hashmap.c b/source_code/external_code/hash_map/hashmap.c new file mode 100644 index 0000000..c7de43c --- /dev/null +++ b/source_code/external_code/hash_map/hashmap.c @@ -0,0 +1,1154 @@ +// Copyright 2020 Joshua J Baker. All rights reserved. +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include "hashmap.h" + +#define GROW_AT 0.60 /* 60% */ +#define SHRINK_AT 0.10 /* 10% */ + +#ifndef HASHMAP_LOAD_FACTOR +#define HASHMAP_LOAD_FACTOR GROW_AT +#endif + +static void *(*hashmap_malloc)(size_t) = NULL; +static void *(*hashmap__realloc)(void *, size_t) = NULL; +static void (*hashmap__free)(void *) = NULL; + +// hashmap_set_allocator allows for configuring a custom allocator for +// all hashmap library operations. This function, if needed, should be called +// only once at startup and a prior to calling hashmap_new(). +void hashmap_set_allocator(void *(*malloc)(size_t), void (*free)(void*)) { + hashmap_malloc = malloc; + hashmap__free = free; +} + +struct bucket { + uint64_t hash:48; + uint64_t dib:16; +}; + +// hashmap is an open addressed hash map using robinhood hashing. +struct hashmap { + void *(*malloc)(size_t); + void *(*realloc)(void *, size_t); + void (*free)(void *); + size_t elsize; + size_t cap; + uint64_t seed0; + uint64_t seed1; + uint64_t (*hash)(const void *item, uint64_t seed0, uint64_t seed1); + int (*compare)(const void *a, const void *b, void *udata); + void (*elfree)(void *item); + void *udata; + size_t bucketsz; + size_t nbuckets; + size_t count; + size_t mask; + size_t growat; + size_t shrinkat; + uint8_t loadfactor; + uint8_t growpower; + bool oom; + void *buckets; + void *spare; + void *edata; +}; + +void hashmap_set_grow_by_power(struct hashmap *map, size_t power) { + map->growpower = power < 1 ? 1 : power > 16 ? 16 : power; +} + +static double clamp_load_factor(double factor, double default_factor) { + // Check for NaN and clamp between 50% and 90% + return factor != factor ? default_factor : + factor < 0.50 ? 0.50 : + factor > 0.95 ? 0.95 : + factor; +} + +void hashmap_set_load_factor(struct hashmap *map, double factor) { + factor = clamp_load_factor(factor, map->loadfactor / 100.0); + map->loadfactor = factor * 100; + map->growat = map->nbuckets * (map->loadfactor / 100.0); +} + +static struct bucket *bucket_at0(void *buckets, size_t bucketsz, size_t i) { + return (struct bucket*)(((char*)buckets)+(bucketsz*i)); +} + +static struct bucket *bucket_at(const struct hashmap *map, size_t index) { + return bucket_at0(map->buckets, map->bucketsz, index); +} + +static void *bucket_item(struct bucket *entry) { + return ((char*)entry)+sizeof(struct bucket); +} + +static uint64_t clip_hash(uint64_t hash) { + return hash & 0xFFFFFFFFFFFF; +} + +static uint64_t get_hash(const struct hashmap *map, const void *key) { + return clip_hash(map->hash(key, map->seed0, map->seed1)); +} + + +// hashmap_new_with_allocator returns a new hash map using a custom allocator. +// See hashmap_new for more information information +struct hashmap *hashmap_new_with_allocator(void *(*_malloc)(size_t), + void *(*_realloc)(void*, size_t), void (*_free)(void*), + size_t elsize, size_t cap, uint64_t seed0, uint64_t seed1, + uint64_t (*hash)(const void *item, uint64_t seed0, uint64_t seed1), + int (*compare)(const void *a, const void *b, void *udata), + void (*elfree)(void *item), + void *udata) +{ + _malloc = _malloc ? _malloc : hashmap_malloc ? hashmap_malloc : malloc; + _realloc = _realloc ? _realloc : hashmap__realloc ? hashmap__realloc : realloc; + _free = _free ? _free : hashmap__free ? hashmap__free : free; + size_t ncap = 16; + if (cap < ncap) { + cap = ncap; + } else { + while (ncap < cap) { + ncap *= 2; + } + cap = ncap; + } + size_t bucketsz = sizeof(struct bucket) + elsize; + while (bucketsz & (sizeof(uintptr_t)-1)) { + bucketsz++; + } + // hashmap + spare + edata + size_t size = sizeof(struct hashmap)+bucketsz*2; + struct hashmap *map = _malloc(size); + if (!map) { + return NULL; + } + memset(map, 0, sizeof(struct hashmap)); + map->elsize = elsize; + map->bucketsz = bucketsz; + map->seed0 = seed0; + map->seed1 = seed1; + map->hash = hash; + map->compare = compare; + map->elfree = elfree; + map->udata = udata; + map->spare = ((char*)map)+sizeof(struct hashmap); + map->edata = (char*)map->spare+bucketsz; + map->cap = cap; + map->nbuckets = cap; + map->mask = map->nbuckets-1; + map->buckets = _malloc(map->bucketsz*map->nbuckets); + if (!map->buckets) { + _free(map); + return NULL; + } + memset(map->buckets, 0, map->bucketsz*map->nbuckets); + map->growpower = 1; + map->loadfactor = clamp_load_factor(HASHMAP_LOAD_FACTOR, GROW_AT) * 100; + map->growat = map->nbuckets * (map->loadfactor / 100.0); + map->shrinkat = map->nbuckets * SHRINK_AT; + map->malloc = _malloc; + map->realloc = _realloc; + map->free = _free; + return map; +} + +// hashmap_new returns a new hash map. +// Param `elsize` is the size of each element in the tree. Every element that +// is inserted, deleted, or retrieved will be this size. +// Param `cap` is the default lower capacity of the hashmap. Setting this to +// zero will default to 16. +// Params `seed0` and `seed1` are optional seed values that are passed to the +// following `hash` function. These can be any value you wish but it's often +// best to use randomly generated values. +// Param `hash` is a function that generates a hash value for an item. It's +// important that you provide a good hash function, otherwise it will perform +// poorly or be vulnerable to Denial-of-service attacks. This implementation +// comes with two helper functions `hashmap_sip()` and `hashmap_murmur()`. +// Param `compare` is a function that compares items in the tree. See the +// qsort stdlib function for an example of how this function works. +// The hashmap must be freed with hashmap_free(). +// Param `elfree` is a function that frees a specific item. This should be NULL +// unless you're storing some kind of reference data in the hash. +struct hashmap *hashmap_new(size_t elsize, size_t cap, uint64_t seed0, + uint64_t seed1, + uint64_t (*hash)(const void *item, uint64_t seed0, uint64_t seed1), + int (*compare)(const void *a, const void *b, void *udata), + void (*elfree)(void *item), + void *udata) +{ + return hashmap_new_with_allocator(NULL, NULL, NULL, elsize, cap, seed0, + seed1, hash, compare, elfree, udata); +} + +static void free_elements(struct hashmap *map) { + if (map->elfree) { + for (size_t i = 0; i < map->nbuckets; i++) { + struct bucket *bucket = bucket_at(map, i); + if (bucket->dib) map->elfree(bucket_item(bucket)); + } + } +} + +// hashmap_clear quickly clears the map. +// Every item is called with the element-freeing function given in hashmap_new, +// if present, to free any data referenced in the elements of the hashmap. +// When the update_cap is provided, the map's capacity will be updated to match +// the currently number of allocated buckets. This is an optimization to ensure +// that this operation does not perform any allocations. +void hashmap_clear(struct hashmap *map, bool update_cap) { + map->count = 0; + free_elements(map); + if (update_cap) { + map->cap = map->nbuckets; + } else if (map->nbuckets != map->cap) { + void *new_buckets = map->malloc(map->bucketsz*map->cap); + if (new_buckets) { + map->free(map->buckets); + map->buckets = new_buckets; + } + map->nbuckets = map->cap; + } + memset(map->buckets, 0, map->bucketsz*map->nbuckets); + map->mask = map->nbuckets-1; + map->growat = map->nbuckets * (map->loadfactor / 100.0) ; + map->shrinkat = map->nbuckets * SHRINK_AT; +} + +static bool resize0(struct hashmap *map, size_t new_cap) { + struct hashmap *map2 = hashmap_new_with_allocator(map->malloc, map->realloc, + map->free, map->elsize, new_cap, map->seed0, map->seed1, map->hash, + map->compare, map->elfree, map->udata); + if (!map2) return false; + for (size_t i = 0; i < map->nbuckets; i++) { + struct bucket *entry = bucket_at(map, i); + if (!entry->dib) { + continue; + } + entry->dib = 1; + size_t j = entry->hash & map2->mask; + while(1) { + struct bucket *bucket = bucket_at(map2, j); + if (bucket->dib == 0) { + memcpy(bucket, entry, map->bucketsz); + break; + } + if (bucket->dib < entry->dib) { + memcpy(map2->spare, bucket, map->bucketsz); + memcpy(bucket, entry, map->bucketsz); + memcpy(entry, map2->spare, map->bucketsz); + } + j = (j + 1) & map2->mask; + entry->dib += 1; + } + } + map->free(map->buckets); + map->buckets = map2->buckets; + map->nbuckets = map2->nbuckets; + map->mask = map2->mask; + map->growat = map2->growat; + map->shrinkat = map2->shrinkat; + map->free(map2); + return true; +} + +static bool resize(struct hashmap *map, size_t new_cap) { + return resize0(map, new_cap); +} + +// hashmap_set_with_hash works like hashmap_set but you provide your +// own hash. The 'hash' callback provided to the hashmap_new function +// will not be called +const void *hashmap_set_with_hash(struct hashmap *map, const void *item, + uint64_t hash) +{ + hash = clip_hash(hash); + map->oom = false; + if (map->count >= map->growat) { + if (!resize(map, map->nbuckets*(1<growpower))) { + map->oom = true; + return NULL; + } + } + + struct bucket *entry = map->edata; + entry->hash = hash; + entry->dib = 1; + void *eitem = bucket_item(entry); + memcpy(eitem, item, map->elsize); + + void *bitem; + size_t i = entry->hash & map->mask; + while(1) { + struct bucket *bucket = bucket_at(map, i); + if (bucket->dib == 0) { + memcpy(bucket, entry, map->bucketsz); + map->count++; + return NULL; + } + bitem = bucket_item(bucket); + if (entry->hash == bucket->hash && (!map->compare || + map->compare(eitem, bitem, map->udata) == 0)) + { + memcpy(map->spare, bitem, map->elsize); + memcpy(bitem, eitem, map->elsize); + return map->spare; + } + if (bucket->dib < entry->dib) { + memcpy(map->spare, bucket, map->bucketsz); + memcpy(bucket, entry, map->bucketsz); + memcpy(entry, map->spare, map->bucketsz); + eitem = bucket_item(entry); + } + i = (i + 1) & map->mask; + entry->dib += 1; + } +} + +// hashmap_set inserts or replaces an item in the hash map. If an item is +// replaced then it is returned otherwise NULL is returned. This operation +// may allocate memory. If the system is unable to allocate additional +// memory then NULL is returned and hashmap_oom() returns true. +const void *hashmap_set(struct hashmap *map, const void *item) { + return hashmap_set_with_hash(map, item, get_hash(map, item)); +} + +// hashmap_get_with_hash works like hashmap_get but you provide your +// own hash. The 'hash' callback provided to the hashmap_new function +// will not be called +const void *hashmap_get_with_hash(const struct hashmap *map, const void *key, + uint64_t hash) +{ + hash = clip_hash(hash); + size_t i = hash & map->mask; + while(1) { + struct bucket *bucket = bucket_at(map, i); + if (!bucket->dib) return NULL; + if (bucket->hash == hash) { + void *bitem = bucket_item(bucket); + if (!map->compare || map->compare(key, bitem, map->udata) == 0) { + return bitem; + } + } + i = (i + 1) & map->mask; + } +} + +// hashmap_get returns the item based on the provided key. If the item is not +// found then NULL is returned. +const void *hashmap_get(const struct hashmap *map, const void *key) { + return hashmap_get_with_hash(map, key, get_hash(map, key)); +} + +// hashmap_probe returns the item in the bucket at position or NULL if an item +// is not set for that bucket. The position is 'moduloed' by the number of +// buckets in the hashmap. +const void *hashmap_probe(struct hashmap *map, uint64_t position) { + size_t i = position & map->mask; + struct bucket *bucket = bucket_at(map, i); + if (!bucket->dib) { + return NULL; + } + return bucket_item(bucket); +} + +// hashmap_delete_with_hash works like hashmap_delete but you provide your +// own hash. The 'hash' callback provided to the hashmap_new function +// will not be called +const void *hashmap_delete_with_hash(struct hashmap *map, const void *key, + uint64_t hash) +{ + hash = clip_hash(hash); + map->oom = false; + size_t i = hash & map->mask; + while(1) { + struct bucket *bucket = bucket_at(map, i); + if (!bucket->dib) { + return NULL; + } + void *bitem = bucket_item(bucket); + if (bucket->hash == hash && (!map->compare || + map->compare(key, bitem, map->udata) == 0)) + { + memcpy(map->spare, bitem, map->elsize); + bucket->dib = 0; + while(1) { + struct bucket *prev = bucket; + i = (i + 1) & map->mask; + bucket = bucket_at(map, i); + if (bucket->dib <= 1) { + prev->dib = 0; + break; + } + memcpy(prev, bucket, map->bucketsz); + prev->dib--; + } + map->count--; + if (map->nbuckets > map->cap && map->count <= map->shrinkat) { + // Ignore the return value. It's ok for the resize operation to + // fail to allocate enough memory because a shrink operation + // does not change the integrity of the data. + resize(map, map->nbuckets/2); + } + return map->spare; + } + i = (i + 1) & map->mask; + } +} + +// hashmap_delete removes an item from the hash map and returns it. If the +// item is not found then NULL is returned. +const void *hashmap_delete(struct hashmap *map, const void *key) { + return hashmap_delete_with_hash(map, key, get_hash(map, key)); +} + +// hashmap_count returns the number of items in the hash map. +size_t hashmap_count(const struct hashmap *map) { + return map->count; +} + +// hashmap_free frees the hash map +// Every item is called with the element-freeing function given in hashmap_new, +// if present, to free any data referenced in the elements of the hashmap. +void hashmap_free(struct hashmap *map) { + if (!map) return; + free_elements(map); + map->free(map->buckets); + map->free(map); +} + +// hashmap_oom returns true if the last hashmap_set() call failed due to the +// system being out of memory. +bool hashmap_oom(struct hashmap *map) { + return map->oom; +} + +// hashmap_scan iterates over all items in the hash map +// Param `iter` can return false to stop iteration early. +// Returns false if the iteration has been stopped early. +bool hashmap_scan(struct hashmap *map, + bool (*iter)(const void *item, void *udata), void *udata) +{ + for (size_t i = 0; i < map->nbuckets; i++) { + struct bucket *bucket = bucket_at(map, i); + if (bucket->dib && !iter(bucket_item(bucket), udata)) { + return false; + } + } + return true; +} + +// hashmap_iter iterates one key at a time yielding a reference to an +// entry at each iteration. Useful to write simple loops and avoid writing +// dedicated callbacks and udata structures, as in hashmap_scan. +// +// map is a hash map handle. i is a pointer to a size_t cursor that +// should be initialized to 0 at the beginning of the loop. item is a void +// pointer pointer that is populated with the retrieved item. Note that this +// is NOT a copy of the item stored in the hash map and can be directly +// modified. +// +// Note that if hashmap_delete() is called on the hashmap being iterated, +// the buckets are rearranged and the iterator must be reset to 0, otherwise +// unexpected results may be returned after deletion. +// +// This function has not been tested for thread safety. +// +// The function returns true if an item was retrieved; false if the end of the +// iteration has been reached. +bool hashmap_iter(struct hashmap *map, size_t *i, void **item) { + struct bucket *bucket; + do { + if (*i >= map->nbuckets) return false; + bucket = bucket_at(map, *i); + (*i)++; + } while (!bucket->dib); + *item = bucket_item(bucket); + return true; +} + + +//----------------------------------------------------------------------------- +// SipHash reference C implementation +// +// Copyright (c) 2012-2016 Jean-Philippe Aumasson +// +// Copyright (c) 2012-2014 Daniel J. Bernstein +// +// To the extent possible under law, the author(s) have dedicated all copyright +// and related and neighboring rights to this software to the public domain +// worldwide. This software is distributed without any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication along +// with this software. If not, see +// . +// +// default: SipHash-2-4 +//----------------------------------------------------------------------------- +static uint64_t SIP64(const uint8_t *in, const size_t inlen, uint64_t seed0, + uint64_t seed1) +{ +#define U8TO64_LE(p) \ + { (((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) | \ + ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \ + ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | \ + ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56)) } +#define U64TO8_LE(p, v) \ + { U32TO8_LE((p), (uint32_t)((v))); \ + U32TO8_LE((p) + 4, (uint32_t)((v) >> 32)); } +#define U32TO8_LE(p, v) \ + { (p)[0] = (uint8_t)((v)); \ + (p)[1] = (uint8_t)((v) >> 8); \ + (p)[2] = (uint8_t)((v) >> 16); \ + (p)[3] = (uint8_t)((v) >> 24); } +#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b)))) +#define SIPROUND \ + { v0 += v1; v1 = ROTL(v1, 13); \ + v1 ^= v0; v0 = ROTL(v0, 32); \ + v2 += v3; v3 = ROTL(v3, 16); \ + v3 ^= v2; \ + v0 += v3; v3 = ROTL(v3, 21); \ + v3 ^= v0; \ + v2 += v1; v1 = ROTL(v1, 17); \ + v1 ^= v2; v2 = ROTL(v2, 32); } + uint64_t k0 = U8TO64_LE((uint8_t*)&seed0); + uint64_t k1 = U8TO64_LE((uint8_t*)&seed1); + uint64_t v3 = UINT64_C(0x7465646279746573) ^ k1; + uint64_t v2 = UINT64_C(0x6c7967656e657261) ^ k0; + uint64_t v1 = UINT64_C(0x646f72616e646f6d) ^ k1; + uint64_t v0 = UINT64_C(0x736f6d6570736575) ^ k0; + const uint8_t *end = in + inlen - (inlen % sizeof(uint64_t)); + for (; in != end; in += 8) { + uint64_t m = U8TO64_LE(in); + v3 ^= m; + SIPROUND; SIPROUND; + v0 ^= m; + } + const int left = inlen & 7; + uint64_t b = ((uint64_t)inlen) << 56; + switch (left) { + case 7: b |= ((uint64_t)in[6]) << 48; /* fall through */ + case 6: b |= ((uint64_t)in[5]) << 40; /* fall through */ + case 5: b |= ((uint64_t)in[4]) << 32; /* fall through */ + case 4: b |= ((uint64_t)in[3]) << 24; /* fall through */ + case 3: b |= ((uint64_t)in[2]) << 16; /* fall through */ + case 2: b |= ((uint64_t)in[1]) << 8; /* fall through */ + case 1: b |= ((uint64_t)in[0]); break; + case 0: break; + } + v3 ^= b; + SIPROUND; SIPROUND; + v0 ^= b; + v2 ^= 0xff; + SIPROUND; SIPROUND; SIPROUND; SIPROUND; + b = v0 ^ v1 ^ v2 ^ v3; + uint64_t out = 0; + U64TO8_LE((uint8_t*)&out, b); + return out; +} + +//----------------------------------------------------------------------------- +// MurmurHash3 was written by Austin Appleby, and is placed in the public +// domain. The author hereby disclaims copyright to this source code. +// +// Murmur3_86_128 +//----------------------------------------------------------------------------- +static uint64_t MM86128(const void *key, const int len, uint32_t seed) { +#define ROTL32(x, r) ((x << r) | (x >> (32 - r))) +#define FMIX32(h) h^=h>>16; h*=0x85ebca6b; h^=h>>13; h*=0xc2b2ae35; h^=h>>16; + const uint8_t * data = (const uint8_t*)key; + const int nblocks = len / 16; + uint32_t h1 = seed; + uint32_t h2 = seed; + uint32_t h3 = seed; + uint32_t h4 = seed; + uint32_t c1 = 0x239b961b; + uint32_t c2 = 0xab0e9789; + uint32_t c3 = 0x38b34ae5; + uint32_t c4 = 0xa1e38b93; + const uint32_t * blocks = (const uint32_t *)(data + nblocks*16); + for (int i = -nblocks; i; i++) { + uint32_t k1 = blocks[i*4+0]; + uint32_t k2 = blocks[i*4+1]; + uint32_t k3 = blocks[i*4+2]; + uint32_t k4 = blocks[i*4+3]; + k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; + h1 = ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b; + k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2; + h2 = ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747; + k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3; + h3 = ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35; + k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4; + h4 = ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17; + } + const uint8_t * tail = (const uint8_t*)(data + nblocks*16); + uint32_t k1 = 0; + uint32_t k2 = 0; + uint32_t k3 = 0; + uint32_t k4 = 0; + switch(len & 15) { + case 15: k4 ^= tail[14] << 16; /* fall through */ + case 14: k4 ^= tail[13] << 8; /* fall through */ + case 13: k4 ^= tail[12] << 0; + k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4; + /* fall through */ + case 12: k3 ^= tail[11] << 24; /* fall through */ + case 11: k3 ^= tail[10] << 16; /* fall through */ + case 10: k3 ^= tail[ 9] << 8; /* fall through */ + case 9: k3 ^= tail[ 8] << 0; + k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3; + /* fall through */ + case 8: k2 ^= tail[ 7] << 24; /* fall through */ + case 7: k2 ^= tail[ 6] << 16; /* fall through */ + case 6: k2 ^= tail[ 5] << 8; /* fall through */ + case 5: k2 ^= tail[ 4] << 0; + k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2; + /* fall through */ + case 4: k1 ^= tail[ 3] << 24; /* fall through */ + case 3: k1 ^= tail[ 2] << 16; /* fall through */ + case 2: k1 ^= tail[ 1] << 8; /* fall through */ + case 1: k1 ^= tail[ 0] << 0; + k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; + /* fall through */ + }; + h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len; + h1 += h2; h1 += h3; h1 += h4; + h2 += h1; h3 += h1; h4 += h1; + FMIX32(h1); FMIX32(h2); FMIX32(h3); FMIX32(h4); + h1 += h2; h1 += h3; h1 += h4; + h2 += h1; h3 += h1; h4 += h1; + return (((uint64_t)h2)<<32)|h1; +} + +//----------------------------------------------------------------------------- +// xxHash Library +// Copyright (c) 2012-2021 Yann Collet +// All rights reserved. +// +// BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php) +// +// xxHash3 +//----------------------------------------------------------------------------- +#define XXH_PRIME_1 11400714785074694791ULL +#define XXH_PRIME_2 14029467366897019727ULL +#define XXH_PRIME_3 1609587929392839161ULL +#define XXH_PRIME_4 9650029242287828579ULL +#define XXH_PRIME_5 2870177450012600261ULL + +static uint64_t XXH_read64(const void* memptr) { + uint64_t val; + memcpy(&val, memptr, sizeof(val)); + return val; +} + +static uint32_t XXH_read32(const void* memptr) { + uint32_t val; + memcpy(&val, memptr, sizeof(val)); + return val; +} + +static uint64_t XXH_rotl64(uint64_t x, int r) { + return (x << r) | (x >> (64 - r)); +} + +static uint64_t xxh3(const void* data, size_t len, uint64_t seed) { + const uint8_t* p = (const uint8_t*)data; + const uint8_t* const end = p + len; + uint64_t h64; + + if (len >= 32) { + const uint8_t* const limit = end - 32; + uint64_t v1 = seed + XXH_PRIME_1 + XXH_PRIME_2; + uint64_t v2 = seed + XXH_PRIME_2; + uint64_t v3 = seed + 0; + uint64_t v4 = seed - XXH_PRIME_1; + + do { + v1 += XXH_read64(p) * XXH_PRIME_2; + v1 = XXH_rotl64(v1, 31); + v1 *= XXH_PRIME_1; + + v2 += XXH_read64(p + 8) * XXH_PRIME_2; + v2 = XXH_rotl64(v2, 31); + v2 *= XXH_PRIME_1; + + v3 += XXH_read64(p + 16) * XXH_PRIME_2; + v3 = XXH_rotl64(v3, 31); + v3 *= XXH_PRIME_1; + + v4 += XXH_read64(p + 24) * XXH_PRIME_2; + v4 = XXH_rotl64(v4, 31); + v4 *= XXH_PRIME_1; + + p += 32; + } while (p <= limit); + + h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + + XXH_rotl64(v4, 18); + + v1 *= XXH_PRIME_2; + v1 = XXH_rotl64(v1, 31); + v1 *= XXH_PRIME_1; + h64 ^= v1; + h64 = h64 * XXH_PRIME_1 + XXH_PRIME_4; + + v2 *= XXH_PRIME_2; + v2 = XXH_rotl64(v2, 31); + v2 *= XXH_PRIME_1; + h64 ^= v2; + h64 = h64 * XXH_PRIME_1 + XXH_PRIME_4; + + v3 *= XXH_PRIME_2; + v3 = XXH_rotl64(v3, 31); + v3 *= XXH_PRIME_1; + h64 ^= v3; + h64 = h64 * XXH_PRIME_1 + XXH_PRIME_4; + + v4 *= XXH_PRIME_2; + v4 = XXH_rotl64(v4, 31); + v4 *= XXH_PRIME_1; + h64 ^= v4; + h64 = h64 * XXH_PRIME_1 + XXH_PRIME_4; + } + else { + h64 = seed + XXH_PRIME_5; + } + + h64 += (uint64_t)len; + + while (p + 8 <= end) { + uint64_t k1 = XXH_read64(p); + k1 *= XXH_PRIME_2; + k1 = XXH_rotl64(k1, 31); + k1 *= XXH_PRIME_1; + h64 ^= k1; + h64 = XXH_rotl64(h64, 27) * XXH_PRIME_1 + XXH_PRIME_4; + p += 8; + } + + if (p + 4 <= end) { + h64 ^= (uint64_t)(XXH_read32(p)) * XXH_PRIME_1; + h64 = XXH_rotl64(h64, 23) * XXH_PRIME_2 + XXH_PRIME_3; + p += 4; + } + + while (p < end) { + h64 ^= (*p) * XXH_PRIME_5; + h64 = XXH_rotl64(h64, 11) * XXH_PRIME_1; + p++; + } + + h64 ^= h64 >> 33; + h64 *= XXH_PRIME_2; + h64 ^= h64 >> 29; + h64 *= XXH_PRIME_3; + h64 ^= h64 >> 32; + + return h64; +} + +// hashmap_sip returns a hash value for `data` using SipHash-2-4. +uint64_t hashmap_sip(const void *data, size_t len, uint64_t seed0, + uint64_t seed1) +{ + return SIP64((uint8_t*)data, len, seed0, seed1); +} + +// hashmap_murmur returns a hash value for `data` using Murmur3_86_128. +uint64_t hashmap_murmur(const void *data, size_t len, uint64_t seed0, + uint64_t seed1) +{ + (void)seed1; + return MM86128(data, len, seed0); +} + +uint64_t hashmap_xxhash3(const void *data, size_t len, uint64_t seed0, + uint64_t seed1) +{ + (void)seed1; + return xxh3(data, len ,seed0); +} + +//============================================================================== +// TESTS AND BENCHMARKS +// $ cc -DHASHMAP_TEST hashmap.c && ./a.out # run tests +// $ cc -DHASHMAP_TEST -O3 hashmap.c && BENCH=1 ./a.out # run benchmarks +//============================================================================== +#ifdef HASHMAP_TEST + +static size_t deepcount(struct hashmap *map) { + size_t count = 0; + for (size_t i = 0; i < map->nbuckets; i++) { + if (bucket_at(map, i)->dib) { + count++; + } + } + return count; +} + +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wpedantic" +#endif +#ifdef __clang__ +#pragma GCC diagnostic ignored "-Wunknown-warning-option" +#pragma GCC diagnostic ignored "-Wcompound-token-split-by-macro" +#pragma GCC diagnostic ignored "-Wgnu-statement-expression-from-macro-expansion" +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +#include +#include +#include +#include +#include +#include "hashmap.h" + +static bool rand_alloc_fail = false; +static int rand_alloc_fail_odds = 3; // 1 in 3 chance malloc will fail. +static uintptr_t total_allocs = 0; +static uintptr_t total_mem = 0; + +static void *xmalloc(size_t size) { + if (rand_alloc_fail && rand()%rand_alloc_fail_odds == 0) { + return NULL; + } + void *mem = malloc(sizeof(uintptr_t)+size); + assert(mem); + *(uintptr_t*)mem = size; + total_allocs++; + total_mem += size; + return (char*)mem+sizeof(uintptr_t); +} + +static void xfree(void *ptr) { + if (ptr) { + total_mem -= *(uintptr_t*)((char*)ptr-sizeof(uintptr_t)); + free((char*)ptr-sizeof(uintptr_t)); + total_allocs--; + } +} + +static void shuffle(void *array, size_t numels, size_t elsize) { + char tmp[elsize]; + char *arr = array; + for (size_t i = 0; i < numels - 1; i++) { + int j = i + rand() / (RAND_MAX / (numels - i) + 1); + memcpy(tmp, arr + j * elsize, elsize); + memcpy(arr + j * elsize, arr + i * elsize, elsize); + memcpy(arr + i * elsize, tmp, elsize); + } +} + +static bool iter_ints(const void *item, void *udata) { + int *vals = *(int**)udata; + vals[*(int*)item] = 1; + return true; +} + +static int compare_ints_udata(const void *a, const void *b, void *udata) { + return *(int*)a - *(int*)b; +} + +static int compare_strs(const void *a, const void *b, void *udata) { + return strcmp(*(char**)a, *(char**)b); +} + +static uint64_t hash_int(const void *item, uint64_t seed0, uint64_t seed1) { + return hashmap_xxhash3(item, sizeof(int), seed0, seed1); + // return hashmap_sip(item, sizeof(int), seed0, seed1); + // return hashmap_murmur(item, sizeof(int), seed0, seed1); +} + +static uint64_t hash_str(const void *item, uint64_t seed0, uint64_t seed1) { + return hashmap_xxhash3(*(char**)item, strlen(*(char**)item), seed0, seed1); + // return hashmap_sip(*(char**)item, strlen(*(char**)item), seed0, seed1); + // return hashmap_murmur(*(char**)item, strlen(*(char**)item), seed0, seed1); +} + +static void free_str(void *item) { + xfree(*(char**)item); +} + +static void all(void) { + int seed = getenv("SEED")?atoi(getenv("SEED")):time(NULL); + int N = getenv("N")?atoi(getenv("N")):2000; + printf("seed=%d, count=%d, item_size=%zu\n", seed, N, sizeof(int)); + srand(seed); + + rand_alloc_fail = true; + + // test sip and murmur hashes + assert(hashmap_sip("hello", 5, 1, 2) == 2957200328589801622); + assert(hashmap_murmur("hello", 5, 1, 2) == 1682575153221130884); + assert(hashmap_xxhash3("hello", 5, 1, 2) == 2584346877953614258); + + int *vals; + while (!(vals = xmalloc(N * sizeof(int)))) {} + for (int i = 0; i < N; i++) { + vals[i] = i; + } + + struct hashmap *map; + + while (!(map = hashmap_new(sizeof(int), 0, seed, seed, + hash_int, compare_ints_udata, NULL, NULL))) {} + shuffle(vals, N, sizeof(int)); + for (int i = 0; i < N; i++) { + // // printf("== %d ==\n", vals[i]); + assert(map->count == (size_t)i); + assert(map->count == hashmap_count(map)); + assert(map->count == deepcount(map)); + const int *v; + assert(!hashmap_get(map, &vals[i])); + assert(!hashmap_delete(map, &vals[i])); + while (true) { + assert(!hashmap_set(map, &vals[i])); + if (!hashmap_oom(map)) { + break; + } + } + + for (int j = 0; j < i; j++) { + v = hashmap_get(map, &vals[j]); + assert(v && *v == vals[j]); + } + while (true) { + v = hashmap_set(map, &vals[i]); + if (!v) { + assert(hashmap_oom(map)); + continue; + } else { + assert(!hashmap_oom(map)); + assert(v && *v == vals[i]); + break; + } + } + v = hashmap_get(map, &vals[i]); + assert(v && *v == vals[i]); + v = hashmap_delete(map, &vals[i]); + assert(v && *v == vals[i]); + assert(!hashmap_get(map, &vals[i])); + assert(!hashmap_delete(map, &vals[i])); + assert(!hashmap_set(map, &vals[i])); + assert(map->count == (size_t)(i+1)); + assert(map->count == hashmap_count(map)); + assert(map->count == deepcount(map)); + } + + int *vals2; + while (!(vals2 = xmalloc(N * sizeof(int)))) {} + memset(vals2, 0, N * sizeof(int)); + assert(hashmap_scan(map, iter_ints, &vals2)); + + // Test hashmap_iter. This does the same as hashmap_scan above. + size_t iter = 0; + void *iter_val; + while (hashmap_iter (map, &iter, &iter_val)) { + assert (iter_ints(iter_val, &vals2)); + } + for (int i = 0; i < N; i++) { + assert(vals2[i] == 1); + } + xfree(vals2); + + shuffle(vals, N, sizeof(int)); + for (int i = 0; i < N; i++) { + const int *v; + v = hashmap_delete(map, &vals[i]); + assert(v && *v == vals[i]); + assert(!hashmap_get(map, &vals[i])); + assert(map->count == (size_t)(N-i-1)); + assert(map->count == hashmap_count(map)); + assert(map->count == deepcount(map)); + for (int j = N-1; j > i; j--) { + v = hashmap_get(map, &vals[j]); + assert(v && *v == vals[j]); + } + } + + for (int i = 0; i < N; i++) { + while (true) { + assert(!hashmap_set(map, &vals[i])); + if (!hashmap_oom(map)) { + break; + } + } + } + + assert(map->count != 0); + size_t prev_cap = map->cap; + hashmap_clear(map, true); + assert(prev_cap < map->cap); + assert(map->count == 0); + + + for (int i = 0; i < N; i++) { + while (true) { + assert(!hashmap_set(map, &vals[i])); + if (!hashmap_oom(map)) { + break; + } + } + } + + prev_cap = map->cap; + hashmap_clear(map, false); + assert(prev_cap == map->cap); + + hashmap_free(map); + + xfree(vals); + + + while (!(map = hashmap_new(sizeof(char*), 0, seed, seed, + hash_str, compare_strs, free_str, NULL))); + + for (int i = 0; i < N; i++) { + char *str; + while (!(str = xmalloc(16))); + snprintf(str, 16, "s%i", i); + while(!hashmap_set(map, &str)); + } + + hashmap_clear(map, false); + assert(hashmap_count(map) == 0); + + for (int i = 0; i < N; i++) { + char *str; + while (!(str = xmalloc(16))); + snprintf(str, 16, "s%i", i); + while(!hashmap_set(map, &str)); + } + + hashmap_free(map); + + if (total_allocs != 0) { + fprintf(stderr, "total_allocs: expected 0, got %lu\n", total_allocs); + exit(1); + } +} + +#define bench(name, N, code) {{ \ + if (strlen(name) > 0) { \ + printf("%-14s ", name); \ + } \ + size_t tmem = total_mem; \ + size_t tallocs = total_allocs; \ + uint64_t bytes = 0; \ + clock_t begin = clock(); \ + for (int i = 0; i < N; i++) { \ + (code); \ + } \ + clock_t end = clock(); \ + double elapsed_secs = (double)(end - begin) / CLOCKS_PER_SEC; \ + double bytes_sec = (double)bytes/elapsed_secs; \ + printf("%d ops in %.3f secs, %.0f ns/op, %.0f op/sec", \ + N, elapsed_secs, \ + elapsed_secs/(double)N*1e9, \ + (double)N/elapsed_secs \ + ); \ + if (bytes > 0) { \ + printf(", %.1f GB/sec", bytes_sec/1024/1024/1024); \ + } \ + if (total_mem > tmem) { \ + size_t used_mem = total_mem-tmem; \ + printf(", %.2f bytes/op", (double)used_mem/N); \ + } \ + if (total_allocs > tallocs) { \ + size_t used_allocs = total_allocs-tallocs; \ + printf(", %.2f allocs/op", (double)used_allocs/N); \ + } \ + printf("\n"); \ +}} + +static void benchmarks(void) { + int seed = getenv("SEED")?atoi(getenv("SEED")):time(NULL); + int N = getenv("N")?atoi(getenv("N")):5000000; + printf("seed=%d, count=%d, item_size=%zu\n", seed, N, sizeof(int)); + srand(seed); + + + int *vals = xmalloc(N * sizeof(int)); + for (int i = 0; i < N; i++) { + vals[i] = i; + } + + shuffle(vals, N, sizeof(int)); + + struct hashmap *map; + shuffle(vals, N, sizeof(int)); + + map = hashmap_new(sizeof(int), 0, seed, seed, hash_int, compare_ints_udata, + NULL, NULL); + bench("set", N, { + const int *v = hashmap_set(map, &vals[i]); + assert(!v); + }) + shuffle(vals, N, sizeof(int)); + bench("get", N, { + const int *v = hashmap_get(map, &vals[i]); + assert(v && *v == vals[i]); + }) + shuffle(vals, N, sizeof(int)); + bench("delete", N, { + const int *v = hashmap_delete(map, &vals[i]); + assert(v && *v == vals[i]); + }) + hashmap_free(map); + + map = hashmap_new(sizeof(int), N, seed, seed, hash_int, compare_ints_udata, + NULL, NULL); + bench("set (cap)", N, { + const int *v = hashmap_set(map, &vals[i]); + assert(!v); + }) + shuffle(vals, N, sizeof(int)); + bench("get (cap)", N, { + const int *v = hashmap_get(map, &vals[i]); + assert(v && *v == vals[i]); + }) + shuffle(vals, N, sizeof(int)); + bench("delete (cap)" , N, { + const int *v = hashmap_delete(map, &vals[i]); + assert(v && *v == vals[i]); + }) + + hashmap_free(map); + + + xfree(vals); + + if (total_allocs != 0) { + fprintf(stderr, "total_allocs: expected 0, got %lu\n", total_allocs); + exit(1); + } +} + +int main(void) { + hashmap_set_allocator(xmalloc, xfree); + + if (getenv("BENCH")) { + printf("Running hashmap.c benchmarks...\n"); + benchmarks(); + } else { + printf("Running hashmap.c tests...\n"); + all(); + printf("PASSED\n"); + } +} + + +#endif + + + diff --git a/source_code/external_code/hash_map/hashmap.h b/source_code/external_code/hash_map/hashmap.h new file mode 100644 index 0000000..4898ab0 --- /dev/null +++ b/source_code/external_code/hash_map/hashmap.h @@ -0,0 +1,62 @@ +// Copyright 2020 Joshua J Baker. All rights reserved. +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +#ifndef HASHMAP_H +#define HASHMAP_H + +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif // __cplusplus + +struct hashmap; + +struct hashmap *hashmap_new(size_t elsize, size_t cap, uint64_t seed0, + uint64_t seed1, + uint64_t (*hash)(const void *item, uint64_t seed0, uint64_t seed1), + int (*compare)(const void *a, const void *b, void *udata), + void (*elfree)(void *item), + void *udata); + +struct hashmap *hashmap_new_with_allocator(void *(*malloc)(size_t), + void *(*realloc)(void *, size_t), void (*free)(void*), size_t elsize, + size_t cap, uint64_t seed0, uint64_t seed1, + uint64_t (*hash)(const void *item, uint64_t seed0, uint64_t seed1), + int (*compare)(const void *a, const void *b, void *udata), + void (*elfree)(void *item), + void *udata); + +void hashmap_free(struct hashmap *map); +void hashmap_clear(struct hashmap *map, bool update_cap); +size_t hashmap_count(const struct hashmap *map); +bool hashmap_oom(struct hashmap *map); +const void *hashmap_get(const struct hashmap *map, const void *item); +const void *hashmap_set(struct hashmap *map, const void *item); +const void *hashmap_delete(struct hashmap *map, const void *item); +const void *hashmap_probe(struct hashmap *map, uint64_t position); +bool hashmap_scan(struct hashmap *map, bool (*iter)(const void *item, void *udata), void *udata); +bool hashmap_iter(struct hashmap *map, size_t *i, void **item); + +uint64_t hashmap_sip(const void *data, size_t len, uint64_t seed0, uint64_t seed1); +uint64_t hashmap_murmur(const void *data, size_t len, uint64_t seed0, uint64_t seed1); +uint64_t hashmap_xxhash3(const void *data, size_t len, uint64_t seed0, uint64_t seed1); + +const void *hashmap_get_with_hash(const struct hashmap *map, const void *key, uint64_t hash); +const void *hashmap_delete_with_hash(struct hashmap *map, const void *key, uint64_t hash); +const void *hashmap_set_with_hash(struct hashmap *map, const void *item, uint64_t hash); +void hashmap_set_grow_by_power(struct hashmap *map, size_t power); +void hashmap_set_load_factor(struct hashmap *map, double load_factor); + + +// DEPRECATED: use `hashmap_new_with_allocator` +void hashmap_set_allocator(void *(*malloc)(size_t), void (*free)(void*)); + +#if defined(__cplusplus) +} +#endif // __cplusplus + +#endif // HASHMAP_H diff --git a/source_code/external_code/perlin/perlin.c b/source_code/external_code/perlin/perlin.c new file mode 100644 index 0000000..21664fc --- /dev/null +++ b/source_code/external_code/perlin/perlin.c @@ -0,0 +1 @@ +#include "perlin.h" diff --git a/source_code/external_code/perlin/perlin.h b/source_code/external_code/perlin/perlin.h new file mode 100644 index 0000000..d835a02 --- /dev/null +++ b/source_code/external_code/perlin/perlin.h @@ -0,0 +1,429 @@ +// stb_perlin.h - v0.5 - perlin noise +// public domain single-file C implementation by Sean Barrett +// +// LICENSE +// +// See end of file. +// +// +// to create the implementation, +// #define STB_PERLIN_IMPLEMENTATION +// in *one* C/CPP file that includes this file. +// +// +// Documentation: +// +// float stb_perlin_noise3( float x, +// float y, +// float z, +// int x_wrap=0, +// int y_wrap=0, +// int z_wrap=0) +// +// This function computes a random value at the coordinate (x,y,z). +// Adjacent random values are continuous but the noise fluctuates +// its randomness with period 1, i.e. takes on wholly unrelated values +// at integer points. Specifically, this implements Ken Perlin's +// revised noise function from 2002. +// +// The "wrap" parameters can be used to create wraparound noise that +// wraps at powers of two. The numbers MUST be powers of two. Specify +// 0 to mean "don't care". (The noise always wraps every 256 due +// details of the implementation, even if you ask for larger or no +// wrapping.) +// +// float stb_perlin_noise3_seed( float x, +// float y, +// float z, +// int x_wrap=0, +// int y_wrap=0, +// int z_wrap=0, +// int seed) +// +// As above, but 'seed' selects from multiple different variations of the +// noise function. The current implementation only uses the bottom 8 bits +// of 'seed', but possibly in the future more bits will be used. +// +// +// Fractal Noise: +// +// Three common fractal noise functions are included, which produce +// a wide variety of nice effects depending on the parameters +// provided. Note that each function will call stb_perlin_noise3 +// 'octaves' times, so this parameter will affect runtime. +// +// float stb_perlin_ridge_noise3(float x, float y, float z, +// float lacunarity, float gain, float offset, int octaves) +// +// float stb_perlin_fbm_noise3(float x, float y, float z, +// float lacunarity, float gain, int octaves) +// +// float stb_perlin_turbulence_noise3(float x, float y, float z, +// float lacunarity, float gain, int octaves) +// +// Typical values to start playing with: +// octaves = 6 -- number of "octaves" of noise3() to sum +// lacunarity = ~ 2.0 -- spacing between successive octaves (use exactly 2.0 for wrapping output) +// gain = 0.5 -- relative weighting applied to each successive octave +// offset = 1.0? -- used to invert the ridges, may need to be larger, not sure +// +// +// Contributors: +// Jack Mott - additional noise functions +// Jordan Peck - seeded noise +// + + +#ifdef __cplusplus +extern "C" { +#endif +extern float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap); +extern float stb_perlin_noise3_seed(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, int seed); +extern float stb_perlin_ridge_noise3(float x, float y, float z, float lacunarity, float gain, float offset, int octaves); +extern float stb_perlin_fbm_noise3(float x, float y, float z, float lacunarity, float gain, int octaves); +extern float stb_perlin_turbulence_noise3(float x, float y, float z, float lacunarity, float gain, int octaves); +extern float stb_perlin_noise3_wrap_nonpow2(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, unsigned char seed); +#ifdef __cplusplus +} +#endif + +#ifdef STB_PERLIN_IMPLEMENTATION + +#include // fabs() + +// not same permutation table as Perlin's reference to avoid copyright issues; +// Perlin's table can be found at http://mrl.nyu.edu/~perlin/noise/ +static unsigned char stb__perlin_randtab[512] = +{ + 23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123, + 152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72, + 175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240, + 8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57, + 225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233, + 94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172, + 165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243, + 65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122, + 26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76, + 250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246, + 132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3, + 91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231, + 38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221, + 131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62, + 27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135, + 61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5, + + // and a second copy so we don't need an extra mask or static initializer + 23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123, + 152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72, + 175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240, + 8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57, + 225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233, + 94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172, + 165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243, + 65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122, + 26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76, + 250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246, + 132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3, + 91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231, + 38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221, + 131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62, + 27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135, + 61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5, +}; + + +// perlin's gradient has 12 cases so some get used 1/16th of the time +// and some 2/16ths. We reduce bias by changing those fractions +// to 5/64ths and 6/64ths + +// this array is designed to match the previous implementation +// of gradient hash: indices[stb__perlin_randtab[i]&63] +static unsigned char stb__perlin_randtab_grad_idx[512] = +{ + 7, 9, 5, 0, 11, 1, 6, 9, 3, 9, 11, 1, 8, 10, 4, 7, + 8, 6, 1, 5, 3, 10, 9, 10, 0, 8, 4, 1, 5, 2, 7, 8, + 7, 11, 9, 10, 1, 0, 4, 7, 5, 0, 11, 6, 1, 4, 2, 8, + 8, 10, 4, 9, 9, 2, 5, 7, 9, 1, 7, 2, 2, 6, 11, 5, + 5, 4, 6, 9, 0, 1, 1, 0, 7, 6, 9, 8, 4, 10, 3, 1, + 2, 8, 8, 9, 10, 11, 5, 11, 11, 2, 6, 10, 3, 4, 2, 4, + 9, 10, 3, 2, 6, 3, 6, 10, 5, 3, 4, 10, 11, 2, 9, 11, + 1, 11, 10, 4, 9, 4, 11, 0, 4, 11, 4, 0, 0, 0, 7, 6, + 10, 4, 1, 3, 11, 5, 3, 4, 2, 9, 1, 3, 0, 1, 8, 0, + 6, 7, 8, 7, 0, 4, 6, 10, 8, 2, 3, 11, 11, 8, 0, 2, + 4, 8, 3, 0, 0, 10, 6, 1, 2, 2, 4, 5, 6, 0, 1, 3, + 11, 9, 5, 5, 9, 6, 9, 8, 3, 8, 1, 8, 9, 6, 9, 11, + 10, 7, 5, 6, 5, 9, 1, 3, 7, 0, 2, 10, 11, 2, 6, 1, + 3, 11, 7, 7, 2, 1, 7, 3, 0, 8, 1, 1, 5, 0, 6, 10, + 11, 11, 0, 2, 7, 0, 10, 8, 3, 5, 7, 1, 11, 1, 0, 7, + 9, 0, 11, 5, 10, 3, 2, 3, 5, 9, 7, 9, 8, 4, 6, 5, + + // and a second copy so we don't need an extra mask or static initializer + 7, 9, 5, 0, 11, 1, 6, 9, 3, 9, 11, 1, 8, 10, 4, 7, + 8, 6, 1, 5, 3, 10, 9, 10, 0, 8, 4, 1, 5, 2, 7, 8, + 7, 11, 9, 10, 1, 0, 4, 7, 5, 0, 11, 6, 1, 4, 2, 8, + 8, 10, 4, 9, 9, 2, 5, 7, 9, 1, 7, 2, 2, 6, 11, 5, + 5, 4, 6, 9, 0, 1, 1, 0, 7, 6, 9, 8, 4, 10, 3, 1, + 2, 8, 8, 9, 10, 11, 5, 11, 11, 2, 6, 10, 3, 4, 2, 4, + 9, 10, 3, 2, 6, 3, 6, 10, 5, 3, 4, 10, 11, 2, 9, 11, + 1, 11, 10, 4, 9, 4, 11, 0, 4, 11, 4, 0, 0, 0, 7, 6, + 10, 4, 1, 3, 11, 5, 3, 4, 2, 9, 1, 3, 0, 1, 8, 0, + 6, 7, 8, 7, 0, 4, 6, 10, 8, 2, 3, 11, 11, 8, 0, 2, + 4, 8, 3, 0, 0, 10, 6, 1, 2, 2, 4, 5, 6, 0, 1, 3, + 11, 9, 5, 5, 9, 6, 9, 8, 3, 8, 1, 8, 9, 6, 9, 11, + 10, 7, 5, 6, 5, 9, 1, 3, 7, 0, 2, 10, 11, 2, 6, 1, + 3, 11, 7, 7, 2, 1, 7, 3, 0, 8, 1, 1, 5, 0, 6, 10, + 11, 11, 0, 2, 7, 0, 10, 8, 3, 5, 7, 1, 11, 1, 0, 7, + 9, 0, 11, 5, 10, 3, 2, 3, 5, 9, 7, 9, 8, 4, 6, 5, +}; + +static float stb__perlin_lerp(float a, float b, float t) +{ + return a + (b-a) * t; +} + +static int stb__perlin_fastfloor(float a) +{ + int ai = (int) a; + return (a < ai) ? ai-1 : ai; +} + +// different grad function from Perlin's, but easy to modify to match reference +static float stb__perlin_grad(int grad_idx, float x, float y, float z) +{ + static float basis[12][4] = + { + { 1, 1, 0 }, + { -1, 1, 0 }, + { 1,-1, 0 }, + { -1,-1, 0 }, + { 1, 0, 1 }, + { -1, 0, 1 }, + { 1, 0,-1 }, + { -1, 0,-1 }, + { 0, 1, 1 }, + { 0,-1, 1 }, + { 0, 1,-1 }, + { 0,-1,-1 }, + }; + + float *grad = basis[grad_idx]; + return grad[0]*x + grad[1]*y + grad[2]*z; +} + +float stb_perlin_noise3_internal(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, unsigned char seed) +{ + float u,v,w; + float n000,n001,n010,n011,n100,n101,n110,n111; + float n00,n01,n10,n11; + float n0,n1; + + unsigned int x_mask = (x_wrap-1) & 255; + unsigned int y_mask = (y_wrap-1) & 255; + unsigned int z_mask = (z_wrap-1) & 255; + int px = stb__perlin_fastfloor(x); + int py = stb__perlin_fastfloor(y); + int pz = stb__perlin_fastfloor(z); + int x0 = px & x_mask, x1 = (px+1) & x_mask; + int y0 = py & y_mask, y1 = (py+1) & y_mask; + int z0 = pz & z_mask, z1 = (pz+1) & z_mask; + int r0,r1, r00,r01,r10,r11; + + #define stb__perlin_ease(a) (((a*6-15)*a + 10) * a * a * a) + + x -= px; u = stb__perlin_ease(x); + y -= py; v = stb__perlin_ease(y); + z -= pz; w = stb__perlin_ease(z); + + r0 = stb__perlin_randtab[x0+seed]; + r1 = stb__perlin_randtab[x1+seed]; + + r00 = stb__perlin_randtab[r0+y0]; + r01 = stb__perlin_randtab[r0+y1]; + r10 = stb__perlin_randtab[r1+y0]; + r11 = stb__perlin_randtab[r1+y1]; + + n000 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z0], x , y , z ); + n001 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z1], x , y , z-1 ); + n010 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z0], x , y-1, z ); + n011 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z1], x , y-1, z-1 ); + n100 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z0], x-1, y , z ); + n101 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z1], x-1, y , z-1 ); + n110 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z0], x-1, y-1, z ); + n111 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z1], x-1, y-1, z-1 ); + + n00 = stb__perlin_lerp(n000,n001,w); + n01 = stb__perlin_lerp(n010,n011,w); + n10 = stb__perlin_lerp(n100,n101,w); + n11 = stb__perlin_lerp(n110,n111,w); + + n0 = stb__perlin_lerp(n00,n01,v); + n1 = stb__perlin_lerp(n10,n11,v); + + return stb__perlin_lerp(n0,n1,u); +} + +float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap) +{ + return stb_perlin_noise3_internal(x,y,z,x_wrap,y_wrap,z_wrap,0); +} + +float stb_perlin_noise3_seed(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, int seed) +{ + return stb_perlin_noise3_internal(x,y,z,x_wrap,y_wrap,z_wrap, (unsigned char) seed); +} + +float stb_perlin_ridge_noise3(float x, float y, float z, float lacunarity, float gain, float offset, int octaves) +{ + int i; + float frequency = 1.0f; + float prev = 1.0f; + float amplitude = 0.5f; + float sum = 0.0f; + + for (i = 0; i < octaves; i++) { + float r = stb_perlin_noise3_internal(x*frequency,y*frequency,z*frequency,0,0,0,(unsigned char)i); + r = offset - (float) fabs(r); + r = r*r; + sum += r*amplitude*prev; + prev = r; + frequency *= lacunarity; + amplitude *= gain; + } + return sum; +} + +float stb_perlin_fbm_noise3(float x, float y, float z, float lacunarity, float gain, int octaves) +{ + int i; + float frequency = 1.0f; + float amplitude = 1.0f; + float sum = 0.0f; + + for (i = 0; i < octaves; i++) { + sum += stb_perlin_noise3_internal(x*frequency,y*frequency,z*frequency,0,0,0,(unsigned char)i)*amplitude; + frequency *= lacunarity; + amplitude *= gain; + } + return sum; +} + +float stb_perlin_turbulence_noise3(float x, float y, float z, float lacunarity, float gain, int octaves) +{ + int i; + float frequency = 1.0f; + float amplitude = 1.0f; + float sum = 0.0f; + + for (i = 0; i < octaves; i++) { + float r = stb_perlin_noise3_internal(x*frequency,y*frequency,z*frequency,0,0,0,(unsigned char)i)*amplitude; + sum += (float) fabs(r); + frequency *= lacunarity; + amplitude *= gain; + } + return sum; +} + +float stb_perlin_noise3_wrap_nonpow2(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, unsigned char seed) +{ + float u,v,w; + float n000,n001,n010,n011,n100,n101,n110,n111; + float n00,n01,n10,n11; + float n0,n1; + + int px = stb__perlin_fastfloor(x); + int py = stb__perlin_fastfloor(y); + int pz = stb__perlin_fastfloor(z); + int x_wrap2 = (x_wrap ? x_wrap : 256); + int y_wrap2 = (y_wrap ? y_wrap : 256); + int z_wrap2 = (z_wrap ? z_wrap : 256); + int x0 = px % x_wrap2, x1; + int y0 = py % y_wrap2, y1; + int z0 = pz % z_wrap2, z1; + int r0,r1, r00,r01,r10,r11; + + if (x0 < 0) x0 += x_wrap2; + if (y0 < 0) y0 += y_wrap2; + if (z0 < 0) z0 += z_wrap2; + x1 = (x0+1) % x_wrap2; + y1 = (y0+1) % y_wrap2; + z1 = (z0+1) % z_wrap2; + + #define stb__perlin_ease(a) (((a*6-15)*a + 10) * a * a * a) + + x -= px; u = stb__perlin_ease(x); + y -= py; v = stb__perlin_ease(y); + z -= pz; w = stb__perlin_ease(z); + + r0 = stb__perlin_randtab[x0]; + r0 = stb__perlin_randtab[r0+seed]; + r1 = stb__perlin_randtab[x1]; + r1 = stb__perlin_randtab[r1+seed]; + + r00 = stb__perlin_randtab[r0+y0]; + r01 = stb__perlin_randtab[r0+y1]; + r10 = stb__perlin_randtab[r1+y0]; + r11 = stb__perlin_randtab[r1+y1]; + + n000 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z0], x , y , z ); + n001 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z1], x , y , z-1 ); + n010 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z0], x , y-1, z ); + n011 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z1], x , y-1, z-1 ); + n100 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z0], x-1, y , z ); + n101 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z1], x-1, y , z-1 ); + n110 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z0], x-1, y-1, z ); + n111 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z1], x-1, y-1, z-1 ); + + n00 = stb__perlin_lerp(n000,n001,w); + n01 = stb__perlin_lerp(n010,n011,w); + n10 = stb__perlin_lerp(n100,n101,w); + n11 = stb__perlin_lerp(n110,n111,w); + + n0 = stb__perlin_lerp(n00,n01,v); + n1 = stb__perlin_lerp(n10,n11,v); + + return stb__perlin_lerp(n0,n1,u); +} +#endif // STB_PERLIN_IMPLEMENTATION + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +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. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +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. +------------------------------------------------------------------------------ +*/ + diff --git a/source_code/frustum_module/frustum/_internal/private_frustum.h b/source_code/frustum_module/frustum/_internal/private_frustum.h new file mode 100644 index 0000000..b393978 --- /dev/null +++ b/source_code/frustum_module/frustum/_internal/private_frustum.h @@ -0,0 +1,13 @@ +#ifndef private_frustum_h +#define private_frustum_h + +enum { + left = 0, + right = 1, + bottom = 2, + top = 3, + near = 4, + far = 5 +}; + +#endif diff --git a/source_code/frustum_module/frustum/frustum.c b/source_code/frustum_module/frustum/frustum.c new file mode 100644 index 0000000..361f727 --- /dev/null +++ b/source_code/frustum_module/frustum/frustum.c @@ -0,0 +1,112 @@ +#include "frustum.h" +#include "_internal/private_frustum.h" + +#include +#include + +/** + * Extracts the six clipping planes from a combined View-Projection matrix. + * * This function utilizes the Gribb-Hartmann method to derive plane equations + * directly from the matrix rows. The resulting planes are normalized to ensure + * that the distance calculations in visibility tests are accurate. + */ +void extract_frustum_planes(frustum_plane_struct planes[number_of_planes], + Matrix view_projection) +{ + /* Left Clipping Plane */ + planes[left].normal.x = view_projection.m3 + view_projection.m0; + planes[left].normal.y = view_projection.m7 + view_projection.m4; + planes[left].normal.z = view_projection.m11 + view_projection.m8; + planes[left].distance = view_projection.m15 + view_projection.m12; + + /* Right Clipping Plane */ + planes[right].normal.x = view_projection.m3 - view_projection.m0; + planes[right].normal.y = view_projection.m7 - view_projection.m4; + planes[right].normal.z = view_projection.m11 - view_projection.m8; + planes[right].distance = view_projection.m15 - view_projection.m12; + + /* Bottom Clipping Plane */ + planes[bottom].normal.x = view_projection.m3 + view_projection.m1; + planes[bottom].normal.y = view_projection.m7 + view_projection.m5; + planes[bottom].normal.z = view_projection.m11 + view_projection.m9; + planes[bottom].distance = view_projection.m15 + view_projection.m13; + + /* Top Clipping Plane */ + planes[top].normal.x = view_projection.m3 - view_projection.m1; + planes[top].normal.y = view_projection.m7 - view_projection.m5; + planes[top].normal.z = view_projection.m11 - view_projection.m9; + planes[top].distance = view_projection.m15 - view_projection.m13; + + /* Near Clipping Plane */ + planes[near].normal.x = view_projection.m3 + view_projection.m2; + planes[near].normal.y = view_projection.m7 + view_projection.m6; + planes[near].normal.z = view_projection.m11 + view_projection.m10; + planes[near].distance = view_projection.m15 + view_projection.m14; + + /* Far Clipping Plane */ + planes[far].normal.x = view_projection.m3 - view_projection.m2; + planes[far].normal.y = view_projection.m7 - view_projection.m6; + planes[far].normal.z = view_projection.m11 - view_projection.m10; + planes[far].distance = view_projection.m15 - view_projection.m14; + + /* Normalize all planes so that normal length equals 1.0 */ + for (uint8_t plane_index = 0; plane_index < number_of_planes; plane_index++) + { + float magnitude = sqrtf( + planes[plane_index].normal.x * planes[plane_index].normal.x + + planes[plane_index].normal.y * planes[plane_index].normal.y + + planes[plane_index].normal.z * planes[plane_index].normal.z + ); + + if (magnitude > 0.0f) + { + float inverse_magnitude = 1.0f / magnitude; + planes[plane_index].normal.x *= inverse_magnitude; + planes[plane_index].normal.y *= inverse_magnitude; + planes[plane_index].normal.z *= inverse_magnitude; + planes[plane_index].distance *= inverse_magnitude; + } + } +} + +/** + * Checks if an Axis-Aligned Bounding Box (AABB) is within the view frustum. + * * This function performs a fast visibility test by finding the "positive vertex" + * of the box (the corner most aligned with the plane normal). If this vertex + * is outside any of the six planes, the entire box is considered culled. + */ +bool is_axis_aligned_bounding_box_in_frustum( const frustum_plane_struct planes + [ + number_of_planes + ], + BoundingBox bounds) +{ + for (uint8_t plane_index = 0; plane_index < number_of_planes; plane_index++) + { + Vector3 positive_vertex; + + positive_vertex.x = + (planes[plane_index].normal.x >= 0.0f) ? bounds.max.x : bounds.min.x; + + positive_vertex.y = + (planes[plane_index].normal.y >= 0.0f) ? bounds.max.y : bounds.min.y; + + positive_vertex.z = + (planes[plane_index].normal.z >= 0.0f) ? bounds.max.z : bounds.min.z; + + /* Calculate the signed distance from the plane to the positive vertex */ + float vertex_distance = + planes[plane_index].normal.x * positive_vertex.x + + planes[plane_index].normal.y * positive_vertex.y + + planes[plane_index].normal.z * positive_vertex.z + + planes[plane_index].distance; + + /* If the most positive point is behind the plane, the box is outside the frustum */ + if (vertex_distance < 0.0f) + { + return false; + } + } + + return true; +} diff --git a/source_code/frustum_module/frustum/frustum.h b/source_code/frustum_module/frustum/frustum.h new file mode 100644 index 0000000..c4a839c --- /dev/null +++ b/source_code/frustum_module/frustum/frustum.h @@ -0,0 +1,38 @@ +#ifndef frustum_h +#define frustum_h + +#include "raylib.h" +#include + +#define number_of_planes 6 + +typedef struct frustum_plane_struct +{ + Vector3 normal; + float distance; +} frustum_plane_struct; + +/** + * Extracts the six clipping planes from a combined View-Projection matrix. + * * This function utilizes the Gribb-Hartmann method to derive plane equations + * directly from the matrix rows. The resulting planes are normalized to ensure + * that the distance calculations in visibility tests are accurate. + */ +void extract_frustum_planes(frustum_plane_struct planes[number_of_planes], + Matrix view_projection); + + +/** + * Checks if an Axis-Aligned Bounding Box (AABB) is within the view frustum. + * * This function performs a fast visibility test by finding the "positive vertex" + * of the box (the corner most aligned with the plane normal). If this vertex + * is outside any of the six planes, the entire box is considered culled. + */ +bool is_axis_aligned_bounding_box_in_frustum( const frustum_plane_struct planes + [ + number_of_planes + ], + BoundingBox bounds); + +#endif + diff --git a/source_code/frustum_module/frustum/tests/test_frustum.c b/source_code/frustum_module/frustum/tests/test_frustum.c new file mode 100644 index 0000000..baf5e82 --- /dev/null +++ b/source_code/frustum_module/frustum/tests/test_frustum.c @@ -0,0 +1,112 @@ +#include "test_frustum.h" +#include "frustum.h" +#include "_internal/private_frustum.h" + +#include "unity.h" + +#include "raylib.h" +#include "raymath.h" + +/* Verify that an identity matrix results in planes that face inward correctly */ +void test_extract_frustum_planes_from_identity_matrix(void) +{ + frustum_plane_struct planes[number_of_planes]; + Matrix identity_matrix = MatrixIdentity(); + + extract_frustum_planes(planes, identity_matrix); + + /* With an identity matrix, the 'Left' plane normal should point along +X + * because the plane equation is Ax + By + Cz + D = 0 and it faces inward. + */ + TEST_ASSERT_FLOAT_WITHIN(0.0001f, 1.0f, planes[left].normal.x); + TEST_ASSERT_FLOAT_WITHIN(0.0001f, 0.0f, planes[left].normal.y); + TEST_ASSERT_FLOAT_WITHIN(0.0001f, 0.0f, planes[left].normal.z); +} + +/* Verify that a box inside the view area is correctly identified as visible */ +void test_bounding_box_inside_frustum_is_not_culled(void) +{ + frustum_plane_struct planes[number_of_planes]; + + /* Create a standard perspective projection matrix + * (FOV: 90, Aspect: 1.0, Near: 0.1, Far: 100.0) + */ + Matrix projection = MatrixPerspective(90.0f * DEG2RAD, 1.0f, 0.1f, 100.0f); + + Matrix view = MatrixLookAt((Vector3){ 0, 0, 0 }, + (Vector3){ 0, 0, -1 }, + (Vector3){ 0, 1, 0 }); + + Matrix view_projection = MatrixMultiply(view, projection); + + extract_frustum_planes(planes, view_projection); + + /* Define a box directly in front of the camera (negative Z axis) */ + BoundingBox box_inside; + box_inside.min = (Vector3){ -1.0f, -1.0f, -10.0f }; + box_inside.max = (Vector3){ 1.0f, 1.0f, -5.0f }; + + TEST_ASSERT_TRUE_MESSAGE(is_axis_aligned_bounding_box_in_frustum(planes, + box_inside), + "Box directly in front of camera should be visible"); +} + +/* Verify that a box behind the camera is correctly culled */ +void test_bounding_box_behind_camera_is_culled(void) +{ + frustum_plane_struct planes[number_of_planes]; + + Matrix projection = MatrixPerspective(90.0f * DEG2RAD, 1.0f, 0.1f, 100.0f); + + Matrix view = MatrixLookAt((Vector3){ 0, 0, 0 }, + (Vector3){ 0, 0, -1 }, + (Vector3){ 0, 1, 0 }); + + Matrix view_projection = MatrixMultiply(view, projection); + + extract_frustum_planes(planes, view_projection); + + /* Define a box behind the camera (positive Z axis) */ + BoundingBox box_behind; + box_behind.min = (Vector3){ -1.0f, -1.0f, 5.0f }; + box_behind.max = (Vector3){ 1.0f, 1.0f, 10.0f }; + + TEST_ASSERT_FALSE_MESSAGE(is_axis_aligned_bounding_box_in_frustum(planes, + box_behind), + "Box behind the camera should be culled"); +} + + + +/* Verify that a box far beyond the far clipping plane is culled */ +void test_bounding_box_beyond_far_plane_is_culled(void) +{ + frustum_plane_struct planes[number_of_planes]; + + /* Far plane is set to 100.0 */ + Matrix projection = MatrixPerspective(90.0f * DEG2RAD, 1.0f, 0.1f, 100.0f); + Matrix view = MatrixLookAt((Vector3){ 0, 0, 0 }, + (Vector3){ 0, 0, -1 }, + (Vector3){ 0, 1, 0 }); + + Matrix view_projection = MatrixMultiply(view, projection); + + extract_frustum_planes(planes, view_projection); + + /* Define a box at Z = -150 (outside the 100 unit limit) */ + BoundingBox box_too_far; + box_too_far.min = (Vector3){ -1.0f, -1.0f, -160.0f }; + box_too_far.max = (Vector3){ 1.0f, 1.0f, -150.0f }; + + TEST_ASSERT_FALSE_MESSAGE(is_axis_aligned_bounding_box_in_frustum(planes, + box_too_far), + "Box beyond the far clipping plane should be culled"); +} + +void run_frustum_tests(void) +{ + RUN_TEST(test_extract_frustum_planes_from_identity_matrix); + RUN_TEST(test_bounding_box_inside_frustum_is_not_culled); + RUN_TEST(test_bounding_box_behind_camera_is_culled); + RUN_TEST(test_bounding_box_beyond_far_plane_is_culled); +} diff --git a/source_code/frustum_module/frustum/tests/test_frustum.h b/source_code/frustum_module/frustum/tests/test_frustum.h new file mode 100644 index 0000000..7fedc65 --- /dev/null +++ b/source_code/frustum_module/frustum/tests/test_frustum.h @@ -0,0 +1,15 @@ +#ifndef test_frustum_h +#define test_frustum_h + +void test_extract_frustum_planes_from_identity_matrix(void); + +void test_bounding_box_inside_frustum_is_not_culled(void); + +void test_bounding_box_behind_camera_is_culled(void); + +void test_bounding_box_beyond_far_plane_is_culled(void); + +void run_frustum_tests(void); + + +#endif diff --git a/source_code/game_initalizer_module/game_initailizer/game_initializer.c b/source_code/game_initalizer_module/game_initailizer/game_initializer.c new file mode 100644 index 0000000..27d7b64 --- /dev/null +++ b/source_code/game_initalizer_module/game_initailizer/game_initializer.c @@ -0,0 +1,34 @@ +#include "game_initializer.h" + +#include "display.h" +#include "performance.h" +#include "player.h" +#include "world.h" +#include "function_success.h" + +#include +#include + +uint8_t create_game(struct world_struct** world, + struct player_struct** player) +{ + (void)InitWindow(screen_width, + screen_height, + "Enter The Fog"); + + if (!function_success(create_player(player), + "Failed to create player in game_initializer")) + { + return EXIT_FAILURE; + } + + if (!function_success(create_world(world), + "Failed to create world in game_initializer")) + { + return EXIT_FAILURE; + } + + (void)SetTargetFPS(framerate_limit); + (void)DisableCursor(); + return EXIT_SUCCESS; +} diff --git a/source_code/game_initalizer_module/game_initailizer/game_initializer.h b/source_code/game_initalizer_module/game_initailizer/game_initializer.h new file mode 100644 index 0000000..44087f6 --- /dev/null +++ b/source_code/game_initalizer_module/game_initailizer/game_initializer.h @@ -0,0 +1,34 @@ +#ifndef game_initializer_h +#define game_initializer_h + +#include + +struct world_struct; +struct player_struct; + +/** + * @brief Initializes the game by creating the world and player, setting up the window, + * and configuring initial game settings like framerate and cursor visibility. + * + * This function is responsible for setting up the core elements of the game, including: + * - Initializing the game window with the specified dimensions and title. + * - Creating the player using the `create_player` function. + * - Creating the world using the `create_world` function. + * - Configuring the framerate limit for the game loop. + * - Disabling the cursor for a more immersive experience. + * + * If any of the initialization steps fail, the function will return an error code + * (`EXIT_FAILURE`). Otherwise, it returns `EXIT_SUCCESS` to indicate successful + * initialization. + * + * @param[out] world A pointer to a pointer of type `struct world_struct`. On success, + * it will point to the created world structure. + * @param[out] player A pointer to a pointer of type `struct player_struct`. On success, + * it will point to the created player structure. + * + * @return `EXIT_SUCCESS` (0) if the game initialization was successful, + * or `EXIT_FAILURE` (1) if any initialization step fails. + */ +uint8_t create_game(struct world_struct** world, struct player_struct** player); + +#endif diff --git a/source_code/inventory_module/inventory/inventory.c b/source_code/inventory_module/inventory/inventory.c new file mode 100644 index 0000000..7e96de6 --- /dev/null +++ b/source_code/inventory_module/inventory/inventory.c @@ -0,0 +1,12 @@ +#include "inventory.h" +#include + +/** + * Prepares an inventory for use by zeroing out all slots. + * This function ensures that every item_struct within the inventory + * is initialized to an 'empty' or 'null' state to prevent undefined behavior. + */ +void create_inventory(inventory_struct *inventory) +{ + memset(inventory, 0, sizeof(inventory_struct)); +} diff --git a/source_code/inventory_module/inventory/inventory.h b/source_code/inventory_module/inventory/inventory.h new file mode 100644 index 0000000..45fae91 --- /dev/null +++ b/source_code/inventory_module/inventory/inventory.h @@ -0,0 +1,25 @@ +#ifndef inventory_h +#define inventory_h + +#include "item.h" + +#define inventory_size 36 + +/** + * A container structure that manages a fixed collection of items. + * This structure acts as the primary data storage for player belongings, + * including the hotbar and the main storage slots. + */ +typedef struct inventory_struct +{ + item_struct slots[inventory_size]; +} inventory_struct; + +/** + * Prepares an inventory for use by zeroing out all slots. + * This function ensures that every item_struct within the inventory + * is initialized to an 'empty' or 'null' state to prevent undefined behavior. + */ +void create_inventory(inventory_struct *inventory); + +#endif diff --git a/source_code/item_module/item/item.c b/source_code/item_module/item/item.c new file mode 100644 index 0000000..52d1d85 --- /dev/null +++ b/source_code/item_module/item/item.c @@ -0,0 +1,23 @@ +#include "item.h" + +#include + +/** + * Initializes an item structure with specific properties. + * This function is used to populate item data from the global item registry + * into specific instances (like inventory slots or world drops). + */ +void initialize_item(item_struct *item, + uint32_t new_item_id, + uint16_t new_max_durability, + uint8_t new_max_stack, + item_type_struct new_item_type, + bool new_is_placeable) +{ + memset(item, 0, sizeof(item_struct)); + item->item_id = new_item_id; + item->max_durability = new_max_durability; + item->max_stack = new_max_stack; + item->item_type = new_item_type; + item->is_placeable = new_is_placeable; +} diff --git a/source_code/item_module/item/item.h b/source_code/item_module/item/item.h new file mode 100644 index 0000000..8452523 --- /dev/null +++ b/source_code/item_module/item/item.h @@ -0,0 +1,57 @@ +#ifndef item_h +#define item_h + +#include +#include + +/** + * Constants defining default item behaviors and limits. + * 'no_id' represents an empty slot, while 'infinite_durability' is used + * for items like blocks or materials that do not break upon use. + */ +#define no_item_id 0 +#define infinite_durability 0 +#define default_max_stack_size 64 + +#define item_is_not_placeable false +#define item_is_placeable true + +/** + * Categorizes an item to determine its behavior when the player interacts + * with the world or uses the item. + */ +typedef uint8_t item_type_struct; +enum { + item_type_block = 0, + item_type_tool = 1, + item_type_consumable = 2, + item_type_miscellaneous = 3 +}; + +/** + * The core definition of an item instance within the game. + * This structure stores the static properties of an item, such as its + * identification, durability limits, and whether it can be placed in the + * world as a block. + */ +typedef struct item_struct { + uint32_t item_id; + uint16_t max_durability; + uint8_t max_stack; + item_type_struct item_type; + bool is_placeable; +} item_struct; + +/** + * Initializes an item structure with specific properties. + * This function is used to populate item data from the global item registry + * into specific instances (like inventory slots or world drops). + */ +void initialize_item(item_struct *item, + uint32_t new_item_id, + uint16_t new_max_durability, + uint8_t new_max_stack, + item_type_struct new_item_type, + bool new_is_placeable); + +#endif diff --git a/source_code/light_processors_module/light_table/light_table.c b/source_code/light_processors_module/light_table/light_table.c new file mode 100644 index 0000000..5866ca8 --- /dev/null +++ b/source_code/light_processors_module/light_table/light_table.c @@ -0,0 +1,7 @@ +#include "light_table.h" + +const float light_table[16] = { + 0.05f, 0.06f, 0.08f, 0.12f, 0.17f, 0.22f, 0.30f, 0.40f, + 0.50f, 0.60f, 0.72f, 0.82f, 0.90f, 0.95f, 0.98f, 1.0f +}; + diff --git a/source_code/light_processors_module/light_table/light_table.h b/source_code/light_processors_module/light_table/light_table.h new file mode 100644 index 0000000..2ab1c7f --- /dev/null +++ b/source_code/light_processors_module/light_table/light_table.h @@ -0,0 +1,6 @@ +#ifndef light_table_h +#define light_table_h + +extern const float light_table[16]; + +#endif diff --git a/source_code/light_processors_module/sunlight/_internal/private_sunlight.c b/source_code/light_processors_module/sunlight/_internal/private_sunlight.c new file mode 100644 index 0000000..580d917 --- /dev/null +++ b/source_code/light_processors_module/sunlight/_internal/private_sunlight.c @@ -0,0 +1,94 @@ +#include "private_sunlight.h" + +#include "chunk.h" +#include "world.h" +#include "world_querier.h" + +/** + * Processes the light queue to propagate light levels across the chunk. + * This function uses a breadth-first search to decay light intensity as it + * travels through air blocks, ensuring neighbors are updated to current - 1. + */ +void _propagate_light_level_flood_fill(struct chunk_struct* chunk, + _light_node_struct* light_queue, + uint16_t* head_index, + uint16_t* tail_index) +{ + const int8_t adjacency_directions[6][3] = { + { 1, 0, 0}, {-1, 0, 0}, + { 0, 1, 0}, { 0,-1, 0}, + { 0, 0, 1}, { 0, 0,-1} + }; + + while (*head_index < *tail_index && *tail_index < 8192) + { + _light_node_struct current_node = light_queue[(*head_index)++]; + + const uint32_t current_index = index_chunk( + current_node.x, + current_node.y, + current_node.z + ); + + uint8_t current_light_level = chunk->light_data[current_index]; + + /* Light below level 2 cannot propagate further. (decay to 0) */ + if (current_light_level <= 1) + { + continue; + } + + for (uint8_t i = 0; i < 6; i++) + { + int32_t neighbor_x = (int32_t)current_node.x + + adjacency_directions[i][0]; + + int32_t neighbor_y = (int32_t)current_node.y + + adjacency_directions[i][1]; + + int32_t neighbor_z = (int32_t)current_node.z + + adjacency_directions[i][2]; + + const bool is_inside_chunk = + (neighbor_x >= 0 && neighbor_x < chunk_size) && + (neighbor_y >= 0 && neighbor_y < chunk_size) && + (neighbor_z >= 0 && neighbor_z < chunk_size); + + if (is_inside_chunk) + { + uint32_t neighbor_index = index_chunk( + (uint8_t)neighbor_x, + (uint8_t)neighbor_y, + (uint8_t)neighbor_z + ); + + /* Only propagate into air blocks + * that have a lower light level. + */ + if (chunk->block_data[neighbor_index] == 0 && + chunk->light_data[neighbor_index] < + current_light_level - 1) + { + chunk->light_data[neighbor_index] = + current_light_level - 1; + + if (*tail_index < 8192) + { + light_queue[(*tail_index)++] = + (_light_node_struct){ + (uint8_t)neighbor_x, + (uint8_t)neighbor_y, + (uint8_t)neighbor_z + }; + } + } + } + } + } +} + +/** + * Evaluates a specific coordinate to determine if it should act as a starting point + * for light propagation. It checks for internal sunlight or light bleeding in + * from neighboring chunks. + */ diff --git a/source_code/light_processors_module/sunlight/_internal/private_sunlight.h b/source_code/light_processors_module/sunlight/_internal/private_sunlight.h new file mode 100644 index 0000000..452381f --- /dev/null +++ b/source_code/light_processors_module/sunlight/_internal/private_sunlight.h @@ -0,0 +1,37 @@ +#ifndef private_sunlight_h +#define private_sunlight_h + +#include "stdint.h" + +struct world_struct; +struct chunk_struct; + +typedef struct _light_node_struct { + uint8_t x, y, z; +} _light_node_struct; + +/** + * Evaluates a specific coordinate to determine if it should act as a starting point + * for light propagation. It checks for internal sunlight or light bleeding in + * from neighboring chunks. + */ +void _seed_light_source_at_coordinate(struct world_struct* world, + struct chunk_struct* chunk, + uint8_t local_x, + uint8_t local_y, + uint8_t local_z, + _light_node_struct* light_queue, + uint16_t* tail_index); + +/** + * Processes the light queue to propagate light levels across the chunk. + * This function uses a breadth-first search to decay light intensity as it + * travels through air blocks, ensuring neighbors are updated to current - 1. + */ +void _propagate_light_level_flood_fill(struct chunk_struct* chunk, + _light_node_struct* light_queue, + uint16_t* head_index, + uint16_t* tail_index); + + +#endif diff --git a/source_code/light_processors_module/sunlight/sunlight.c b/source_code/light_processors_module/sunlight/sunlight.c new file mode 100644 index 0000000..301f779 --- /dev/null +++ b/source_code/light_processors_module/sunlight/sunlight.c @@ -0,0 +1,50 @@ +#include "sunlight.h" +#include "_internal/private_sunlight.h" + +#include "chunk.h" +#include "world.h" +#include "world_querier.h" + +#include + +/** + * Calculates initial vertical illumination for a specific chunk. + * * This function performs a top-down scan of the chunk's columns. It + * determines where sunlight is obstructed by solid blocks and where it + * can pass through to the chunk below. This serves as the primary data + * source for the light propagation phase. + */ +void apply_sunlight(struct chunk_struct *neighbors[3][3][3], struct chunk_struct *chunk) +{ + for (uint8_t local_x = 0; local_x < chunk_size; local_x++) + { + for (uint8_t local_z = 0; local_z < chunk_size; local_z++) + { + /* Check the light level at the bottom of the chunk + * above this one by looking at the neighbor array. + */ + uint8_t column_light = get_light_at_neighbor(neighbors, + chunk, + (int32_t)local_x, + (int32_t)chunk_size, + (int32_t)local_z); + + for (int8_t local_y = (int8_t)chunk_size - 1; local_y >= 0; local_y--) + { + uint32_t block_index = index_chunk(local_x, + (uint8_t)local_y, + local_z); + + /* If we hit any non-air block, + * the column below it enters shadow + */ + if (chunk->block_data[block_index] != 0) + { + column_light = 0; + } + + chunk->light_data[block_index] = column_light; + } + } + } +} diff --git a/source_code/light_processors_module/sunlight/sunlight.h b/source_code/light_processors_module/sunlight/sunlight.h new file mode 100644 index 0000000..8c183af --- /dev/null +++ b/source_code/light_processors_module/sunlight/sunlight.h @@ -0,0 +1,25 @@ +#ifndef sunlight_h +#define sunlight_h + +struct world_struct; +struct chunk_struct; + +/** + * Calculates initial vertical illumination for a specific chunk. + * * This function performs a top-down scan of the chunk's columns. It + * determines where sunlight is obstructed by solid blocks and where it + * can pass through to the chunk below. This serves as the primary data + * source for the light propagation phase. + */ +void apply_sunlight(struct chunk_struct *neighbors[3][3][3], struct chunk_struct *chunk); + +/** + * Expands light levels from sources into adjacent empty spaces. + * * This function handles the "flood fill" or "diffusion" of light. It + * takes the initial values from sunlight and light-emitting blocks + * and spreads them horizontally and vertically, decreasing intensity + * with distance to create natural-looking gradients and shadows. + */ +void light_propagate(struct world_struct* world, struct chunk_struct* chunk); + +#endif diff --git a/source_code/light_processors_module/sunlight/tests/test_sunlight.c b/source_code/light_processors_module/sunlight/tests/test_sunlight.c new file mode 100644 index 0000000..0aeed3c --- /dev/null +++ b/source_code/light_processors_module/sunlight/tests/test_sunlight.c @@ -0,0 +1,132 @@ +#include "test_sunlight.h" + +#include "unity.h" +#include "chunk.h" +#include "sunlight.h" +#include "_internal/private_sunlight.h" +#include + +/* Verify that vertical sunlight is blocked by a solid ceiling, casting shadow */ +void test_apply_sunlight_obstructed_by_solid_block(void) +{ + chunk_position_struct initial_position = { 0.0f, 0.0f, 0.0f }; + chunk_struct *test_chunk = initialize_chunk(initial_position); + + /* Ensure the chunk arrays are completely cleared */ + memset(test_chunk->block_data, 0, sizeof(test_chunk->block_data)); + memset(test_chunk->light_data, 0, sizeof(test_chunk->light_data)); + + /* Place a single solid block at the top-middle of the chunk */ + uint32_t block_index = index_chunk(8, 15, 8); + test_chunk->block_data[block_index] = 1; + + /* Process the vertical sunlight pass. + * The guard you added will now prevent a crash when passing NULL. + */ + apply_sunlight(NULL, test_chunk); + + /* The block at (8, 14, 8) is directly beneath the solid block. + * It must have a light level of zero. + */ + uint32_t shadowed_index = index_chunk(8, 14, 8); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(0, + test_chunk->light_data[shadowed_index], + "The block directly beneath an obstruction must be in shadow"); + + /* The block at (0, 15, 0) has nothing above it. + * Depending on your get_light_at_neighbor logic, it should either + * be 0 (if NULL returns 0) or 15 (if NULL returns max light). + */ + + destroy_chunk(test_chunk); +} + +/* Verify that light intensity decreases by exactly one per meter of travel */ +void test_light_propagation_decay_intensity_over_distance(void) +{ + chunk_position_struct initial_position = { 0.0f, 0.0f, 0.0f }; + chunk_struct *test_chunk = initialize_chunk(initial_position); + + memset(test_chunk->block_data, 0, sizeof(test_chunk->block_data)); + memset(test_chunk->light_data, 0, sizeof(test_chunk->light_data)); + + /* Manually set a high light intensity in the center of the chunk */ + uint32_t source_index = index_chunk(8, 8, 8); + test_chunk->light_data[source_index] = 15; + + /* Manually prepare the BFS queue for the internal flood fill function */ + _light_node_struct light_queue[8192]; + uint16_t head_index = 0; + uint16_t tail_index = 0; + + light_queue[tail_index++] = (_light_node_struct){ 8, 8, 8 }; + + /* Execute the propagation logic directly to isolate the test */ + _propagate_light_level_flood_fill(test_chunk, + light_queue, + &head_index, + &tail_index); + + /* Check the light gradient along the X axis */ + /* Distance 1: 15 - 1 = 14 */ + TEST_ASSERT_EQUAL_UINT8(14, test_chunk->light_data[index_chunk(9, 8, 8)]); + /* Distance 2: 15 - 2 = 13 */ + TEST_ASSERT_EQUAL_UINT8(13, test_chunk->light_data[index_chunk(10, 8, 8)]); + /* Distance 5: 15 - 5 = 10 */ + TEST_ASSERT_EQUAL_UINT8(10, test_chunk->light_data[index_chunk(13, 8, 8)]); + + destroy_chunk(test_chunk); +} + + + +/* Verify that light cannot pass through a solid wall of blocks */ +void test_light_propagation_is_blocked_by_solid_wall(void) +{ + chunk_position_struct initial_position = { 0.0f, 0.0f, 0.0f }; + chunk_struct *test_chunk = initialize_chunk(initial_position); + + memset(test_chunk->block_data, 0, sizeof(test_chunk->block_data)); + memset(test_chunk->light_data, 0, sizeof(test_chunk->light_data)); + + /* Create a solid 1-block thick wall at X = 5 */ + for (uint8_t y = 0; y < chunk_size; y++) + { + for (uint8_t z = 0; z < chunk_size; z++) + { + test_chunk->block_data[index_chunk(5, y, z)] = 1; + } + } + + /* Seed light at X = 4 (directly in front of the wall) */ + uint32_t source_index = index_chunk(4, 8, 8); + test_chunk->light_data[source_index] = 10; + + _light_node_struct light_queue[8192]; + uint16_t head_index = 0; + uint16_t tail_index = 0; + light_queue[tail_index++] = (_light_node_struct){ 4, 8, 8 }; + + _propagate_light_level_flood_fill(test_chunk, + light_queue, + &head_index, + &tail_index); + + /* Verify that light exists on the source side of the wall */ + TEST_ASSERT_EQUAL_UINT8(9, test_chunk->light_data[index_chunk(4, 9, 8)]); + + /* Verify that light did not penetrate the wall to reach X = 6 */ + uint32_t destination_index = index_chunk(6, 8, 8); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(0, + test_chunk->light_data[destination_index], + "Light should not pass through a solid wall of blocks"); + + destroy_chunk(test_chunk); +} + +void run_sunlight_tests(void) +{ + RUN_TEST(test_apply_sunlight_obstructed_by_solid_block); + RUN_TEST(test_light_propagation_decay_intensity_over_distance); + RUN_TEST(test_light_propagation_is_blocked_by_solid_wall); +} diff --git a/source_code/light_processors_module/sunlight/tests/test_sunlight.h b/source_code/light_processors_module/sunlight/tests/test_sunlight.h new file mode 100644 index 0000000..407b8c5 --- /dev/null +++ b/source_code/light_processors_module/sunlight/tests/test_sunlight.h @@ -0,0 +1,13 @@ +#ifndef test_sunglight_h +#define test_sunglight_h + +void test_apply_sunlight_obstructed_by_solid_block(void); + +void test_light_propagation_decay_intensity_over_distance(void); + +void test_light_propagation_is_blocked_by_solid_wall(void); + +void run_sunlight_tests(void); + + +#endif diff --git a/source_code/main.c b/source_code/main.c new file mode 100644 index 0000000..2d1c3a7 --- /dev/null +++ b/source_code/main.c @@ -0,0 +1,43 @@ +#include "function_success.h" +#include "player.h" +#include "game_initializer.h" +#include "game_renderer.h" +#include "chunk_scheduler.h" +#include "world.h" +#include + + +// This is the main function. You know what it is for. +int main(void) +{ + world_struct* world = NULL; + player_struct* player = NULL; + + if (!function_success(create_game(&world, + &player), + "failed to create world in main")) + { + return EXIT_FAILURE; + } + + (void)start_chunk_scheduler(world); + + while (!WindowShouldClose()) + { + (void)destroy_inactive_chunks(world); + (void)update_player_controller(player); + + (void)update_players_known_chunks(player, + world); + (void)update_world(world, + player); + + (void)draw_game(world, + player); + } + (void)stop_chunk_scheduler(); + (void)destroy_world(&world); + (void)destroy_player(&player); + (void)CloseWindow(); + return EXIT_SUCCESS; +} diff --git a/source_code/mesh_builders_module/chunk_mesh_builder/_internal/private_chunk_mesh_builder.c b/source_code/mesh_builders_module/chunk_mesh_builder/_internal/private_chunk_mesh_builder.c new file mode 100644 index 0000000..027e149 --- /dev/null +++ b/source_code/mesh_builders_module/chunk_mesh_builder/_internal/private_chunk_mesh_builder.c @@ -0,0 +1,244 @@ +#include "private_chunk_mesh_builder.h" +#include "chunk_mesh_builder.h" + +#include "block.h" +#include "chunk.h" +#include "hashmap.h" +#include "light_table.h" +#include "world.h" +#include "world_querier.h" + +#include +#include +#include + +/** + * Defines the local vertex offsets for each of the six cube faces. + * The order follows: Top, Bottom, Left, Right, Front, Back. + */ +static const float face_vertices_offsets[faces_per_cube] + [vertices_per_face] + [components_per_vertex] = +{ + {{0,1,0}, {0,1,1}, {1,1,1}, {1,1,0}}, // Top + {{0,0,1}, {0,0,0}, {1,0,0}, {1,0,1}}, // Bottom + {{0,0,0}, {0,0,1}, {0,1,1}, {0,1,0}}, // Left + {{1,0,1}, {1,0,0}, {1,1,0}, {1,1,1}}, // Right + {{0,0,1}, {1,0,1}, {1,1,1}, {0,1,1}}, // Front + {{1,0,0}, {0,0,0}, {0,1,0}, {1,1,0}} // Back +}; + +/** + * Applies a multiplier to a color's RGB components to simulate directional shading. + */ +static Color _calculate_shading(Color input_color, float multiplier) +{ + return (Color){ + (uint8_t)(input_color.r * multiplier), + (uint8_t)(input_color.g * multiplier), + (uint8_t)(input_color.b * multiplier), + input_color.a + }; +} + +/** + * Retrieves the base color for a block face and applies directional "fake" ambient occlusion. + */ +static Color _get_directional_face_color(block_id_struct block_id, int32_t face_direction) +{ + Color base_block_color = get_block_data(block_id).color; + + switch (face_direction) + { + case 0: return _calculate_shading(base_block_color, 1.00f); // top + case 1: return _calculate_shading(base_block_color, 0.45f); // bottom + case 2: + case 3: return _calculate_shading(base_block_color, 0.75f); // sides + case 4: + case 5: return _calculate_shading(base_block_color, 0.65f); // sides + default: return base_block_color; + } +} + +/** + * Flattens face data into the contiguous CPU buffers used for mesh generation. + */ +static void _push_face_to_cpu_buffers(float *vertex_buffer, int32_t *vertex_count, + uint16_t *index_buffer, int32_t *index_count, + uint8_t *color_buffer, int32_t *color_count, + float *texture_buffer, int32_t *texture_count, + const _face_data_struct *face_data) +{ + uint16_t index_start_offset = (uint16_t)(*vertex_count / components_per_vertex); + + for (uint8_t i = 0; i < vertices_per_face; i++) + { + vertex_buffer[(*vertex_count)++] = face_data->vertices[i].x; + vertex_buffer[(*vertex_count)++] = face_data->vertices[i].y; + vertex_buffer[(*vertex_count)++] = face_data->vertices[i].z; + } + + uint16_t relative_indices[indices_per_face] = + { + index_start_offset, index_start_offset + 1, index_start_offset + 2, + index_start_offset, index_start_offset + 2, index_start_offset + 3 + }; + + for (uint8_t i = 0; i < indices_per_face; i++) + { + index_buffer[(*index_count)++] = relative_indices[i]; + } + + for (uint8_t i = 0; i < vertices_per_face; i++) + { + color_buffer[(*color_count)++] = face_data->color.r; + color_buffer[(*color_count)++] = face_data->color.g; + color_buffer[(*color_count)++] = face_data->color.b; + color_buffer[(*color_count)++] = face_data->color.a; + } + + for (uint8_t i = 0; i < vertices_per_face; i++) + { + texture_buffer[(*texture_count)++] = face_data->texture_coordinates[i][0]; + texture_buffer[(*texture_count)++] = face_data->texture_coordinates[i][1]; + } +} + +/** + * Prepares the geometry and lighting data for a single visible face. + */ +void _construct_and_emit_face(int32_t face_direction, + float world_x, float world_y, float world_z, + block_id_struct block_id, uint8_t light_level, + float *vertex_buffer, int32_t *vertex_count, + uint16_t *index_buffer, int32_t *index_count, + uint8_t *color_buffer, int32_t *color_count, + float *texture_buffer, int32_t *texture_count) +{ + _face_data_struct final_face_data = {0}; + Color shading_base = _get_directional_face_color(block_id, face_direction); + + float intensity_multiplier = light_table[light_level > 15 ? 15 : light_level]; + + final_face_data.color.r = (uint8_t)(shading_base.r * intensity_multiplier); + final_face_data.color.g = (uint8_t)(shading_base.g * intensity_multiplier); + final_face_data.color.b = (uint8_t)(shading_base.b * intensity_multiplier); + final_face_data.color.a = shading_base.a; + + for (uint8_t vertex_index = 0; vertex_index < vertices_per_face; vertex_index++) + { + final_face_data.vertices[vertex_index].x = + world_x + face_vertices_offsets[face_direction][vertex_index][0]; + + final_face_data.vertices[vertex_index].y = + world_y + face_vertices_offsets[face_direction][vertex_index][1]; + + final_face_data.vertices[vertex_index].z = + world_z + face_vertices_offsets[face_direction][vertex_index][2]; + } + + float face_texture_uvs[4][2]; + get_block_face_texture_coordinates(block_id, face_direction, face_texture_uvs); + + for (uint8_t i = 0; i < vertices_per_face; i++) + { + final_face_data.texture_coordinates[i][0] = face_texture_uvs[i][0]; + final_face_data.texture_coordinates[i][1] = face_texture_uvs[i][1]; + } + + _push_face_to_cpu_buffers(vertex_buffer, vertex_count, index_buffer, index_count, + color_buffer, color_count, texture_buffer, texture_count, + &final_face_data); +} + +/** + * Main logic for determining which block faces are visible and should be rendered. + */ +void _emit_block(struct chunk_struct *chunk, + struct chunk_struct *neighbors[3][3][3], + int32_t local_x, int32_t local_y, int32_t local_z, + float *vertex_buffer, int32_t *vertex_count, + uint16_t *index_buffer, int32_t *index_count, + uint8_t *color_buffer, int32_t *color_count, + float *texture_buffer, int32_t *texture_count) +{ + block_id_struct current_block_id = + chunk->block_data[index_chunk((uint8_t)local_x, + (uint8_t)local_y, + (uint8_t)local_z)]; + if (current_block_id == block_air) + { + return; + } + + float world_float_x = (float)local_x; + float world_float_y = (float)local_y; + float world_float_z = (float)local_z; + + static const int8_t neighbor_directions[faces_per_cube][spatial_dimensions] = + { + { 0, 1, 0}, { 0,-1, 0}, // top, bottom + {-1, 0, 0}, { 1, 0, 0}, // left, right + { 0, 0, 1}, { 0, 0,-1} // front, back + }; + + for (uint8_t face_index = 0; face_index < faces_per_cube; face_index++) + { + int8_t offset_x = neighbor_directions[face_index][x_axis]; + int8_t offset_y = neighbor_directions[face_index][y_axis]; + int8_t offset_z = neighbor_directions[face_index][z_axis]; + + if (!is_neighbor_solid(neighbors, chunk, local_x, local_y, local_z, + offset_x, offset_y, offset_z)) + { + uint8_t face_light_level = get_light_at_neighbor(neighbors, chunk, + local_x + offset_x, + local_y + offset_y, + local_z + offset_z); + + _construct_and_emit_face(face_index, + world_float_x, + world_float_y, + world_float_z, + current_block_id, + face_light_level, + vertex_buffer, vertex_count, + index_buffer, index_count, + color_buffer, color_count, + texture_buffer, texture_count); + } + } +} + +void _free_pending_mesh_cpu_data(struct chunk_struct *chunk) +{ + if (chunk == NULL || chunk->pending_mesh == NULL) return; + + MemFree(chunk->pending_mesh->vertices); + MemFree(chunk->pending_mesh->texture_coordinates); + MemFree(chunk->pending_mesh->indices); + MemFree(chunk->pending_mesh->colors); + MemFree(chunk->pending_mesh); + + chunk->pending_mesh = NULL; +} + +/** + * Fully deallocates both GPU and CPU mesh data for a chunk. + */ +void _free_chunk_mesh(struct chunk_struct* chunk) +{ + if (chunk == NULL) + { + return; + } + + if (chunk->model.meshes != NULL) + { + UnloadModel(chunk->model); + chunk->model.meshes = NULL; + } + + _free_pending_mesh_cpu_data(chunk); + atomic_store(&chunk->build_state, state_needs_data); +} diff --git a/source_code/mesh_builders_module/chunk_mesh_builder/_internal/private_chunk_mesh_builder.h b/source_code/mesh_builders_module/chunk_mesh_builder/_internal/private_chunk_mesh_builder.h new file mode 100644 index 0000000..057522a --- /dev/null +++ b/source_code/mesh_builders_module/chunk_mesh_builder/_internal/private_chunk_mesh_builder.h @@ -0,0 +1,94 @@ +#ifndef private_chunk_mesh_builder_h +#define private_chunk_mesh_builder_h + +#include "block.h" + +#include "raylib.h" + +#include + +struct chunk_struct; +struct world_struct; + +/** + * Temporary storage for a single quad's geometric data. + * This structure is used as an intermediate step to collect vertex positions, + * texture coordinates, and lighting colors before they are flattened into + * the larger CPU mesh buffers. + */ +typedef struct _face_data_struct { + Vector3 vertices[4]; + float texture_coordinates[4][2]; + Color color; +} _face_data_struct; + +/** + * Geometric constants for voxel mesh construction. + * These values define the fixed relationships between vertices, triangles, + * and faces used to calculate buffer offsets and memory allocations. + */ +enum { + components_per_vertex = 3, // x, y, z + vertices_per_face = 4, // 4 corners of a square/quad + vertices_per_triangle = 3, // 3 corners of a triangle + triangles_per_face = 2, // 2 triangles make 1 square/quad + bytes_per_color = 4, // r, g, b, a + texture_dimensions_per_vertex = 2, // u, v + faces_per_cube = 6, // 6 faces of a cube. + + x_axis = 0, + y_axis = 1, + z_axis = 2, + spatial_dimensions = 3 +}; + +/* Calculation macros for buffer stride and size requirements */ +#define floats_per_face ((uint8_t)(vertices_per_face * components_per_vertex)) +#define indices_per_face ((uint8_t)(triangles_per_face * vertices_per_triangle)) +#define color_bytes_per_face ((uint8_t)(vertices_per_face * bytes_per_color)) +#define texture_coordinates_per_face ((uint8_t)(vertices_per_face * texture_dimensions_per_vertex)) + + +/** + * Evaluates a block's neighbors and appends visible faces to the mesh buffers. + * This is the core "greedy" or "hidden-face" culling logic that ensures + * only surfaces facing air are generated as geometry. + */ + +void _emit_block(struct chunk_struct *chunk, + struct chunk_struct *neighbors[3][3][3], + int32_t local_x, int32_t local_y, int32_t local_z, + float *vertex_buffer, int32_t *vertex_count, + uint16_t *index_buffer, int32_t *index_count, + uint8_t *color_buffer, int32_t *color_count, + float *texture_buffer, int32_t *texture_count); + +void _construct_and_emit_face(int32_t face_direction, + float world_x, float world_y, float world_z, + block_id_struct block_id, uint8_t light_level, + float *vertex_buffer, int32_t *vertex_count, + uint16_t *index_buffer, int32_t *index_count, + uint8_t *color_buffer, int32_t *color_count, + float *texture_buffer, int32_t *texture_count); +/** + * Transfers the completed CPU mesh data into a Raylib/GPU Model structure. + * This function must be called on the main thread to ensure the OpenGL + * context is available for buffer creation and shader binding. + */ +void _upload_chunk_mesh_to_gpu(struct chunk_struct *chunk, + Shader fog_shader, + Texture2D world_texture); + +/** + * Internal helper to release CPU-side memory used during the build process. + * This frees the raw float and index arrays after they have been + * successfully uploaded to the GPU. + */ +void _free_chunk_mesh(struct chunk_struct* chunk); + +/** + * Releases CPU-side memory buffers once a chunk has been uploaded to the GPU. + */ +void _free_pending_mesh_cpu_data(struct chunk_struct *chunk); + +#endif diff --git a/source_code/mesh_builders_module/chunk_mesh_builder/chunk_mesh_builder.c b/source_code/mesh_builders_module/chunk_mesh_builder/chunk_mesh_builder.c new file mode 100644 index 0000000..9bf4d46 --- /dev/null +++ b/source_code/mesh_builders_module/chunk_mesh_builder/chunk_mesh_builder.c @@ -0,0 +1,144 @@ +#include "chunk_mesh_builder.h" +#include "_internal/private_chunk_mesh_builder.h" + +#include "chunk.h" +#include "chunk_scheduler.h" +#include "hashmap.h" +#include "world.h" + +#include "raylib.h" + +#include +#include + +/** + * Generates the vertex and index data for a specific chunk's geometry. + * * This function performs hidden-face culling by checking neighboring blocks + * (both internal and external to the chunk) and populates a temporary CPU buffer + * with vertex positions, texture coordinates, and lighting data. + */ +void build_chunk_mesh(struct chunk_struct *chunk, struct world_struct *world) +{ + struct chunk_struct *neighbors[3][3][3]; + + pthread_mutex_lock(&world->mutex); + for (int offset_x = -1; offset_x <= 1; offset_x++) + { + for (int offset_y = -1; offset_y <= 1; offset_y++) + { + for (int offset_z = -1; offset_z <= 1; offset_z++) + { + worlds_chunk_hashmap_entry chunk_to_look_up = + { + .chunk_position = &chunk->position, + .chunk_pointer = chunk, + }; + + worlds_chunk_hashmap_entry *search_result = (worlds_chunk_hashmap_entry*) + hashmap_get(world->active_chunks, + &chunk_to_look_up); + + int array_index_x = offset_x + 1; + int array_index_y = offset_y + 1; + int array_index_z = offset_z + 1; + + if (search_result != NULL) + { + neighbors[array_index_x] + [array_index_y] + [array_index_z] = + search_result->chunk_pointer; + } + else + { + neighbors[array_index_x] + [array_index_y] + [array_index_z] = NULL; + } + } + } + } + pthread_mutex_unlock(&world->mutex); + + cpu_mesh_data_struct *mesh_data = MemAlloc(sizeof(cpu_mesh_data_struct)); + memset(mesh_data, 0, sizeof(*mesh_data)); + + uint32_t max_faces = + (chunk_size * chunk_size * chunk_size * faces_per_cube) / 2; + + mesh_data->vertices = + MemAlloc(max_faces * floats_per_face * sizeof(float)); + + mesh_data->texture_coordinates = + MemAlloc(max_faces * texture_coordinates_per_face * sizeof(float)); + + mesh_data->indices = + MemAlloc(max_faces * indices_per_face * sizeof(uint16_t)); + + mesh_data->colors = + MemAlloc(max_faces * color_bytes_per_face * sizeof(uint8_t)); + + for (uint8_t local_x = 0; local_x < chunk_size; local_x++) + { + for (uint8_t local_y = 0; local_y < chunk_size; local_y++) + { + for (uint8_t local_z = 0; local_z < chunk_size; local_z++) + { + _emit_block(chunk, neighbors, local_x, local_y, local_z, + mesh_data->vertices, &mesh_data->vertex_count, + mesh_data->indices, &mesh_data->index_count, + mesh_data->colors, &mesh_data->color_count, + mesh_data->texture_coordinates, + &mesh_data->texture_coordinate_count + ); + } + } + } + chunk->pending_mesh = mesh_data; +} + +void upload_chunk_mesh_to_gpu(struct chunk_struct *chunk, + struct Shader fog_shader, + Texture2D texture_atlas) +{ + if (chunk == NULL || chunk->pending_mesh == NULL) return; + + int expected = state_chunk_is_uploading; + + if(!atomic_compare_exchange_strong(&chunk->build_state, + &expected, + state_chunk_finished_uploading)) + { + return; + } + // Clean up old model if it exists + if (chunk->model.meshCount > 0) + { + UnloadModel(chunk->model); + chunk->model = (Model){ 0 }; + } + + Mesh raylib_mesh = { 0 }; + raylib_mesh.vertexCount = chunk->pending_mesh->vertex_count / spatial_dimensions; + raylib_mesh.triangleCount = chunk->pending_mesh->index_count / vertices_per_triangle; + + raylib_mesh.vertices = chunk->pending_mesh->vertices; + raylib_mesh.indices = chunk->pending_mesh->indices; + raylib_mesh.colors = chunk->pending_mesh->colors; + raylib_mesh.texcoords = chunk->pending_mesh->texture_coordinates; + + UploadMesh(&raylib_mesh, true); + + chunk->model = LoadModelFromMesh(raylib_mesh); + chunk->model.materials[0].shader = fog_shader; + chunk->model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture_atlas; + + + MemFree(chunk->pending_mesh); + chunk->pending_mesh = NULL; +} + +void destroy_chunk_mesh(struct chunk_struct* chunk) +{ + _free_chunk_mesh(chunk); +} diff --git a/source_code/mesh_builders_module/chunk_mesh_builder/chunk_mesh_builder.h b/source_code/mesh_builders_module/chunk_mesh_builder/chunk_mesh_builder.h new file mode 100644 index 0000000..fdb5c3d --- /dev/null +++ b/source_code/mesh_builders_module/chunk_mesh_builder/chunk_mesh_builder.h @@ -0,0 +1,48 @@ +#ifndef chunk_mesh_builder_h +#define chunk_mesh_builder_h + +struct chunk_struct; +struct world_struct; +struct Shader; + +#include "raylib.h" + +#include + +/** + * A raw CPU-side buffer collection for building mesh geometry. + * This structure holds the contiguous arrays required by OpenGL/Raylib to + * define a mesh. It tracks counts for each attribute to allow for + * sequential "push" operations during the culling process. + */ +typedef struct cpu_mesh_data_struct { + float *vertices; + float *texture_coordinates; + uint16_t *indices; + uint8_t *colors; + + int32_t vertex_count; + int32_t index_count; + int32_t color_count; + int32_t texture_coordinate_count; +} cpu_mesh_data_struct; + +/** + * Generates the vertex and index data for a specific chunk's geometry. + * * This function performs hidden-face culling by checking neighboring blocks + * (both internal and external to the chunk) and populates a temporary CPU buffer + * with vertex positions, texture coordinates, and lighting data. + */ +void build_chunk_mesh(struct chunk_struct *chunk, struct world_struct *world); + +void upload_chunk_mesh_to_gpu(struct chunk_struct *chunk, + struct Shader fog_shader, + Texture2D texture_atlas); +/** + * Frees the GPU resources and CPU buffers associated with a chunk's mesh. + * * This should be called when a chunk is being unloaded from memory to prevent + * VRAM leaks. It safely handles the disposal of Raylib Model and Mesh structures. + */ +void destroy_chunk_mesh(struct chunk_struct* chunk); + +#endif diff --git a/source_code/mesh_builders_module/chunk_mesh_builder/tests/test_chunk_mesh_builder.c b/source_code/mesh_builders_module/chunk_mesh_builder/tests/test_chunk_mesh_builder.c new file mode 100644 index 0000000..5a50a52 --- /dev/null +++ b/source_code/mesh_builders_module/chunk_mesh_builder/tests/test_chunk_mesh_builder.c @@ -0,0 +1,148 @@ +#include "test_chunk_mesh_builder.h" + +#include "unity.h" +#include "../chunk_mesh_builder.h" +#include "../_internal/private_chunk_mesh_builder.h" + +#include "chunk.h" +#include "world.h" +#include "hashmap.h" + +#include +#include + +static void destroy_test_environment(struct world_struct *world, chunk_struct *chunk); +static uint64_t test_chunk_hash(const void *item, uint64_t seed0, uint64_t seed1); +static int test_chunk_compare(const void *a, const void *b, void *udata); +// --- Required Hashmap Callbacks for Joshua Baker's Implementation --- + +/** + * Hash function: Takes an item and returns a 64-bit hash. + * We use the Vector3 position inside the chunk_entry as the key. + */ +static uint64_t test_chunk_hash(const void *item, uint64_t seed0, uint64_t seed1) +{ + const worlds_chunk_hashmap_entry* entry = + (const worlds_chunk_hashmap_entry*)item; + + return hashmap_xxhash3(&entry->chunk_position, sizeof(Vector3), seed0, seed1); +} + +/** + * Comparison function: Returns 0 if items are equal, non-zero otherwise. + */ +static int test_chunk_compare(const void *a, const void *b, void *udata) +{ + (void)udata; + const worlds_chunk_hashmap_entry *ua = (const worlds_chunk_hashmap_entry *)a; + const worlds_chunk_hashmap_entry *ub = (const worlds_chunk_hashmap_entry *)b; + + if (ua->chunk_position->x == ub->chunk_position->x + && ua->chunk_position->y == ub->chunk_position->y + && ua->chunk_position->z == ub->chunk_position->z) + { + return 0; + } + return 1; +} + +// --- Environment Setup --- + +static struct world_struct* create_test_environment(chunk_struct **out_chunk) +{ + struct world_struct *world = malloc(sizeof(struct world_struct)); + memset(world, 0, sizeof(struct world_struct)); + + world->active_chunks = hashmap_new(sizeof(worlds_chunk_hashmap_entry), 0, 0, 0, + test_chunk_hash, + test_chunk_compare, + NULL, NULL); + + pthread_mutex_init(&world->mutex, NULL); + + chunk_position_struct origin = { 0.0f, 0.0f, 0.0f }; + *out_chunk = initialize_chunk(origin); + memset((*out_chunk)->block_data, 0, sizeof((*out_chunk)->block_data)); + + return world; +} + +static void destroy_test_environment(struct world_struct *world, chunk_struct *chunk) +{ + if (chunk != NULL) + { + if (chunk->pending_mesh != NULL) + { + _free_pending_mesh_cpu_data(chunk); + } + free(chunk); + } + + if (world != NULL) + { + if (world->active_chunks != NULL) + { + hashmap_free(world->active_chunks); + } + pthread_mutex_destroy(&world->mutex); + free(world); + } +} + +void test_build_chunk_mesh_single_block_generation(void) +{ + chunk_struct *chunk; + struct world_struct *world = create_test_environment(&chunk); + + chunk->block_data[index_chunk(8, 8, 8)] = 1; + build_chunk_mesh(chunk, world); + + TEST_ASSERT_NOT_NULL(chunk->pending_mesh); + + // 1 block = 6 faces = 36 indices + TEST_ASSERT_EQUAL_INT(36, chunk->pending_mesh->index_count); + + destroy_test_environment(world, chunk); +} + +void test_build_chunk_mesh_hidden_face_culling(void) +{ + chunk_struct *chunk; + struct world_struct *world = create_test_environment(&chunk); + + // Two adjacent blocks + chunk->block_data[index_chunk(8, 8, 8)] = 1; + chunk->block_data[index_chunk(9, 8, 8)] = 1; + + build_chunk_mesh(chunk, world); + + // (6-1) + (6-1) = 10 faces = 60 indices + TEST_ASSERT_EQUAL_INT(60, chunk->pending_mesh->index_count); + + destroy_test_environment(world, chunk); +} + +void test_free_pending_mesh_cpu_data_clears_pointers(void) +{ + chunk_struct *chunk; + struct world_struct *world = create_test_environment(&chunk); + + chunk->block_data[index_chunk(0, 0, 0)] = 1; + build_chunk_mesh(chunk, world); + + TEST_ASSERT_NOT_NULL(chunk->pending_mesh); + + _free_pending_mesh_cpu_data(chunk); + + TEST_ASSERT_NULL_MESSAGE(chunk->pending_mesh, + "Mesh pointer should be NULL after free"); + + destroy_test_environment(world, chunk); +} + +void run_chunk_mesh_builder_tests(void) +{ + RUN_TEST(test_build_chunk_mesh_single_block_generation); + RUN_TEST(test_build_chunk_mesh_hidden_face_culling); + RUN_TEST(test_free_pending_mesh_cpu_data_clears_pointers); +} diff --git a/source_code/mesh_builders_module/chunk_mesh_builder/tests/test_chunk_mesh_builder.h b/source_code/mesh_builders_module/chunk_mesh_builder/tests/test_chunk_mesh_builder.h new file mode 100644 index 0000000..4c36a22 --- /dev/null +++ b/source_code/mesh_builders_module/chunk_mesh_builder/tests/test_chunk_mesh_builder.h @@ -0,0 +1,13 @@ +#ifndef test_chunk_mesh_builder_h +#define test_chunk_mesh_builder_h + +void test_build_chunk_mesh_single_block_generation(void); + +void test_build_chunk_mesh_hidden_face_culling(void); + +void test_free_pending_mesh_cpu_data_clears_pointers(void); + +void run_chunk_mesh_builder_tests(void); + + +#endif diff --git a/source_code/performance_statistics_module/performance/performance.c b/source_code/performance_statistics_module/performance/performance.c new file mode 100644 index 0000000..b9b8841 --- /dev/null +++ b/source_code/performance_statistics_module/performance/performance.c @@ -0,0 +1,25 @@ +#include "performance.h" + +#include + +uint16_t framerate_limit = 0; + +void draw_fps_counter(void) { + int fps = GetFPS(); + char buffer[32]; + snprintf(buffer, sizeof(buffer), "FPS: %d", fps); + DrawText(buffer, 10, 10, 20, WHITE); +} + +void draw_coordinates(Vector3 position) +{ + char buffer[64]; + snprintf( buffer, + sizeof(buffer), + "X: %.2f Y: %.2f Z: %.2f", + position.x, + position.y, + position.z); + + DrawText(buffer, 10, 35, 20, WHITE); +} diff --git a/source_code/performance_statistics_module/performance/performance.h b/source_code/performance_statistics_module/performance/performance.h new file mode 100644 index 0000000..547f4f4 --- /dev/null +++ b/source_code/performance_statistics_module/performance/performance.h @@ -0,0 +1,15 @@ +#ifndef performance_h +#define performance_h + +#include "raylib.h" + +#include + +extern uint16_t framerate_limit; + + +void draw_fps_counter(void); +void draw_coordinates(Vector3 position); + +#endif + diff --git a/source_code/player_module/player/_internal/private_player.c b/source_code/player_module/player/_internal/private_player.c new file mode 100644 index 0000000..ca85793 --- /dev/null +++ b/source_code/player_module/player/_internal/private_player.c @@ -0,0 +1,301 @@ +#include "private_player.h" + +#include "chunk.h" +#include "chunk_position_to_vector_3/chunk_position_to_vector_3.h" +#include "player.h" +#include "world.h" +#include "player_input_map.h" + +#include "raymath.h" + +#include +#include +#include +#include +#include +#include + + +void _initialize_player(struct player_struct* player) +{ + player->position = (Vector3){ 0.0f, 2.0f, 0.0f }; + player->velocity = (Vector3){ 0.0f, 0.0f, 0.0f }; + player->yaw = 0.0f; + player->pitch = 0.0f; + player->mouse_sensitivity = 0.15f; + player->camera.position = player->position; + player->camera.target = (Vector3){ + player->position.x, + player->position.y, + player->position.z + 1.0f + }; + + player->camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; + + player->camera.fovy = 55.0f; + player->camera.projection = CAMERA_PERSPECTIVE; + player->corner_of_watched_block = (Vector3){ 0.0f, 0.0f, 0.0f }; + player->block_being_watched = (Vector3){ 0.0f, 0.0f, 0.0f }; + + player->should_highlight_block = false; + player->reach = 5.0f; + player->movement_speed = 10.0f; + + player->is_breaking_block = false; + player->breaking_block_progress = 0.0f; + + player->is_making_contact_with.below = false; + player->is_making_contact_with.above = false; + player->is_making_contact_with.left = false; + player->is_making_contact_with.right = false; + player->is_making_contact_with.front = false; + player->is_making_contact_with.back = false; + + player->health = 100; + player->fortitude = 0; + player->selected_hotbar_slot = 0; + player->has_entered_new_chunk = true; + + player->positive_borders = + offset_chunk_position(player->occupied_chunks_position, + render_distance + 1, + vertical_render_distance + 1, + render_distance + 1 ); + + player->negative_borders = + offset_chunk_position(player->occupied_chunks_position, + -render_distance - 1, + -vertical_render_distance - 1, + -render_distance - 1 ); + + create_inventory(&player->inventory); +} + +void _free_player(struct player_struct *player) +{ + memset(player, 0, sizeof(player_struct)); +} + +/** + * Refreshes the player's currently highlighted/targeted block based on their + * current position and rotation within the world. + */ +void _update_player_targeted_block(struct player_struct* player) +{ + player->should_highlight_block = false; + float closest_hit_distance_squared = FLT_MAX; + + // No world mutex lock needed here as we use the local snapshot + for (int x = 0; x < 3; x++) + { + for (int y = 0; y < 3; y++) + { + for (int z = 0; z < 3; z++) + { + struct chunk_struct *current_chunk = player->nearby_chunks[x][y][z]; + + if (current_chunk == NULL) + { + continue; + } + + // Skip chunks that aren't ready yet + if (atomic_load(¤t_chunk->build_state) == state_needs_data) + { + continue; + } + + bool found_solid_block_from_raycast = + _get_first_solid_block_in_chunk_along_view_ray(player, + current_chunk, + player->reach); + + if (found_solid_block_from_raycast) + { + // current_chunk->position is the world space origin of the chunk + Vector3 center_of_watched_block = { + player->corner_of_watched_block.x + (float)current_chunk->position.x + 0.5f, + player->corner_of_watched_block.y + (float)current_chunk->position.y + 0.5f, + player->corner_of_watched_block.z + (float)current_chunk->position.z + 0.5f + }; + + float current_hit_distance_squared = Vector3DistanceSqr( + player->camera.position, + center_of_watched_block + ); + + if (current_hit_distance_squared < closest_hit_distance_squared) + { + closest_hit_distance_squared = current_hit_distance_squared; + player->should_highlight_block = true; + player->block_being_watched = center_of_watched_block; + } + } + } + } + } +} + +/** + * Iterates through a specific chunk along the player's look vector to find + * the first non-air block. + */ +bool _get_first_solid_block_in_chunk_along_view_ray(struct player_struct *player, + struct chunk_struct *chunk, + float maximum_ray_distance) +{ + // Since we are using the player->nearby_chunks snapshot, + // the pointer 'chunk' is guaranteed to be valid for the duration + // of this frame. + + Vector3 ray_origin = player->camera.position; + Vector3 ray_direction = Vector3Normalize( + Vector3Subtract( + player->camera.target, + player->camera.position + ) + ); + + /* Smaller step increments result in higher precision block selection */ + float ray_traversal_step_increment = 0.1f; + + for (float distance_along_ray = 0.0f; + distance_along_ray <= maximum_ray_distance; + distance_along_ray += ray_traversal_step_increment) + { + Vector3 sampled_world_position = Vector3Add( + ray_origin, + Vector3Scale(ray_direction, distance_along_ray) + ); + + Vector3 sampled_chunk_local_position = Vector3Subtract( + sampled_world_position, + chunk_position_to_vector_3(chunk->position) + ); + + int32_t block_coordinate_x = (int32_t)floorf(sampled_chunk_local_position.x); + int32_t block_coordinate_y = (int32_t)floorf(sampled_chunk_local_position.y); + int32_t block_coordinate_z = (int32_t)floorf(sampled_chunk_local_position.z); + + // Boundary check relative to the chunk being sampled + if (block_coordinate_x < 0 || block_coordinate_y < 0 || + block_coordinate_z < 0 || block_coordinate_x >= (int32_t)chunk_size || + block_coordinate_y >= (int32_t)chunk_size || + block_coordinate_z >= (int32_t)chunk_size) + { + continue; + } + + uint32_t block_index = index_chunk((uint8_t)block_coordinate_x, + (uint8_t)block_coordinate_y, + (uint8_t)block_coordinate_z); + + // If chunks are modified by other threads, we treat block_data as + // effectively read-only during the raycast phase. + if (chunk->block_data[block_index] != 0) + { + player->corner_of_watched_block = (Vector3){ + (float)block_coordinate_x, + (float)block_coordinate_y, + (float)block_coordinate_z + }; + return true; + } + } + + return false; +} + +Vector3 _update_player_movement(struct player_struct* player, float seconds_since_last_frame) +{ + Vector3 camera_movement_this_frame = { 0 }; + + if (player_walk_forward_pressed) + { + camera_movement_this_frame.x += + player->movement_speed * seconds_since_last_frame; + } + + if (player_walk_backward_pressed) + { + camera_movement_this_frame.x -= + player->movement_speed * seconds_since_last_frame; + } + + if (player_walk_right_pressed) + { + camera_movement_this_frame.y += + player->movement_speed * seconds_since_last_frame; + } + + if (player_walk_left_pressed) + { + camera_movement_this_frame.y -= + player->movement_speed * seconds_since_last_frame; + } + + if (player_fly_up_is_pressed) + { + camera_movement_this_frame.z += + player->movement_speed * seconds_since_last_frame; + } + + if (player_fly_down_is_pressed) + { + camera_movement_this_frame.z -= + player->movement_speed * seconds_since_last_frame; + } + + return camera_movement_this_frame; +} + +void _update_player_chunk_occupancy_state(struct player_struct *player) +{ + if (player == NULL) + { + fprintf(stderr, "_update_player_chunk_occupancy_state," + "player argument is NULL"); + return; + } + int32_t current_chunk_x = (int32_t)floorf(player->camera.position.x / chunk_size); + int32_t current_chunk_y = (int32_t)floorf(player->camera.position.y / chunk_size); + int32_t current_chunk_z = (int32_t)floorf(player->camera.position.z / chunk_size); + + int32_t current_chunks_position_x = (current_chunk_x * chunk_size); + int32_t current_chunks_position_y = (current_chunk_y * chunk_size); + int32_t current_chunks_position_z = (current_chunk_z * chunk_size); + + bool has_moved_to_different_chunk_x = + (current_chunks_position_x != player->occupied_chunks_position.x); + + bool has_moved_to_different_chunk_y = + (current_chunks_position_y != player->occupied_chunks_position.y); + + bool has_moved_to_different_chunk_z = + (current_chunks_position_z != player->occupied_chunks_position.z); + + if (has_moved_to_different_chunk_x + || has_moved_to_different_chunk_y + || has_moved_to_different_chunk_z) + { + player->has_entered_new_chunk = true; + player->occupied_chunks_position.x = current_chunks_position_x; + player->occupied_chunks_position.y = current_chunks_position_y; + player->occupied_chunks_position.z = current_chunks_position_z; + player->positive_borders = + offset_chunk_position(player->occupied_chunks_position, + render_distance + 1, + vertical_render_distance + 1, + render_distance + 1 ); + + player->negative_borders = + offset_chunk_position(player->occupied_chunks_position, + -render_distance - 1, + -vertical_render_distance - 1, + -render_distance - 1 ); + } + else + { + player->has_entered_new_chunk = false; + } +} diff --git a/source_code/player_module/player/_internal/private_player.h b/source_code/player_module/player/_internal/private_player.h new file mode 100644 index 0000000..0f27c61 --- /dev/null +++ b/source_code/player_module/player/_internal/private_player.h @@ -0,0 +1,27 @@ +#ifndef private_player_h +#define private_player_h + +struct player_struct; +struct world_struct; +struct chunk_struct; + +#include "raylib.h" + +#include + +void _initialize_player(struct player_struct* player); + +void _free_player(struct player_struct* player); + +bool _get_first_solid_block_in_chunk_along_view_ray(struct player_struct *player, + struct chunk_struct *chunk, + float maximum_ray_distance); + +void _update_player_targeted_block(struct player_struct* player); + +void _update_player_chunk_occupancy_state(struct player_struct *player); + +Vector3 _update_player_movement(struct player_struct* player, + float seconds_since_last_frame); + +#endif diff --git a/source_code/player_module/player/player.c b/source_code/player_module/player/player.c new file mode 100644 index 0000000..eb61c66 --- /dev/null +++ b/source_code/player_module/player/player.c @@ -0,0 +1,119 @@ +#include "player.h" +#include "_internal/private_player.h" + +#include "chunk.h" +#include "world.h" +#include "hashmap.h" + +#include +#include +#include +#include + +uint8_t create_player(struct player_struct** player) +{ + *player = (player_struct *) calloc(1, sizeof(player_struct)); + + if(*player == NULL) + { + return EXIT_FAILURE; + } + + _initialize_player(*player); + return EXIT_SUCCESS; +} + +void destroy_player(struct player_struct** player) +{ + if (*player == NULL) + { + return; + } + _free_player(*player); + free(*player); + *player = NULL; +} + +void update_player_controller(struct player_struct *player) +{ + if(player == NULL) + { + fprintf(stderr, + "Error: update_player_controller, argument player is NULL\n"); + return; + } + float seconds_since_last_frame = GetFrameTime(); + _update_player_chunk_occupancy_state(player); + + Vector3 camera_movement_this_frame = + _update_player_movement(player, + seconds_since_last_frame); + + Vector2 change_in_mouse_position_since_last_call = GetMouseDelta(); + + Vector3 change_in_cameras_rotation = + { + change_in_mouse_position_since_last_call.x * player->mouse_sensitivity, + change_in_mouse_position_since_last_call.y * player->mouse_sensitivity, + 0.0f // Mice only rotate x and y so we leave it 0.0f. + }; + + UpdateCameraPro(&player->camera, + camera_movement_this_frame, + change_in_cameras_rotation, + 0.0f); // We do not zoom the camera this way so we leave it 0.0f. + + _update_player_targeted_block(player); +} + +void update_players_known_chunks(struct player_struct* player, + struct world_struct* world) +{ + if (player == NULL + || world == NULL) + { + fprintf(stderr, + "Error: update_players_known_chunks, arguement player," + " or world, or both are NULL\n"); + return; + } + pthread_mutex_lock(&world->mutex); + for (int8_t offset_x = -1; offset_x <= 1; offset_x++) + { + for (int8_t offset_y = -1; offset_y <= 1; offset_y++) + { + for (int8_t offset_z = -1; offset_z <= 1; offset_z++) + { + worlds_chunk_hashmap_entry query_entry = + { + .chunk_position = + &player->occupied_chunks_position, + }; + + worlds_chunk_hashmap_entry* search_result = + (worlds_chunk_hashmap_entry*) + hashmap_get(world->active_chunks, + &query_entry); + + int array_index_x = offset_x + 1; + int array_index_y = offset_y + 1; + int array_index_z = offset_z + 1; + + if (search_result == NULL) + { + player->nearby_chunks[array_index_x] + [array_index_y] + [array_index_z] = NULL; + continue; + } + + player->nearby_chunks[array_index_x] + [array_index_y] + [array_index_z] = + (chunk_struct*) + search_result->chunk_pointer; + } + } + } + pthread_mutex_unlock(&world->mutex); +} diff --git a/source_code/player_module/player/player.h b/source_code/player_module/player/player.h new file mode 100644 index 0000000..c177667 --- /dev/null +++ b/source_code/player_module/player/player.h @@ -0,0 +1,57 @@ +#ifndef player_h +#define player_h + +#include "chunk.h" +#include "inventory.h" + +#include "raylib.h" + +#include +#include + +struct world_struct; + +typedef struct player_struct +{ + Vector3 position; + Vector3 velocity; + float yaw; + float pitch; + float mouse_sensitivity; + Camera3D camera; + Vector3 corner_of_watched_block; + Vector3 block_being_watched; + float reach; + float breaking_block_progress; + bool should_highlight_block; + bool is_breaking_block; + bool has_entered_new_chunk; + float movement_speed; + + struct chunk_position_struct occupied_chunks_position; + struct chunk_position_struct negative_borders; + struct chunk_position_struct positive_borders; + struct + { + bool below, above; + bool left, right; + bool front, back; + } is_making_contact_with; + uint8_t health; + uint8_t fortitude; + uint8_t selected_hotbar_slot; + struct chunk_struct* nearby_chunks[3][3][3]; + struct inventory_struct inventory; +} player_struct; + + +uint8_t create_player(struct player_struct** player); + +void destroy_player(struct player_struct** player); + +void update_player_controller(struct player_struct *player); + +void update_players_known_chunks(struct player_struct* player, + struct world_struct *world); + +#endif diff --git a/source_code/player_module/player_input_map/player_input_map.h b/source_code/player_module/player_input_map/player_input_map.h new file mode 100644 index 0000000..4b8ac0c --- /dev/null +++ b/source_code/player_module/player_input_map/player_input_map.h @@ -0,0 +1,15 @@ +#ifndef player_input_map_h +#define player_input_map_h + +// Walking controls. +#define player_walk_forward_pressed IsKeyDown(KEY_W) +#define player_walk_left_pressed IsKeyDown(KEY_A) +#define player_walk_backward_pressed IsKeyDown(KEY_S) +#define player_walk_right_pressed IsKeyDown(KEY_D) +#define player_jump_is_pressed IsKeyDown(KEY_SPACE) + +// Flying controllers. +#define player_fly_up_is_pressed IsKeyDown(KEY_Q) +#define player_fly_down_is_pressed IsKeyDown(KEY_E) + +#endif diff --git a/source_code/renderers_module/chunk_renderer/chunk_renderer.c b/source_code/renderers_module/chunk_renderer/chunk_renderer.c new file mode 100644 index 0000000..d2ecf38 --- /dev/null +++ b/source_code/renderers_module/chunk_renderer/chunk_renderer.c @@ -0,0 +1,25 @@ +#include "chunk_renderer.h" + +#include "chunk.h" +#include "chunk_position_to_vector_3/chunk_position_to_vector_3.h" +#include "frustum.h" + +#include "raylib.h" + + +/** + * Renders a single chunk to the screen, performing frustum culling + * using the provided array of clipping planes. + */ +void draw_chunk(struct chunk_struct* chunk, + const struct frustum_plane_struct* frustum) +{ + if (!is_axis_aligned_bounding_box_in_frustum(frustum, chunk->bounds)) + { + return; + } + + DrawModel(chunk->model, chunk_position_to_vector_3(chunk->position), 1.0f, WHITE); +} + + diff --git a/source_code/renderers_module/chunk_renderer/chunk_renderer.h b/source_code/renderers_module/chunk_renderer/chunk_renderer.h new file mode 100644 index 0000000..2083190 --- /dev/null +++ b/source_code/renderers_module/chunk_renderer/chunk_renderer.h @@ -0,0 +1,14 @@ +#ifndef chunk_renderer_h +#define chunk_renderer_h + +struct chunk_struct; +struct frustum_plane_struct; + +/** + * Renders a single chunk to the screen, performing frustum culling + * using the provided array of clipping planes. + */ +void draw_chunk(struct chunk_struct* chunk, + const struct frustum_plane_struct* frustum_planes); + +#endif diff --git a/source_code/renderers_module/game_renderer/game_renderer.c b/source_code/renderers_module/game_renderer/game_renderer.c new file mode 100644 index 0000000..bc01aba --- /dev/null +++ b/source_code/renderers_module/game_renderer/game_renderer.c @@ -0,0 +1,19 @@ +#include "game_renderer.h" + +#include "user_interface.h" +#include "world_renderer.h" + +#include "raylib.h" + +/** + * Orchestrates the entire frame rendering sequence by clearing the buffers, + * invoking the 3D world pass, and overlaying the 2D user interface. + */ +void draw_game(struct world_struct *world, struct player_struct *player) +{ + BeginDrawing(); + ClearBackground((Color){ 120, 180, 180, 255 }); + draw_world_3D(world, player); + draw_ui_2D(player); + EndDrawing(); +} diff --git a/source_code/renderers_module/game_renderer/game_renderer.h b/source_code/renderers_module/game_renderer/game_renderer.h new file mode 100644 index 0000000..6c4532d --- /dev/null +++ b/source_code/renderers_module/game_renderer/game_renderer.h @@ -0,0 +1,13 @@ +#ifndef game_renderer_h +#define game_renderer_h + +struct world_struct; +struct player_struct; + +/** + * Orchestrates the entire frame rendering sequence by clearing the buffers, + * invoking the 3D world pass, and overlaying the 2D user interface. + */ +void draw_game(struct world_struct *world, struct player_struct *player); + +#endif diff --git a/source_code/renderers_module/player_renderer/player_renderer.c b/source_code/renderers_module/player_renderer/player_renderer.c new file mode 100644 index 0000000..02171f3 --- /dev/null +++ b/source_code/renderers_module/player_renderer/player_renderer.c @@ -0,0 +1,25 @@ +#include "player_renderer.h" + +#include "player.h" + +#include "raylib.h" + +/** + * Renders the third-person representation of the player character, + * including the 3D model, animations, and equipped items. + */ +void draw_player(struct player_struct *player) { + highlight_block_being_watched(player); +} + + +/** + * Renders a 3D wireframe or highlight effect around the voxel + * currently targeted by the player's view ray. + */ +void highlight_block_being_watched(struct player_struct *player) { + if (player->should_highlight_block) + { + DrawCubeWires(player->block_being_watched, 1.02f, 1.02f, 1.02f, BLACK); + } +} diff --git a/source_code/renderers_module/player_renderer/player_renderer.h b/source_code/renderers_module/player_renderer/player_renderer.h new file mode 100644 index 0000000..f72c87c --- /dev/null +++ b/source_code/renderers_module/player_renderer/player_renderer.h @@ -0,0 +1,18 @@ +#ifndef player_renderer_h +#define player_renderer_h + +struct player_struct; + +/** + * Renders the third-person representation of the player character, + * including the 3D model, animations, and equipped items. + */ +void draw_player(struct player_struct *player); + +/** + * Renders a 3D wireframe or highlight effect around the voxel + * currently targeted by the player's view ray. + */ +void highlight_block_being_watched(struct player_struct *player); + +#endif diff --git a/source_code/renderers_module/world_renderer/_internal/private_world_renderer.c b/source_code/renderers_module/world_renderer/_internal/private_world_renderer.c new file mode 100644 index 0000000..da68bd4 --- /dev/null +++ b/source_code/renderers_module/world_renderer/_internal/private_world_renderer.c @@ -0,0 +1,67 @@ +#include "private_world_renderer.h" + +#include "chunk.h" +#include "chunk_renderer.h" +#include "frustum.h" +#include "hashmap.h" +#include "player.h" +#include "player_renderer.h" +#include "world.h" + +#include "raymath.h" + +/** + * Iterates through the world chunks to perform distance culling and frustum + * culling before invoking the individual chunk and player renderers. + */ +void _draw_games_3D_elements(struct world_struct *world, + struct player_struct *player, + const struct frustum_plane_struct *frustum_planes) +{ + draw_player(player); + + float render_distance_threshold = (float)((render_distance + 1) * chunk_size); + float half_chunk_dimension = (float)chunk_size / 2.0f; + + pthread_mutex_lock(&world->mutex); + + size_t hashmap_iterator = 0; + void *current_item; + + while (hashmap_iter(world->active_chunks, &hashmap_iterator, ¤t_item)) + { + worlds_chunk_hashmap_entry *current_entry = current_item; + chunk_struct *current_chunk = current_entry->chunk_pointer; + + if (current_chunk == NULL) + { + continue; + } + + if (atomic_load(¤t_chunk->build_state) != + state_chunk_finished_uploading) + { + continue; + } + + Vector3 chunk_center_position = { + (float)current_entry->chunk_position->x + half_chunk_dimension, + (float)current_entry->chunk_position->y + half_chunk_dimension, + (float)current_entry->chunk_position->z + half_chunk_dimension + }; + + float distance_to_player_camera = Vector3Distance( + player->camera.position, + chunk_center_position + ); + + if (distance_to_player_camera > render_distance_threshold) + { + continue; + } + + draw_chunk(current_chunk, frustum_planes); + } + + pthread_mutex_unlock(&world->mutex); +} diff --git a/source_code/renderers_module/world_renderer/_internal/private_world_renderer.h b/source_code/renderers_module/world_renderer/_internal/private_world_renderer.h new file mode 100644 index 0000000..65a12f3 --- /dev/null +++ b/source_code/renderers_module/world_renderer/_internal/private_world_renderer.h @@ -0,0 +1,17 @@ +#ifndef private_world_renderer_h +#define private_world_renderer_h + +struct world_struct; +struct player_struct; +struct frustum_plane_struct; + +/** + * Iterates through the world chunks to perform distance culling and frustum + * culling before invoking the individual chunk and player renderers. + */ +void _draw_games_3D_elements(struct world_struct* world, + struct player_struct* player, + const struct frustum_plane_struct* frustum); + + +#endif diff --git a/source_code/renderers_module/world_renderer/world_renderer.c b/source_code/renderers_module/world_renderer/world_renderer.c new file mode 100644 index 0000000..0d2dc64 --- /dev/null +++ b/source_code/renderers_module/world_renderer/world_renderer.c @@ -0,0 +1,40 @@ +#include "world_renderer.h" +#include "_internal/private_world_renderer.h" + +#include "constants.h" +#include "frustum.h" +#include "player.h" +#include "world.h" + +#include "raymath.h" + +/** + * Prepares the 3D rendering context by calculating the view-projection matrix, + * extracting the current viewing frustum, and invoking the 3D element pass. + */ +void draw_world_3D(struct world_struct *world, struct player_struct *player) +{ + struct frustum_plane_struct frustum_planes[number_of_planes_for_a_cube]; + + Matrix view_matrix = GetCameraMatrix(player->camera); + float aspect_ratio = (float)GetScreenWidth() / (float)GetScreenHeight(); + float near_plane_clipping_distance = 0.01f; + float far_plane_clipping_distance = 1000.0f; + + Matrix projection_matrix = MatrixPerspective( + player->camera.fovy * DEG2RAD, + aspect_ratio, + near_plane_clipping_distance, + far_plane_clipping_distance + ); + + Matrix view_projection_matrix = MatrixMultiply(view_matrix, projection_matrix); + + extract_frustum_planes(frustum_planes, view_projection_matrix); + + BeginMode3D(player->camera); + + _draw_games_3D_elements(world, player, frustum_planes); + + EndMode3D(); +} diff --git a/source_code/renderers_module/world_renderer/world_renderer.h b/source_code/renderers_module/world_renderer/world_renderer.h new file mode 100644 index 0000000..0145f43 --- /dev/null +++ b/source_code/renderers_module/world_renderer/world_renderer.h @@ -0,0 +1,13 @@ +#ifndef world_renderer_h +#define world_renderer_h + +struct world_struct; +struct player_struct; + +/** + * Prepares the 3D rendering context by calculating the view-projection matrix, + * extracting the current viewing frustum, and invoking the 3D element pass. + */ +void draw_world_3D(struct world_struct* world, struct player_struct* player); + +#endif diff --git a/source_code/schedulers_module/chunk_scheduler/_internal/private_chunk_scheduler.c b/source_code/schedulers_module/chunk_scheduler/_internal/private_chunk_scheduler.c new file mode 100644 index 0000000..340b960 --- /dev/null +++ b/source_code/schedulers_module/chunk_scheduler/_internal/private_chunk_scheduler.c @@ -0,0 +1,184 @@ +#include "private_chunk_scheduler.h" + +#include "chunk.h" +#include "chunk_mesh_builder.h" +#include "sunlight.h" +#include "terrain.h" +#include "world.h" + +#include +#include +#include +#include + +_chunk_job_struct _scheduler_job_queue[maximum_threads_allowed]; + +pthread_t _private_scheduler_worker_thread; +pthread_mutex_t _private_scheduler_job_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t _private_scheduler_job_condition = PTHREAD_COND_INITIALIZER; +bool _private_scheduler_is_currently_running = false; + +// Static functions +static uint16_t _static_scheduler_job_head_index = 0; +static uint16_t _static_scheduler_job_tail_index = 0; +static bool _static_is_job_queue_empty(void); +static bool _static_is_job_queue_full(void); +static bool _static_pop_chunk_from_job_queue(_chunk_job_struct *output_job); +static void _static_step_chunk_state_machine_once(struct chunk_struct* chunk_to_process, + struct world_struct* world_context); +static bool _static_is_job_queue_empty(void) +{ + return _static_scheduler_job_head_index == _static_scheduler_job_tail_index; +} + +static bool _static_is_job_queue_full(void) +{ + return ((_static_scheduler_job_tail_index + 1) % + maximum_threads_allowed) == + _static_scheduler_job_head_index; +} + +static bool _static_pop_chunk_from_job_queue(_chunk_job_struct *output_job) +{ + pthread_mutex_lock(&_private_scheduler_job_mutex); + + while (_static_is_job_queue_empty() && _private_scheduler_is_currently_running) + { + pthread_cond_wait(&_private_scheduler_job_condition, + &_private_scheduler_job_mutex); + } + + if (!_private_scheduler_is_currently_running) + { + pthread_mutex_unlock(&_private_scheduler_job_mutex); + return false; + } + + *output_job = _scheduler_job_queue[_static_scheduler_job_head_index]; + + _static_scheduler_job_head_index = + (uint16_t)(_static_scheduler_job_head_index + 1) % + maximum_threads_allowed; + + pthread_mutex_unlock(&_private_scheduler_job_mutex); + + return true; +} + +static void _static_step_chunk_state_machine_once(struct chunk_struct* chunk_to_process, + struct world_struct* world) +{ + puts("scheduler run"); + int expected = state_needs_data; + if (atomic_compare_exchange_strong(&chunk_to_process->build_state, + &expected, + state_getting_data) + || + atomic_compare_exchange_strong(&chunk_to_process->build_state, + &(int){state_needs_terrain}, + state_getting_terrain)) + { + puts("getting data run"); + generate_terrain(chunk_to_process, + &chunk_to_process->position); + + atomic_store(&chunk_to_process->build_state, + state_needs_lighting); + + _private_push_chunk_to_job_queue(chunk_to_process); + } + + expected = state_needs_lighting; + if (atomic_compare_exchange_strong(&chunk_to_process->build_state, + &expected, + state_getting_lighting)) + { + puts("getting lighting run"); + //apply_sunlight(world_context, current_chunk); + atomic_store(&chunk_to_process->build_state, state_needs_mesh); + _private_push_chunk_to_job_queue(chunk_to_process); + } + + expected = state_needs_mesh; + if (atomic_compare_exchange_strong(&chunk_to_process->build_state, + &expected, + state_getting_mesh)) + { + puts("getting mesh run"); + build_chunk_mesh(chunk_to_process, world); + atomic_store(&chunk_to_process->build_state, state_need_upload_to_gpu); + _private_push_chunk_to_job_queue(chunk_to_process); + } + + expected = state_need_upload_to_gpu; + if (atomic_compare_exchange_strong(&chunk_to_process->build_state, + &expected, + state_chunk_pending_upload)) + { + uint32_t index_awaiting_draw = + atomic_load(&world->count_chunks_awaiting_draw); + printf("test"); + sem_wait(&world->semaphore_free_slots_in_awaiting_draw); + printf("test"); + world->chunks_awaiting_draw[index_awaiting_draw] = chunk_to_process; + atomic_fetch_add_explicit(&world->count_chunks_awaiting_draw, + 1, + memory_order_acq_rel); + + atomic_fetch_sub_explicit(&chunk_to_process->reference_counter, + 1, + memory_order_acquire); + } +} + +bool _private_push_chunk_to_job_queue(struct chunk_struct *chunk) +{ + bool push_was_successful = false; + + pthread_mutex_lock(&_private_scheduler_job_mutex); + + if (!_static_is_job_queue_full()) + { + _scheduler_job_queue[_static_scheduler_job_tail_index].chunk = chunk; + + _static_scheduler_job_tail_index = + (uint16_t)(_static_scheduler_job_tail_index + 1) % + maximum_threads_allowed; + + pthread_cond_signal(&_private_scheduler_job_condition); + push_was_successful = true; + } + + pthread_mutex_unlock(&_private_scheduler_job_mutex); + + return push_was_successful; +} + +void* _private_chunk_worker_thread_main_loop(void* argument) +{ + struct world_struct *world_context = (struct world_struct*)argument; + + while (true) + { + _chunk_job_struct current_active_job; + + if (!_static_pop_chunk_from_job_queue(¤t_active_job)) + { + break; + } + + struct chunk_struct *current_chunk = current_active_job.chunk; + if (current_chunk == NULL) + { + continue; + } + + if (atomic_load(¤t_chunk->marked_for_unload)) + { + continue; + } + + _static_step_chunk_state_machine_once(current_chunk, world_context); + } + return NULL; +} diff --git a/source_code/schedulers_module/chunk_scheduler/_internal/private_chunk_scheduler.h b/source_code/schedulers_module/chunk_scheduler/_internal/private_chunk_scheduler.h new file mode 100644 index 0000000..326d9b4 --- /dev/null +++ b/source_code/schedulers_module/chunk_scheduler/_internal/private_chunk_scheduler.h @@ -0,0 +1,25 @@ +#ifndef private_chunk_scheduler_h +#define private_chunk_scheduler_h + +#include +#include + +struct chunk_struct; + +typedef struct _chunk_job_struct +{ + struct chunk_struct *chunk; +} _chunk_job_struct; + +#define maximum_threads_allowed 256 + +extern pthread_t _private_scheduler_worker_thread; +extern pthread_mutex_t _private_scheduler_job_mutex; +extern pthread_cond_t _private_scheduler_job_condition; +extern bool _private_scheduler_is_currently_running; + +bool _private_push_chunk_to_job_queue(struct chunk_struct* chunk); + +void* _private_chunk_worker_thread_main_loop(void* argument); + +#endif diff --git a/source_code/schedulers_module/chunk_scheduler/chunk_scheduler.c b/source_code/schedulers_module/chunk_scheduler/chunk_scheduler.c new file mode 100644 index 0000000..7a09c35 --- /dev/null +++ b/source_code/schedulers_module/chunk_scheduler/chunk_scheduler.c @@ -0,0 +1,33 @@ +#include "chunk_scheduler.h" +#include "_internal/private_chunk_scheduler.h" + +#include "chunk.h" + +void start_chunk_scheduler(struct world_struct* world) +{ + _private_scheduler_is_currently_running = true; + pthread_create(&_private_scheduler_worker_thread, + NULL, + _private_chunk_worker_thread_main_loop, + world); +} + +void stop_chunk_scheduler(void) +{ + pthread_mutex_lock(&_private_scheduler_job_mutex); + _private_scheduler_is_currently_running = false; + pthread_cond_signal(&_private_scheduler_job_condition); + pthread_mutex_unlock(&_private_scheduler_job_mutex); + pthread_join(_private_scheduler_worker_thread, NULL); +} + +void schedule_chunk_job(struct chunk_struct *chunk) +{ + int expected = state_new; + if (atomic_compare_exchange_strong(&chunk->build_state, + &expected, + state_needs_data)) + { + _private_push_chunk_to_job_queue(chunk); + } +} diff --git a/source_code/schedulers_module/chunk_scheduler/chunk_scheduler.h b/source_code/schedulers_module/chunk_scheduler/chunk_scheduler.h new file mode 100644 index 0000000..eb5f345 --- /dev/null +++ b/source_code/schedulers_module/chunk_scheduler/chunk_scheduler.h @@ -0,0 +1,25 @@ +#ifndef chunk_scheduler_h +#define chunk_scheduler_h + +struct chunk_struct; +struct world_struct; + +/** + * Initializes the threading primitives and launches the background worker + * thread responsible for processing the chunk mesh generation queue. + */ +void start_chunk_scheduler(struct world_struct* world); + +/** + * Signals the worker thread to terminate, waits for it to finish its + * current task, and cleans up the thread handle. + */ +void stop_chunk_scheduler(void); + +/** + * Places a chunk into the asynchronous build queue if it is not already + * being processed or waiting in the queue. + */ +void schedule_chunk_job(struct chunk_struct *chunk); + +#endif diff --git a/source_code/terrain_module/terrain/_internal/private_terrain.c b/source_code/terrain_module/terrain/_internal/private_terrain.c new file mode 100644 index 0000000..4415267 --- /dev/null +++ b/source_code/terrain_module/terrain/_internal/private_terrain.c @@ -0,0 +1,69 @@ +#include "private_terrain.h" + +#include "perlin.h" + +#include +#include + +#define base_noise_scale 0.01f +#define detail_noise_scale 0.05f +#define base_noise_amplitude 12.0f +#define detail_noise_amplitude 4.0f +#define global_ground_level 0.0f + +/** + * Calculates the noise-based height value for the terrain surface + * at the given global world coordinates. + */ +float _get_terrain_height_at_coordinates(int32_t world_coordinate_x, + int32_t world_coordinate_z) +{ + float noise_input_x = (float)world_coordinate_x; + float noise_input_z = (float)world_coordinate_z; + float base_elevation = stb_perlin_noise3(noise_input_x * base_noise_scale, + 0.0f, + noise_input_z * base_noise_scale, + 0, + 0, + 0); + + float detail_variation = stb_perlin_noise3( + noise_input_x * detail_noise_scale, + 100.0f, + noise_input_z * detail_noise_scale, + 0, 0, 0 + ); + + float calculated_final_height = + base_elevation * base_noise_amplitude + + detail_variation * detail_noise_amplitude + + global_ground_level; + + return floorf(calculated_final_height); +} + +/** + * Determines if a specific 3D coordinate should be empty (air) based + * on 3D noise patterns to create subterranean cave systems. + */ +bool _is_cave_present_at_coordinates(int32_t world_coordinate_x, + int32_t world_coordinate_y, + int32_t world_coordinate_z, + int32_t surface_height_at_location) +{ + if (world_coordinate_y >= surface_height_at_location + 5) + { + return false; + } + + float cave_noise_scale = 0.06f; + + float noise_density_value = stb_perlin_noise3( + (float)world_coordinate_x * cave_noise_scale, + (float)world_coordinate_y * cave_noise_scale, + (float)world_coordinate_z * cave_noise_scale, + 0, 0, 0 + ); + + return noise_density_value > 0.3f; +} diff --git a/source_code/terrain_module/terrain/_internal/private_terrain.h b/source_code/terrain_module/terrain/_internal/private_terrain.h new file mode 100644 index 0000000..a172edf --- /dev/null +++ b/source_code/terrain_module/terrain/_internal/private_terrain.h @@ -0,0 +1,23 @@ +#ifndef private_terrain_h +#define private_terrain_h + +#include +#include + +/** + * Calculates the noise-based height value for the terrain surface + * at the given global world coordinates. + */ +float _get_terrain_height_at_coordinates(int32_t world_coordinate_x, + int32_t world_coordinate_z); + +/** + * Determines if a specific 3D coordinate should be empty (air) based + * on 3D noise patterns to create subterranean cave systems. + */ +bool _is_cave_present_at_coordinates(int32_t world_coordinate_x, + int32_t world_coordinate_y, + int32_t world_coordinate_z, + int32_t surface_height_at_location); + +#endif diff --git a/source_code/terrain_module/terrain/terrain.c b/source_code/terrain_module/terrain/terrain.c new file mode 100644 index 0000000..d2fd4fd --- /dev/null +++ b/source_code/terrain_module/terrain/terrain.c @@ -0,0 +1,67 @@ +#include "terrain.h" +#include "_internal/private_terrain.h" + +#include "block.h" +#include "chunk.h" + +#include "raylib.h" + +#include + +/** + * Iterates through every coordinate in a chunk, calculating terrain height + * and cave presence to populate the block data array. + */ +void generate_terrain(struct chunk_struct *chunk, struct chunk_position_struct* chunks_world_position) +{ + // Align world position to integers once + int32_t base_x = chunks_world_position->x; + int32_t base_y = chunks_world_position->y; + int32_t base_z = chunks_world_position->z; + + for (uint8_t x = 0; x < chunk_size; x++) + { + for (uint8_t z = 0; z < chunk_size; z++) + { + int32_t world_coordinate_x = base_x + x; + int32_t world_coordinate_z = base_z + z; + int32_t surface_height = + (int32_t)floorf( + _get_terrain_height_at_coordinates( + world_coordinate_x, + world_coordinate_z)); + + for (uint8_t y = 0; y < chunk_size; y++) + { + int32_t world_coordinate_y = base_y + y; + uint8_t resulting_block_id = block_air; + + if (world_coordinate_y <= surface_height) + { + resulting_block_id = block_dirt; + + if (world_coordinate_y < surface_height - 3) + { + resulting_block_id = block_stone; + } + + if (world_coordinate_y > surface_height - 1) + { + resulting_block_id = block_grass; + } + + if (_is_cave_present_at_coordinates( + world_coordinate_x, + world_coordinate_y, + world_coordinate_z, + surface_height)) + { + resulting_block_id = block_air; + } + } + + chunk->block_data[index_chunk(x, y, z)] = resulting_block_id; + } + } + } +} diff --git a/source_code/terrain_module/terrain/terrain.h b/source_code/terrain_module/terrain/terrain.h new file mode 100644 index 0000000..10ae38b --- /dev/null +++ b/source_code/terrain_module/terrain/terrain.h @@ -0,0 +1,16 @@ +#ifndef terrain_h +#define terrain_h + +struct chunk_struct; +struct chunk_position_struct; + +#define grass_height -1 + +/** + * Iterates through every coordinate in a chunk, calculating terrain height + * and cave presence to populate the block data array. + */ +void generate_terrain(struct chunk_struct *chunk, + struct chunk_position_struct* chunks_world_position); + +#endif diff --git a/source_code/terrain_module/terrain/tests/test_terrain.c b/source_code/terrain_module/terrain/tests/test_terrain.c new file mode 100644 index 0000000..2b10a9f --- /dev/null +++ b/source_code/terrain_module/terrain/tests/test_terrain.c @@ -0,0 +1,120 @@ +#include "test_terrain.h" + +#include "unity.h" +#include "../terrain.h" +#include "../_internal/private_terrain.h" +#include "block.h" +#include "chunk.h" + +#include +#include +#include +#include + +//#define terrain_c_test_debug + +/* Definition of the missing global the scheduler is looking for */ +struct world_struct *world; + +/* Checks to see if _get_terrain_hight_at_coordinates is consistant. */ +void test_terrain_height_is_consistent(void) +{ + float height_one = _get_terrain_height_at_coordinates(10, 10); + float height_two = _get_terrain_height_at_coordinates(10, 10); + + TEST_ASSERT_EQUAL_FLOAT(height_one, height_two); +} + +void test_cave_probability_at_high_altitude(void) +{ + bool has_cave = _is_cave_present_at_coordinates(0, 100, 0, 10); + + TEST_ASSERT_FALSE(has_cave); +} + +/* Step 1: initialize a chunk with location 0, 0, 0. + * Step 2: run the terrain generator over the chunk. + * Step 3: verify that the surface of the chunk IS NOT made of air. + * Step 4: veridy that blocks 1 unit above the surface (within the same chunk) IS air. + * Step 5: clean up. + */ +void test_generate_terrain_fuzzer(void) +{ + /* Step 1: Set up logging and randomness */ + unsigned int seed = (unsigned int)time(NULL); + + FILE *log = fopen("test_logs/last_test_random_vars.txt", "w"); + fprintf(log, "Seed: %u\n", seed); + srand(seed); + + for (int i = 0; i < 100; i++) + { + /* Step 2: Generate a random world position aligned to 16x grid */ + // We multiply by 16 to ensure the chunk is grid-aligned + chunk_position_struct world_pos = { + .x = (rand() % 2000 - 1000) * chunk_size, + .y = (rand() % 10 - 5) * chunk_size, // Focus near sea level + .z = (rand() % 2000 - 1000) * chunk_size + }; + + fprintf(log, "Chunk %d: x:%" PRId32 " y:%" PRId32 " z:%" PRId32 "\n", + i, + (int32_t)world_pos.x, + (int32_t)world_pos.y, + (int32_t)world_pos.z); + + struct chunk_struct *chunk = initialize_chunk(world_pos); + generate_terrain(chunk, &world_pos); + + /* Step 3: Verify the internal data */ + for (uint8_t x = 0; x < chunk_size; x++) + { + for (uint8_t z = 0; z < chunk_size; z++) + { + int32_t world_x = (int32_t)world_pos.x + x; + int32_t world_z = (int32_t)world_pos.z + z; + + int32_t surface_y = + (int32_t)floorf( + _get_terrain_height_at_coordinates(world_x, + world_z)); + + // Convert world surface to local chunk space + int32_t local_y = surface_y - (int32_t)world_pos.y; + + if (local_y >= 0 && local_y < chunk_size) + { + uint16_t block = + chunk->block_data[index_chunk(x, local_y, z)]; + + if (block == block_air) + { + bool is_cave = + _is_cave_present_at_coordinates + (world_x, + local_y + (int32_t)world_pos.y, + world_z, + surface_y + ); + if (!is_cave) + { + fclose(log); + TEST_FAIL_MESSAGE("Found AIR at s" + "urface where no " + "cave exists!"); + } + } + } + } + } + destroy_chunk(chunk); + } + fclose(log); +} + +void run_terrain_tests(void) +{ + RUN_TEST(test_terrain_height_is_consistent); + RUN_TEST(test_cave_probability_at_high_altitude); + RUN_TEST(test_generate_terrain_fuzzer); +} diff --git a/source_code/terrain_module/terrain/tests/test_terrain.h b/source_code/terrain_module/terrain/tests/test_terrain.h new file mode 100644 index 0000000..f55fbbc --- /dev/null +++ b/source_code/terrain_module/terrain/tests/test_terrain.h @@ -0,0 +1,13 @@ +#ifndef test_terrain_h +#define test_terrain_h + +void test_terrain_height_is_consistent(void); + +void test_cave_probability_at_high_altitude(void); + +void test_generate_terrain_fuzzer(void); + +void run_terrain_tests(void); + + +#endif diff --git a/source_code/test_main.c b/source_code/test_main.c new file mode 100644 index 0000000..8f01d80 --- /dev/null +++ b/source_code/test_main.c @@ -0,0 +1,25 @@ +/* source_code/test_main.c */ +#include "unity.h" + +/* Forward declarations of the test runner functions + located in your various module test files */ +extern void run_terrain_tests(void); +extern void run_block_tests(void); +extern void run_chunk_tests(void); +extern void run_sunlight_tests(void); +void run_frustum_tests(void); +void run_chunk_mesh_builder_tests(void); + +void setUp(void) {} +void tearDown(void) {} + +int main(void) { + UNITY_BEGIN(); + run_terrain_tests(); + run_block_tests(); + run_chunk_tests(); + run_sunlight_tests(); + run_frustum_tests(); + run_chunk_mesh_builder_tests(); + return UNITY_END(); +} diff --git a/source_code/user_interface_module/user_interface/user_interface.c b/source_code/user_interface_module/user_interface/user_interface.c new file mode 100644 index 0000000..e199ee2 --- /dev/null +++ b/source_code/user_interface_module/user_interface/user_interface.c @@ -0,0 +1,40 @@ +#include "user_interface.h" + +#include "performance.h" +#include "player.h" + +#include "raylib.h" + +#include + +/** + * Renders the 2D overlay, including the crosshair, positional coordinates, + * and performance metrics. + */ +void draw_ui_2D(struct player_struct *player) +{ + int screen_center_coordinate_x = GetScreenWidth() / 2; + int screen_center_coordinate_y = GetScreenHeight() / 2; + + uint8_t crosshair_line_length = 10; + uint8_t crosshair_line_thickness = 2; + + DrawRectangle( + screen_center_coordinate_x - crosshair_line_length, + screen_center_coordinate_y - (crosshair_line_thickness / 2), + crosshair_line_length * 2, + crosshair_line_thickness, + PINK + ); + + DrawRectangle( + screen_center_coordinate_x - (crosshair_line_thickness / 2), + screen_center_coordinate_y - crosshair_line_length, + crosshair_line_thickness, + crosshair_line_length * 2, + PINK + ); + + draw_coordinates(player->camera.position); + draw_fps_counter(); +} diff --git a/source_code/user_interface_module/user_interface/user_interface.h b/source_code/user_interface_module/user_interface/user_interface.h new file mode 100644 index 0000000..84df737 --- /dev/null +++ b/source_code/user_interface_module/user_interface/user_interface.h @@ -0,0 +1,13 @@ +#ifndef ui_h +#define ui_h + +struct player_struct; + +/** + * Renders the 2D overlay, including the crosshair, positional coordinates, + * and performance metrics. + */ +void draw_ui_2D(struct player_struct* player); + + +#endif diff --git a/source_code/utilities_module/chunk_position_to_vector_3/chunk_position_to_vector_3.c b/source_code/utilities_module/chunk_position_to_vector_3/chunk_position_to_vector_3.c new file mode 100644 index 0000000..e84aa8a --- /dev/null +++ b/source_code/utilities_module/chunk_position_to_vector_3/chunk_position_to_vector_3.c @@ -0,0 +1,14 @@ +#include "chunk_position_to_vector_3.h" + +#include "chunk.h" +#include "raylib.h" + +Vector3 chunk_position_to_vector_3(struct chunk_position_struct chunk_position) +{ + return (Vector3) + { + (float)chunk_position.x, + (float)chunk_position.y, + (float)chunk_position.z + }; +} diff --git a/source_code/utilities_module/chunk_position_to_vector_3/chunk_position_to_vector_3.h b/source_code/utilities_module/chunk_position_to_vector_3/chunk_position_to_vector_3.h new file mode 100644 index 0000000..4f33d78 --- /dev/null +++ b/source_code/utilities_module/chunk_position_to_vector_3/chunk_position_to_vector_3.h @@ -0,0 +1,9 @@ +#ifndef chunk_position_to_vector_3_h +#define chunk_position_to_vector_3_h + +struct chunk_position_struct; +struct Vector3; + +struct Vector3 chunk_position_to_vector_3(struct chunk_position_struct chunk_position); + +#endif diff --git a/source_code/utilities_module/config.h b/source_code/utilities_module/config.h new file mode 100644 index 0000000..43645f3 --- /dev/null +++ b/source_code/utilities_module/config.h @@ -0,0 +1,3 @@ +#define max_active_chunks 1000 +#define render_distance 6 +#define vertical_render_distance 3 // chunks below ground only diff --git a/source_code/utilities_module/constants.h b/source_code/utilities_module/constants.h new file mode 100644 index 0000000..50639d8 --- /dev/null +++ b/source_code/utilities_module/constants.h @@ -0,0 +1,6 @@ +#ifndef constants_h +#define constants_h + +#define number_of_planes_for_a_cube 6 + +#endif diff --git a/source_code/utilities_module/error_handlers/null_checker/function_success.c b/source_code/utilities_module/error_handlers/null_checker/function_success.c new file mode 100644 index 0000000..1c6d328 --- /dev/null +++ b/source_code/utilities_module/error_handlers/null_checker/function_success.c @@ -0,0 +1,16 @@ +#include "function_success.h" + +#include +#include +#include + +bool function_success(const uint8_t code, const char* error_message) +{ + if (code == EXIT_SUCCESS) + { + return true; + } + + fprintf(stderr, "\nError: %s\n", error_message); + return false; +} diff --git a/source_code/utilities_module/error_handlers/null_checker/function_success.h b/source_code/utilities_module/error_handlers/null_checker/function_success.h new file mode 100644 index 0000000..40d1bd2 --- /dev/null +++ b/source_code/utilities_module/error_handlers/null_checker/function_success.h @@ -0,0 +1,9 @@ +#ifndef function_success_h +#define function_success_h + +#include +#include + +bool function_success(const uint8_t code, const char* error_message); + +#endif diff --git a/source_code/world_module/world/_internal/private_world.c b/source_code/world_module/world/_internal/private_world.c new file mode 100644 index 0000000..d57fc04 --- /dev/null +++ b/source_code/world_module/world/_internal/private_world.c @@ -0,0 +1,359 @@ +#include "private_world.h" + +#include "player.h" +#include "chunk.h" +#include "chunk_scheduler.h" +#include "chunk_mesh_builder.h" +#include "hashmap.h" +#include "world.h" + +#include "raylib.h" + +#include +#include +#include +#include +#include +#include + +static void _static_find_new_chunks_to_load(struct world_struct* world, + struct player_struct* player); + +static void _static_send_chunks_to_renderer(struct world_struct* world); + +static void _static_schedule_chunks(struct world_struct* world); + +static void _static_update_ready_to_load_list(struct world_struct* world); + +static void _static_clean_hashmap_outside_render_distance(struct world_struct* world, + struct player_struct* player); + +static void _static_find_new_chunks_to_load(struct world_struct* world, + struct player_struct* player) +{ + for (int32_t x = 0; x < render_distance; x++) + { + for (int32_t y = 0; y < vertical_render_distance; y++) + { + for (int32_t z = 0; z < render_distance; z++) + { + chunk_position_struct new_chunk_key = + { + player->occupied_chunks_position.x + + (x * chunk_size), + + player->occupied_chunks_position.y + + (y * chunk_size), + + player->occupied_chunks_position.z + + (z * chunk_size), + }; + + struct worlds_chunk_hashmap_entry new_chunk_entry; + new_chunk_entry.chunk_position = &new_chunk_key; + + pthread_mutex_lock(&world->mutex); + if (hashmap_get(world->active_chunks, + &new_chunk_entry) == NULL) + { + new_chunk_entry.chunk_pointer = + initialize_chunk(new_chunk_key); + + if (new_chunk_entry.chunk_pointer != NULL) + { + hashmap_set(world->active_chunks, + &new_chunk_entry); + } + } + pthread_mutex_unlock(&world->mutex); + } + } + } +} + +static void _static_update_ready_to_load_list(struct world_struct* world) +{ + pthread_mutex_lock(&world->mutex); + size_t iterator = 0; + void *item; + + while (hashmap_iter(world->active_chunks, &iterator, &item)) + { + struct worlds_chunk_hashmap_entry *entry = item; + + chunk_state_enum chunk_build_state = + (chunk_state_enum) + atomic_load_explicit( + &entry->chunk_pointer->build_state, + memory_order_acquire); + + if (chunk_build_state > state_new) + { + continue; + } + + if (world->count_chunks_awaiting_delete < maximum_possible_chunks) + { + world->chunks_awaiting_load + [world->count_chunks_awaiting_load++] = + entry->chunk_pointer; + } + else + { + TraceLog(LOG_ERROR, "chunks_awaiting_delete overflow"); + } + + + } + pthread_mutex_unlock(&world->mutex); +} + +static void _static_schedule_chunks(struct world_struct* world) +{ + while (world->count_chunks_awaiting_load > 0) + { + chunk_struct* chunk = world->chunks_awaiting_load + [--world->count_chunks_awaiting_load]; + + atomic_fetch_add_explicit(&chunk->reference_counter, + 1, + memory_order_acquire); + + schedule_chunk_job(chunk); + } +} + +static void _static_send_chunks_to_renderer(struct world_struct* world) +{ + uint32_t awaiting_draw_index = atomic_load(&world->count_chunks_awaiting_draw); + + while (awaiting_draw_index > 0) + { + upload_chunk_mesh_to_gpu(world->chunks_awaiting_load[awaiting_draw_index], + world->fog_shader, + world->texture_atlas); + awaiting_draw_index = + atomic_fetch_sub_explicit(&world->count_chunks_awaiting_draw, + 1, + memory_order_acq_rel); + sem_post(&world->semaphore_free_slots_in_awaiting_draw); + } +} + +static void _static_clean_hashmap_outside_render_distance(struct world_struct* world, + struct player_struct* player) +{ + pthread_mutex_lock(&world->mutex); + + size_t iterator = 0; + void* item; + + while (hashmap_iter(world->active_chunks, &iterator, &item)) + { + struct worlds_chunk_hashmap_entry* entry = item; + struct chunk_position_struct *position = entry->chunk_position; + + if (position->x < player->negative_borders.x + || position->x > player->positive_borders.x + || position->y < player->negative_borders.y + || position->y > player->positive_borders.y + || position->z < player->negative_borders.z + || position->z > player->positive_borders.z) + { + atomic_store_explicit(&entry->chunk_pointer->marked_for_unload, + true, + memory_order_release); + + hashmap_delete(world->active_chunks, &entry->chunk_position); + + world->chunks_awaiting_delete + [world->count_chunks_awaiting_delete++] = + entry->chunk_pointer; + } + } + + pthread_mutex_unlock(&world->mutex); +} + +world_struct* _initialize_world(struct world_struct* world, + world_struct_error_enum* error_code) +{ + if (pthread_mutex_init(&world->mutex, NULL) != 0) + { + *error_code = mutex_failed_to_initialize; + } + + world->texture_atlas = LoadTexture("resources/atlas.png"); + world->active_chunks = hashmap_new( + sizeof(worlds_chunk_hashmap_entry), + 1024, + 0, 0, + _private_active_chunk_hashmap_key_hasher, + _private_chunk_discriminator, + NULL, + NULL + ); + if (world->active_chunks == NULL) + { + pthread_mutex_destroy(&world->mutex); + *error_code = hashmap_failed_to_initialize; + } + + if ( sem_init(&world->semaphore_free_slots_in_awaiting_draw, + 0, + maximum_possible_chunks) == -1) + { + pthread_mutex_destroy(&world->mutex); + hashmap_free(world->active_chunks); + *error_code = semaphore_failed_to_initialize; + } + + world->count_chunks_awaiting_delete = 0; + world->count_chunks_awaiting_draw = 0; + world->count_chunks_awaiting_load = 0; + memset(world->chunks_awaiting_delete, + 0, + sizeof(world->chunks_awaiting_delete)); + memset(world->chunks_awaiting_draw, + 0, + sizeof(world->chunks_awaiting_draw)); + memset(world->chunks_awaiting_load, + 0, + sizeof(world->chunks_awaiting_load)); + + world->fog_density = 0.019f; + world->fog_start = 1.0f; + + world->fog_shader = LoadShader("resources/shaders/fog.vs", + "resources/shaders/fog.fs"); + + world->fog_density_location = GetShaderLocation(world->fog_shader, + "fogDensity"); + + world->fog_start_location = GetShaderLocation(world->fog_shader, + "fogStart"); + + Matrix identity = MatrixIdentity(); + SetShaderValueMatrix(world->fog_shader, + GetShaderLocation(world->fog_shader, + "matModel"), + identity); + + world->fog_shader.locs[SHADER_LOC_VECTOR_VIEW] = + GetShaderLocation(world->fog_shader, + "viewPos"); + return world; +} + +void _free_world(struct world_struct* world) +{ + UnloadShader(world->fog_shader); + + stop_chunk_scheduler(); + + if (world->active_chunks != NULL) + { + hashmap_free(world->active_chunks); + } + + sem_destroy(&world->semaphore_free_slots_in_awaiting_draw); + + UnloadTexture(world->texture_atlas); + pthread_mutex_destroy(&world->mutex); +} + + +uint64_t _private_active_chunk_hashmap_key_hasher(const void *item, + uint64_t seed_zero, + uint64_t seed_one) +{ + const worlds_chunk_hashmap_entry *entry = item; + return hashmap_sip(&entry->chunk_position, + sizeof(chunk_position_struct), + seed_zero, seed_one); +} + +void _private_update_shader_values(struct world_struct* world, + struct player_struct* player) +{ + const float fog_limit = chunk_size * render_distance; + float player_y = player->camera.position.y; + float altitude_factor = player_y / 200.0f; + + if (altitude_factor < 0.0f) + { + altitude_factor = 0.0f; + } + + if (altitude_factor > 1.0f) + { + altitude_factor = 1.0f; + } + + const float maximum_fog_thickness = fog_limit; + const float minimum_fog_thickness = 48.0f; + + float fogThickness = maximum_fog_thickness + + (minimum_fog_thickness - maximum_fog_thickness) * + altitude_factor; + world->fog_start = fog_limit - fogThickness; + + if (world->fog_start < 0.0f) + { + world->fog_start = 0.0f; + } + + const float total_fog_mass = 2.146f; + world->fog_density = total_fog_mass / fogThickness; + + SetShaderValue(world->fog_shader, + world->fog_density_location, + &world->fog_density, + SHADER_UNIFORM_FLOAT); + + SetShaderValue(world->fog_shader, + world->fog_start_location, + &world->fog_start, + SHADER_UNIFORM_FLOAT); + + SetShaderValue(world->fog_shader, + world->fog_shader.locs[SHADER_LOC_VECTOR_VIEW], + &player->camera.position, + SHADER_UNIFORM_VEC3); +} + +int _private_chunk_discriminator(const void *element_a, + const void *element_b, + void *user_data) +{ + (void)user_data; + const worlds_chunk_hashmap_entry *entry_a = element_a; + const worlds_chunk_hashmap_entry *entry_b = element_b; + + if (entry_a->chunk_position->x == entry_b->chunk_position->x + && entry_a->chunk_position->y == entry_b->chunk_position->y + && entry_a->chunk_position->z == entry_b->chunk_position->z) + { + return 0; + } + return 1; +} + +void _private_update_which_chunks_are_active(struct world_struct* world, + struct player_struct* player) +{ + if (!player->has_entered_new_chunk) + { + return; + } + else + { + player->has_entered_new_chunk = false; + } + + _static_find_new_chunks_to_load(world, player); + _static_update_ready_to_load_list(world); + _static_schedule_chunks(world); + _static_send_chunks_to_renderer(world); + _static_clean_hashmap_outside_render_distance(world, player); +} diff --git a/source_code/world_module/world/_internal/private_world.h b/source_code/world_module/world/_internal/private_world.h new file mode 100644 index 0000000..a2125e3 --- /dev/null +++ b/source_code/world_module/world/_internal/private_world.h @@ -0,0 +1,39 @@ +#ifndef private_world_h +#define private_world_h + +#include + +struct chunk_position_struct; +struct player_struct; +struct world_struct; + +typedef enum world_struct_error_enum +{ + okay = 0, + mutex_failed_to_initialize, + hashmap_failed_to_initialize, + semaphore_failed_to_initialize +} world_struct_error_enum; + +struct world_struct* _initialize_world(struct world_struct* world, + world_struct_error_enum* error_code); + +void _free_world(struct world_struct* world); + +uint64_t _private_active_chunk_hashmap_key_hasher(const void *item, + uint64_t seed_zero, + uint64_t seed_one); + +void _private_update_shader_values(struct world_struct* world, + struct player_struct* player); + +int _private_chunk_discriminator(const void *element_a, + const void *element_b, + void *user_data); + +void _private_update_which_chunks_are_active(struct world_struct* world, + struct player_struct* player); + + + +#endif diff --git a/source_code/world_module/world/world.c b/source_code/world_module/world/world.c new file mode 100644 index 0000000..9bb0152 --- /dev/null +++ b/source_code/world_module/world/world.c @@ -0,0 +1,84 @@ +#include "world.h" +#include "_internal/private_world.h" + +#include "chunk.h" +#include "chunk_mesh_builder.h" +#include "chunk_scheduler.h" +#include "player.h" + +#include +#include +#include +#include +#include +#include + +uint8_t create_world(struct world_struct** world) +{ + *world = (world_struct *) calloc(1, sizeof(world_struct)); + + if(*world == NULL) + { + return EXIT_FAILURE; + } + + world_struct_error_enum error_code = okay; + _initialize_world(*world, &error_code); + + switch (error_code) + { + case mutex_failed_to_initialize: + fprintf(stderr, "Error: Failed to initialize world->mutex\n"); + assert(false); + break; + case hashmap_failed_to_initialize: + fprintf(stderr, "Error: Failed to initialize world->hashmap\n"); + assert(false); + break; + case semaphore_failed_to_initialize: + fprintf(stderr, "Error: Failed to initialize world->semaphore\n"); + assert(false); + break; + default: + return EXIT_SUCCESS; + } + return EXIT_FAILURE; +} + +void destroy_world(struct world_struct **world) +{ + if (world == NULL) + { + return; + } + _free_world(*world); +} + + +void update_world(struct world_struct* world, + struct player_struct* player) +{ + _private_update_which_chunks_are_active(world, player); +} + +void destroy_inactive_chunks(struct world_struct* world) +{ + if (world == NULL) + { + return; + } + + for (uint32_t i = 0; i < world->count_chunks_awaiting_delete; i++) + { + if (world->chunks_awaiting_delete[i] != NULL) + { + if(world->chunks_awaiting_delete[i]->reference_counter != 0) + { + continue; + } + destroy_chunk(world->chunks_awaiting_delete[i]); + world->chunks_awaiting_delete[i] = NULL; + } + } + world->count_chunks_awaiting_delete = 0; +} diff --git a/source_code/world_module/world/world.h b/source_code/world_module/world/world.h new file mode 100644 index 0000000..fca90c9 --- /dev/null +++ b/source_code/world_module/world/world.h @@ -0,0 +1,108 @@ +#ifndef world_h +#define world_h + +#include "config.h" + +#include "raylib.h" + +#include +#include +#include + +struct chunk_position_struct; +struct chunk; +struct player_struct; +struct chunk_struct; +struct dynamic_list_struct; +struct hashmap; + + +#define maximum_possible_chunks render_distance*vertical_render_distance*render_distance + +/** + * @brief Hashmap entry structure for world chunks + * + * Associates a chunk position with its corresponding chunk data. + * Used as the key-value pair in the active chunks hashmap. + */ +typedef struct worlds_chunk_hashmap_entry +{ + struct chunk_position_struct* chunk_position; + struct chunk_struct* chunk_pointer; +} worlds_chunk_hashmap_entry; + +/** + * @brief World structure + * + * Main world state management structure. + * Contains all data related to world generation, rendering, and chunk management. + */ +typedef struct world_struct +{ + float fog_density; + Shader fog_shader; + int fog_density_location; + int fog_start_location; + float fog_start; + struct hashmap* active_chunks; + pthread_mutex_t mutex; + _Atomic uint32_t count_chunks_awaiting_draw; + uint32_t count_chunks_awaiting_load; + uint32_t count_chunks_awaiting_delete; + sem_t semaphore_free_slots_in_awaiting_draw; + struct chunk_struct* chunks_awaiting_draw[maximum_possible_chunks]; + struct chunk_struct* chunks_awaiting_load[maximum_possible_chunks]; + struct chunk_struct* chunks_awaiting_delete[maximum_possible_chunks]; + Texture2D texture_atlas; +} world_struct; + +/** + * @brief Creates a new world instance + * + * Allocates memory for a new world structure and initializes all subsystems + * including mutexes, hashmaps, and semaphores. + * + * @param world Double pointer to world structure to be created + * @return 0 on success, non-zero on failure + * @retval 0 Successfully created world + * @retval non-zero Failed to create world (memory allocation or initialization error) + */ +uint8_t create_world(struct world_struct** world); + +/** + * @brief Destroys a world instance + * + * Frees all memory associated with the world and cleans up all subsystems. + * + * @param world Double pointer to world structure to destroy + * @pre world must not be NULL + * @post world is set to NULL + */ +void destroy_world(struct world_struct** world); + +/** + * @brief Updates world state + * + * Performs all world updates including chunk management, rendering preparation, + * and chunk scheduling for generation. + * + * @param world Pointer to world structure + * @param player Pointer to player structure + * @pre world must not be NULL + * @pre player must not be NULL + */ +void update_world(struct world_struct* world, + struct player_struct* player); + +/** + * @brief Destroys inactive chunks + * + * Cleans up chunks that are no longer needed and have been marked for deletion. + * Checks reference counters to ensure safe destruction. + * + * @param world Pointer to world structure + * @pre world must not be NULL + */ +void destroy_inactive_chunks(struct world_struct* world); + +#endif diff --git a/source_code/world_querier_module/world_querier/_internal/private_world_querier.c b/source_code/world_querier_module/world_querier/_internal/private_world_querier.c new file mode 100644 index 0000000..dbf6aa5 --- /dev/null +++ b/source_code/world_querier_module/world_querier/_internal/private_world_querier.c @@ -0,0 +1,110 @@ +#include "private_world_querier.h" + +#include "chunk.h" +#include "hashmap.h" +#include "world.h" + +#include +#include + +/** + * Checks the solidity of a block at a global world coordinate. + * This function identifies the correct chunk via the world hashmap and + * samples the block data. + * It assumes the world mutex is already locked by the caller to ensure thread safety. + */ +bool is_block_solid_at___thread_unsafe(struct world_struct* world, + int32_t world_coordinate_x, + int32_t world_coordinate_y, + int32_t world_coordinate_z) +{ + + const int32_t chunk_origin_x = world_coordinate_x / chunk_size * chunk_size; + const int32_t chunk_origin_y = world_coordinate_y / chunk_size * chunk_size; + const int32_t chunk_origin_z = world_coordinate_z / chunk_size * chunk_size; + + chunk_position_struct chunk_position = + { + .x = chunk_origin_x, + .y = chunk_origin_y, + .z = chunk_origin_z, + }; + + worlds_chunk_hashmap_entry entry_to_query = + { + .chunk_position = &chunk_position, + .chunk_pointer = NULL + }; + + const worlds_chunk_hashmap_entry* search_result = + hashmap_get(world->active_chunks, + &entry_to_query); + + if (search_result == NULL || search_result->chunk_pointer == NULL) + { + return false; + } + + const int32_t local_block_x = world_coordinate_x - chunk_origin_x; + const int32_t local_block_y = world_coordinate_y - chunk_origin_y; + const int32_t local_block_z = world_coordinate_z - chunk_origin_z; + + const uint32_t block_index = index_chunk(local_block_x, + local_block_y, + local_block_z); + + bool result = search_result->chunk_pointer->block_data[block_index] != 0; + return result; +} + + +/** + * Queries the world for a light level at a specific global coordinate. + * This function assumes the world mutex is already held by the calling thread. + */ +uint8_t get_light_at___thread_unsafe(struct world_struct* world, + int32_t world_coordinate_x, + int32_t world_coordinate_y, + int32_t world_coordinate_z) +{ + const int32_t chunk_origin_x = + (int32_t)floorf((float)world_coordinate_x / chunk_size) * chunk_size; + const int32_t chunk_origin_y = + (int32_t)floorf((float)world_coordinate_y / chunk_size) * chunk_size; + const int32_t chunk_origin_z = + (int32_t)floorf((float)world_coordinate_z / chunk_size) * chunk_size; + + + chunk_position_struct chunk_position = + { + .x = chunk_origin_x, + .y = chunk_origin_y, + .z = chunk_origin_z, + }; + + worlds_chunk_hashmap_entry entry_to_query = + { + .chunk_position = &chunk_position, + .chunk_pointer = NULL + }; + + const worlds_chunk_hashmap_entry* search_result = + hashmap_get(world->active_chunks, + &entry_to_query); + + if (search_result == NULL || search_result->chunk_pointer == NULL) + { + return 15; + } + + const int32_t local_block_x = world_coordinate_x - chunk_origin_x; + const int32_t local_block_y = world_coordinate_y - chunk_origin_y; + const int32_t local_block_z = world_coordinate_z - chunk_origin_z; + + const uint32_t block_index = index_chunk(local_block_x, + local_block_y, + local_block_z); + + uint8_t light_level = search_result->chunk_pointer->light_data[block_index]; + return light_level; +} diff --git a/source_code/world_querier_module/world_querier/_internal/private_world_querier.h b/source_code/world_querier_module/world_querier/_internal/private_world_querier.h new file mode 100644 index 0000000..3afc00c --- /dev/null +++ b/source_code/world_querier_module/world_querier/_internal/private_world_querier.h @@ -0,0 +1,29 @@ +#ifndef private_world_querier_h +#define private_world_querier_h + +#include +#include + +struct world_struct; + +/** + * Checks the solidity of a block at a global world coordinate. + * This function identifies the correct chunk via the world hashmap and + * samples the block data. + * It assumes the world mutex is already locked by the caller to ensure thread safety. + */ +bool is_block_solid_at___thread_unsafe(struct world_struct* world, + int32_t world_coordinate_x, + int32_t world_coordinate_y, + int32_t world_coordinate_z); + +/** + * Queries the world for a light level at a specific global coordinate. + * This function assumes the world mutex is already held by the calling thread. + */ +uint8_t get_light_at___thread_unsafe(struct world_struct* world, + int32_t world_coordinate_x, + int32_t world_coordinate_y, + int32_t world_coordinate_z); + +#endif diff --git a/source_code/world_querier_module/world_querier/world_querier.c b/source_code/world_querier_module/world_querier/world_querier.c new file mode 100644 index 0000000..93b393a --- /dev/null +++ b/source_code/world_querier_module/world_querier/world_querier.c @@ -0,0 +1,111 @@ +#include "world_querier.h" +#include "_internal/private_world_querier.h" + +#include "hashmap.h" +#include "world.h" +#include "chunk.h" + +#include +#include +#include + +/** + * Determines if a block adjacent to the specified local coordinates is solid. + * This function handles the logic for both intra-chunk checks (direct array access) + * and inter-chunk checks + * (using world-space translation for neighbors in adjacent chunks). + */ +bool is_neighbor_solid(struct chunk_struct *neighbors[3][3][3], + struct chunk_struct *chunk, + int32_t local_x, int32_t local_y, int32_t local_z, + int8_t offset_x, int8_t offset_y, int8_t offset_z) +{ + const int32_t neighbor_local_x = local_x + offset_x; + const int32_t neighbor_local_y = local_y + offset_y; + const int32_t neighbor_local_z = local_z + offset_z; + + const bool is_inside_current_chunk = + (neighbor_local_x >= 0 && neighbor_local_x < chunk_size) && + (neighbor_local_y >= 0 && neighbor_local_y < chunk_size) && + (neighbor_local_z >= 0 && neighbor_local_z < chunk_size); + + if (is_inside_current_chunk) + { + const uint32_t block_index = index_chunk((uint8_t)neighbor_local_x, + (uint8_t)neighbor_local_y, + (uint8_t)neighbor_local_z); + return chunk->block_data[block_index] != 0; + } + + // Determine which neighbor chunk to use (index 0, 1, or 2) + // If neighbor_local_x < 0, we want index 0 (negative offset) + // If neighbor_local_x >= chunk_size, we want index 2 (positive offset) + // Otherwise, we want index 1 (the current chunk's axis) + const int8_t array_index_x = (neighbor_local_x < 0) ? 0 : (neighbor_local_x >= chunk_size ? 2 : 1); + const int8_t array_index_y = (neighbor_local_y < 0) ? 0 : (neighbor_local_y >= chunk_size ? 2 : 1); + const int8_t array_index_z = (neighbor_local_z < 0) ? 0 : (neighbor_local_z >= chunk_size ? 2 : 1); + + struct chunk_struct *target_chunk = neighbors[array_index_x][array_index_y][array_index_z]; + + if (target_chunk == NULL) + { + return false; + } + + // Convert the coordinate that "overflowed" back into the 0 to (chunk_size - 1) range + // for the target neighbor chunk. + const uint8_t wrapped_x = (uint8_t)((neighbor_local_x % chunk_size + chunk_size) % chunk_size); + const uint8_t wrapped_y = (uint8_t)((neighbor_local_y % chunk_size + chunk_size) % chunk_size); + const uint8_t wrapped_z = (uint8_t)((neighbor_local_z % chunk_size + chunk_size) % chunk_size); + + const uint32_t block_index = index_chunk(wrapped_x, wrapped_y, wrapped_z); + + return target_chunk->block_data[block_index] != 0; +} + +/** + * Retrieves the light level from a neighboring coordinate. + * If the coordinates are within the current chunk, it accesses the local array. + * Otherwise, it translates the position to world space to query neighboring chunks. + */ +uint8_t get_light_at_neighbor(struct chunk_struct *neighbors[3][3][3], + struct chunk_struct *chunk, + int32_t neighbor_local_x, + int32_t neighbor_local_y, + int32_t neighbor_local_z) +{ + const bool is_inside_current_chunk = + (neighbor_local_x >= 0 && neighbor_local_x < chunk_size) && + (neighbor_local_y >= 0 && neighbor_local_y < chunk_size) && + (neighbor_local_z >= 0 && neighbor_local_z < chunk_size); + + if (is_inside_current_chunk) + { + const uint32_t block_index = index_chunk((uint8_t)neighbor_local_x, + (uint8_t)neighbor_local_y, + (uint8_t)neighbor_local_z); + return chunk->light_data[block_index]; + } + + // Determine which neighbor chunk to use (index 0, 1, or 2) + const int8_t array_index_x = (neighbor_local_x < 0) ? 0 : (neighbor_local_x >= chunk_size ? 2 : 1); + const int8_t array_index_y = (neighbor_local_y < 0) ? 0 : (neighbor_local_y >= chunk_size ? 2 : 1); + const int8_t array_index_z = (neighbor_local_z < 0) ? 0 : (neighbor_local_z >= chunk_size ? 2 : 1); + + struct chunk_struct *target_chunk = neighbors[array_index_x][array_index_y][array_index_z]; + + if (target_chunk == NULL) + { + // Return maximum light (or your preferred default) if the neighbor doesn't exist + return 15; + } + + // Wrap the coordinates to the 0 to (chunk_size - 1) range for the target neighbor + const uint8_t wrapped_x = (uint8_t)((neighbor_local_x % chunk_size + chunk_size) % chunk_size); + const uint8_t wrapped_y = (uint8_t)((neighbor_local_y % chunk_size + chunk_size) % chunk_size); + const uint8_t wrapped_z = (uint8_t)((neighbor_local_z % chunk_size + chunk_size) % chunk_size); + + const uint32_t block_index = index_chunk(wrapped_x, wrapped_y, wrapped_z); + + return target_chunk->light_data[block_index]; +} diff --git a/source_code/world_querier_module/world_querier/world_querier.h b/source_code/world_querier_module/world_querier/world_querier.h new file mode 100644 index 0000000..13b2f4a --- /dev/null +++ b/source_code/world_querier_module/world_querier/world_querier.h @@ -0,0 +1,33 @@ +#ifndef world_querier_h +#define world_querier_h + +#include +#include + +struct world_struct; +struct chunk_struct; + +/** + * Determines if a block adjacent to the specified local coordinates is solid. + * This function handles the logic for both intra-chunk checks (direct array access) + * and inter-chunk checks + * (using world-space translation for neighbors in adjacent chunks). + */ +bool is_neighbor_solid(struct chunk_struct *neighbors[3][3][3], + struct chunk_struct *chunk, + int32_t local_x, int32_t local_y, int32_t local_z, + int8_t offset_x, int8_t offset_y, int8_t offset_z); + +/** + * Retrieves the light level from a neighboring coordinate. + * If the coordinates are within the current chunk, it accesses the local array. + * Otherwise, it translates the position to world space to query neighboring chunks. + */ +uint8_t get_light_at_neighbor(struct chunk_struct *neighbors[3][3][3], + struct chunk_struct *chunk, + int32_t neighbor_local_x, + int32_t neighbor_local_y, + int32_t neighbor_local_z); + +#endif +