transfered from codeberg

This commit is contained in:
2026-03-26 14:46:39 -05:00
parent 630f28bb7e
commit 5ed2173793
136 changed files with 14932 additions and 0 deletions

10
.gitignore vendored Normal file
View File

@@ -0,0 +1,10 @@
binary/
build/
test_logs/
*.o
.cache/
.clangd/
compile_commands.json
test_logs/*
.DS_Store
Thumbs.db

122
CMakeLists.txt Normal file
View File

@@ -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()

29
clean Executable file
View File

@@ -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."

53
compile Executable file
View File

@@ -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

BIN
resources/atlas.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
resources/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

44
resources/shaders/fog.fs Normal file
View File

@@ -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);
}

29
resources/shaders/fog.vs Normal file
View File

@@ -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);
}

View File

@@ -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
}
}
};

View File

@@ -0,0 +1,8 @@
#ifndef private_block_h
#define private_block_h
#include "block.h"
extern const block_render_data_struct _block_library[];
#endif

View File

@@ -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;
}

View File

@@ -0,0 +1,51 @@
#ifndef block_h
#define block_h
#include <stdint.h>
#include <raylib.h>
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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,45 @@
#include "private_chunk.h"
#include "chunk.h"
#include "raylib.h"
#include <stdatomic.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
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);
}

View File

@@ -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

View File

@@ -0,0 +1,41 @@
#include "chunk.h"
#include "_internal/private_chunk.h"
#include "chunk_mesh_builder.h"
#include <stdlib.h>
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;
}

View File

@@ -0,0 +1,66 @@
#ifndef chunk_h
#define chunk_h
#include "raylib.h"
#include <pthread.h>
#include <stdint.h>
#include <stdatomic.h>
#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

View File

@@ -0,0 +1,56 @@
#include "test_chunk.h"
#include "chunk.h"
#include "unity.h"
#include <stdint.h>
/* 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);
}

View File

@@ -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

View File

@@ -0,0 +1,5 @@
#include "display.h"
const int screen_height = 300;
const int screen_width = 600;

View File

@@ -0,0 +1,8 @@
#ifndef display_h
#define display_h
extern const int screen_width;
extern const int screen_height;
#endif

View File

@@ -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

View File

@@ -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

View File

@@ -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']

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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
# =========================================================================

View File

@@ -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

View File

@@ -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

View File

@@ -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))

View File

@@ -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
//-------------------------------------------

View File

@@ -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

View File

@@ -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 <stdlib.h>')
if @options[:rng_seed] == 0
output.puts('#include <time.h>')
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

View File

@@ -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 << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\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 = "<testsuite name=#{xml_encode_s(@real_test_suite_name)} tests=\"#{@total_tests}\" failures=\"#{@test_failed}\" skips=\"#{@test_ignored}\">"
@array_list.insert(0, heading)
# Push back the closing tag
@array_list.push '</testsuite>'
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 " <testcase classname=#{xml_encode_s(@test_suite)} name=#{xml_encode_s(test_name)} time=#{xml_encode_s((execution_time / 1000.0).to_s)} />"
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 " <testcase classname=#{xml_encode_s(@test_suite)} name=#{xml_encode_s(test_name)} time=#{xml_encode_s((execution_time / 1000.0).to_s)} >"
@array_list.push " <failure type=\"ASSERT FAILED\">#{reason}</failure>"
@array_list.push ' </testcase>'
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 " <testcase classname=#{xml_encode_s(@test_suite)} name=#{xml_encode_s(test_name)} time=#{xml_encode_s((execution_time / 1000.0).to_s)} >"
@array_list.push " <skipped type=\"TEST IGNORED\">#{reason}</skipped>"
@array_list.push ' </testcase>'
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:
# <path>/<test_file>.c:36:test_tc1000_opsys:FAIL: Expected 1 Was 0
# <path>/<test_file>.c:112:test_tc5004_initCanChannel:IGNORE: Not Yet Implemented
# <path>/<test_file>.c:115:test_tc5100_initCanVoidPtrs:PASS
#
# 2. fixture output
# <path>/<test_file>.c:63:TEST(<test_group>, <test_function>):FAIL: Expected 0x00001234 Was 0x00005A5A
# <path>/<test_file>.c:36:TEST(<test_group>, <test_function>):IGNORE
# Note: "PASS" information won't be generated in this mode
#
# 3. fixture output with verbose information ("-v")
# TEST(<test_group, <test_file>)<path>/<test_file>:168::FAIL: Expected 0x8D Was 0x8C
# TEST(<test_group>, <test_file>)<path>/<test_file>:22::IGNORE: This Test Was Ignored On Purpose
# IGNORE_TEST(<test_group, <test_file>)
# TEST(<test_group, <test_file>) 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

View File

@@ -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();
}

View File

@@ -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())

View File

@@ -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 <dir>', 'Look for Unity Results files here.') do |results|
# puts "results #{results}"
options.results_dir = results
end
o.on('-p', '--root_path <path>', 'Prepend this path to files in results.') do |root_path|
options.root_path = root_path
end
o.on('-o', '--output <filename>', '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 <dir> Look for Unity Results files here.'
puts ' -p, --root_path <path> Prepend this path to files in results.'
puts ' -o, --output <filename> 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 "<?xml version='1.0' encoding='utf-8' ?>"
end
def write_suites_header(stream)
stream.puts '<testsuites>'
end
def write_suite_header(counts, stream)
stream.puts "\t<testsuite errors=\"0\" skipped=\"#{counts[:ignored]}\" failures=\"#{counts[:failed]}\" tests=\"#{counts[:total]}\" name=\"unity\">"
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<testcase classname=\"#{@unit_name}\" name=\"#{item[:test]}\" time=\"0\">"
stream.puts "\t\t\t<failure message=\"#{item[:message]}\" type=\"Assertion\"/>"
stream.puts "\t\t\t<system-err>&#xD;[File] #{filename}&#xD;[Line] #{item[:line]}&#xD;</system-err>"
stream.puts "\t\t</testcase>"
end
end
def write_tests(results, stream)
result = results[:successes]
result.each do |item|
stream.puts "\t\t<testcase classname=\"#{@unit_name}\" name=\"#{item[:test]}\" time=\"0\" />"
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<testcase classname=\"#{@unit_name}\" name=\"#{item[:test]}\" time=\"0\">"
stream.puts "\t\t\t<skipped message=\"#{item[:message]}\" type=\"Assertion\"/>"
stream.puts "\t\t\t<system-err>&#xD;[File] #{filename}&#xD;[Line] #{item[:line]}&#xD;</system-err>"
stream.puts "\t\t</testcase>"
end
end
def write_suite_footer(stream)
stream.puts "\t</testsuite>"
end
def write_suites_footer(stream)
stream.puts '</testsuites>'
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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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
// ...
}
}
}
```

View File

@@ -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 <stdio.h>
/**
* @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

View File

@@ -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

View File

@@ -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"
}
}

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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 <stdint.h>
* - define UNITY_EXCLUDE_LIMITS_H to stop attempting to look in <limits.h>
* - 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

File diff suppressed because it is too large Load Diff

View File

@@ -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.

View File

@@ -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 <stdio.h>
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 <stdio.h>
#include <stdint.h>
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 <stdio.h>
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);
}
```

View File

@@ -0,0 +1,69 @@
#include "private_dynamic_list.h"
#include "dynamic_list.h"
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
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;
}

View File

@@ -0,0 +1,38 @@
#ifndef private_dynamic_list_h
#define private_dynamic_list_h
#include "dynamic_list.h"
#include <stdbool.h>
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 */

View File

@@ -0,0 +1,428 @@
#include "dynamic_list.h"
#include "_internal/private_dynamic_list.h"
#include <string.h>
#include <stdlib.h>
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;
}

View File

@@ -0,0 +1,165 @@
#ifndef dynamic_list_h
#define dynamic_list_h
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/** 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 */

View File

@@ -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.

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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 <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#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

View File

@@ -0,0 +1 @@
#include "perlin.h"

View File

@@ -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 <math.h> // 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.
------------------------------------------------------------------------------
*/

View File

@@ -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

View File

@@ -0,0 +1,112 @@
#include "frustum.h"
#include "_internal/private_frustum.h"
#include <math.h>
#include <stdint.h>
/**
* 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;
}

View File

@@ -0,0 +1,38 @@
#ifndef frustum_h
#define frustum_h
#include "raylib.h"
#include <stdbool.h>
#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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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 <stdint.h>
#include <stdlib.h>
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;
}

View File

@@ -0,0 +1,34 @@
#ifndef game_initializer_h
#define game_initializer_h
#include <stdint.h>
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

View File

@@ -0,0 +1,12 @@
#include "inventory.h"
#include <string.h>
/**
* 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));
}

View File

@@ -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

View File

@@ -0,0 +1,23 @@
#include "item.h"
#include <string.h>
/**
* 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;
}

View File

@@ -0,0 +1,57 @@
#ifndef item_h
#define item_h
#include <stdint.h>
#include <stdbool.h>
/**
* 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

View File

@@ -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
};

View File

@@ -0,0 +1,6 @@
#ifndef light_table_h
#define light_table_h
extern const float light_table[16];
#endif

View File

@@ -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.
*/

View File

@@ -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

View File

@@ -0,0 +1,50 @@
#include "sunlight.h"
#include "_internal/private_sunlight.h"
#include "chunk.h"
#include "world.h"
#include "world_querier.h"
#include <stdint.h>
/**
* 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;
}
}
}
}

View File

@@ -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

View File

@@ -0,0 +1,132 @@
#include "test_sunlight.h"
#include "unity.h"
#include "chunk.h"
#include "sunlight.h"
#include "_internal/private_sunlight.h"
#include <string.h>
/* 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);
}

View File

@@ -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

43
source_code/main.c Normal file
View File

@@ -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 <stdlib.h>
// 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;
}

View File

@@ -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 <math.h>
#include <stdint.h>
#include <string.h>
/**
* 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);
}

View File

@@ -0,0 +1,94 @@
#ifndef private_chunk_mesh_builder_h
#define private_chunk_mesh_builder_h
#include "block.h"
#include "raylib.h"
#include <stdint.h>
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

View File

@@ -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 <string.h>
#include <stdio.h>
/**
* 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);
}

View File

@@ -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 <stdint.h>
/**
* 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

View File

@@ -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 <stdlib.h>
#include <string.h>
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);
}

View File

@@ -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

View File

@@ -0,0 +1,25 @@
#include "performance.h"
#include <stdio.h>
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);
}

View File

@@ -0,0 +1,15 @@
#ifndef performance_h
#define performance_h
#include "raylib.h"
#include <stdint.h>
extern uint16_t framerate_limit;
void draw_fps_counter(void);
void draw_coordinates(Vector3 position);
#endif

View File

@@ -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 <float.h>
#include <math.h>
#include <stdatomic.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
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(&current_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;
}
}

View File

@@ -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 <stdbool.h>
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

View File

@@ -0,0 +1,119 @@
#include "player.h"
#include "_internal/private_player.h"
#include "chunk.h"
#include "world.h"
#include "hashmap.h"
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
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);
}

View File

@@ -0,0 +1,57 @@
#ifndef player_h
#define player_h
#include "chunk.h"
#include "inventory.h"
#include "raylib.h"
#include <stdbool.h>
#include <stdint.h>
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

View File

@@ -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

View File

@@ -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);
}

Some files were not shown because too many files have changed in this diff Show More