diff --git a/.github/workflows/meson_ci.yml b/.github/workflows/meson_ci.yml index 749b05ee..0c8fe604 100644 --- a/.github/workflows/meson_ci.yml +++ b/.github/workflows/meson_ci.yml @@ -56,18 +56,8 @@ jobs: - name: Configure run: meson setup builddir_msvc_${{ matrix.msvc_version }} --fatal-meson-warnings -Dwerror=true -Dwith_test=enabled -Dwarning_level=3 - - name: Compile - run: meson compile -C builddir_msvc_${{ matrix.msvc_version }} - - name: Run Tests - run: meson test -C builddir_msvc_${{ matrix.msvc_version }} -v --test-arg='summary ci' - - - name: Upload Test Log - if: failure() - uses: actions/upload-artifact@v4 - with: - name: windows_msvc_${{ matrix.msvc_version }}_meson_testlog - path: builddir_msvc_${{ matrix.msvc_version }}/meson-logs/testlog.txt + run: meson test -C builddir_msvc_${{ matrix.msvc_version }} -v --test-args='verbose=ci' build_macosx: name: Building on macOS with Xcode ${{ matrix.xcode_version }} @@ -96,18 +86,8 @@ jobs: - name: Configure run: meson setup builddir --fatal-meson-warnings -Dwerror=true -Dwith_test=enabled -Dwarning_level=3 - - name: Compile - run: meson compile -C builddir - - name: Run Tests - run: meson test -C builddir -v --test-arg='summary ci' - - - name: Upload Test Log - if: failure() - uses: actions/upload-artifact@v4 - with: - name: macos_xcode_${{ matrix.xcode_version }}_meson_testlog - path: builddir/meson-logs/testlog.txt + run: meson test -C builddir -v --test-args='verbose=ci' build_msys: name: Building on MSYS ${{ matrix.architecture }} @@ -143,18 +123,8 @@ jobs: - name: Configure run: meson setup builddir --fatal-meson-warnings -Dwerror=true -Dwith_test=enabled -Dwarning_level=3 - - name: Compile - run: meson compile -C builddir - - name: Run Tests - run: meson test -C builddir -v --test-arg='summary ci' - - - name: Upload Test Log - if: failure() - uses: actions/upload-artifact@v4 - with: - name: msys_${{ matrix.architecture }}_meson_testlog - path: builddir/meson-logs/testlog.txt + run: meson test -C builddir -v --test-args='verbose=ci' build_mingw: name: Building on MinGW ${{ matrix.architecture }} @@ -194,18 +164,8 @@ jobs: - name: Configure run: meson setup builddir --fatal-meson-warnings -Dwerror=true -Dwith_test=enabled -Dwarning_level=3 - - name: Compile - run: meson compile -C builddir - - name: Run Tests - run: meson test -C builddir -v --test-arg='summary ci' - - - name: Upload Test Log - if: failure() - uses: actions/upload-artifact@v4 - with: - name: mingw_${{ matrix.architecture }}_meson_testlog - path: builddir/meson-logs/testlog.txt + run: meson test -C builddir -v --test-args='verbose=ci' build_posix: name: Build on Linux ${{ matrix.distro }} @@ -247,8 +207,7 @@ jobs: /bin/bash -c " sudo apt update meson setup builddir --fatal-meson-warnings -Dwerror=true -Dwith_test=enabled -Dwarning_level=3 - meson compile -C builddir - meson test -C builddir -v --test-arg='summary ci'" + meson test -C builddir -v --test-args='verbose=ci'" build_cross: name: Building on Bedrock ${{ matrix.architecture }} @@ -342,9 +301,9 @@ jobs: - name: Build the Project run: | - meson compile -C builddir -v + meson compile -C builddir - name: Test the Project run: | - meson test -C builddir -v --test-arg='summary ci' + meson test -C builddir -v --test-args='verbose=ci' diff --git a/README.md b/README.md index c5cfa7f8..15513267 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,17 @@ # ***Fossil Test by Fossil Logic*** -**Fossil Test** is a powerful and flexible unit testing, mocking, and benchmarking suite developed by Fossil Logic to ensure the reliability, clarity, and performance of **C**, **C++**, and **Python** projects. Supporting multiple development methodologies—including Behavior-Driven Development (BDD), Domain-Driven Design (DDD), and Test-Driven Development (TDD)—Fossil Test offers a versatile foundation for building high-quality, maintainable test suites across diverse workflows. Starting with version 1.1.8, it integrates **Jellyfish AI**, an intelligent system designed to apply AI where it makes sense—enhancing test coverage insights, feedback clarity, and overall developer experience. - -The Fossil suite consists of three complementary frameworks to streamline the development and testing process: - -- **Fossil Test**: The core unit testing framework that enables developers to create, manage, and execute unit tests effectively, ensuring each component functions as expected. -- **Fossil Mock**: A dedicated mocking library that simulates complex dependencies. Using mock objects, developers can isolate and thoroughly test individual components, improving the precision and reliability of test coverage. -- **Fossil Mark**: A benchmarking tool that provides detailed performance insights by measuring execution time, identifying bottlenecks, and offering in-depth reporting to optimize code efficiency. - -Together, **Fossil Test**, **Fossil Mock**, and **Fossil Mark** — now supercharged with **Jellyfish AI** — offer a powerful, integrated toolkit for developing, testing, and optimizing robust software, making them an essential asset for developers committed to quality and performance. - ---- - -## **Key Features** +**Fossil Test** is a comprehensive suite for unit testing, mocking, and benchmarking, designed by Fossil Logic to enhance the reliability, clarity, and performance of **C**, **C++**, and **Python** projects. Supporting methodologies like Behavior-Driven Development (BDD), Domain-Driven Design (DDD), and Test-Driven Development (TDD), it caters to diverse workflows with features such as a robust Command-Line Interface (CLI), advanced mocking tools, integrated benchmarking, and parallel test execution. With additional capabilities like customizable output themes, tag-based test filtering, and detailed performance insights, **Fossil Test**, alongside **Fossil Mock**, **Fossil Mark**, and **Fossil Sanity Kit** for testing command-line operations, forms a powerful toolkit for building, testing, and optimizing high-quality, maintainable software. | Feature | Description | |-----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------| -| **Jellyfish AI Integration** | Intelligent assistant for analyzing test coverage, diagnostics, and improving test clarity (available from v1.1.8 onward). | -| **BDD, DDD, and TDD Support** | Supports Behavior-Driven, Domain-Driven, and Test-Driven Development styles, catering to various project methodologies. | -| **Comprehensive Unit Testing** | A full suite of tools for creating, managing, and executing unit tests, ensuring that individual units of code behave as expected. | -| **Mocking Capabilities** | Powerful mocking features allow developers to simulate complex dependencies, ensuring focused and reliable unit tests. | -| **Performance Tracking** | Measures and reports the performance of each test case, helping developers optimize test execution time and performance. | -| **Command-Line Interface (CLI)** | A powerful CLI for running tests, generating reports, and managing the test suite, supporting automation and integration workflows. | - ---- - -*Note: Jellyfish AI is developed as part of the Fossil Logic ecosystem and is tightly integrated with Fossil Test to offer non-intrusive, intelligent guidance throughout the testing process.* +| **Command-Line Interface (CLI)** | A robust CLI for executing tests, managing test suites, and generating reports, enabling seamless automation and integration workflows. | +| **Support for Multiple Testing Styles** | Fully compatible with Behavior-Driven Development (BDD), Domain-Driven Design (DDD), and Test-Driven Development (TDD) methodologies. | +| **Mocking Capabilities** | Advanced mocking tools to simulate complex dependencies, ensuring isolated and precise unit testing. | +| **Benchmarking Tools** | Integrated benchmarking features to measure execution time, identify bottlenecks, and optimize code performance. | +| **Sanity Kit for Command Tests** | A specialized suite for validating command-line tools and scripts, ensuring consistent behavior across environments. | +| **Customizable Output Themes** | Multiple output themes (e.g., fossil, catch, doctest) to tailor the appearance of test results. | +| **Tag-Based Test Filtering** | Organize and execute tests based on custom tags for better test management. | +| **Detailed Performance Insights** | Comprehensive reporting on test execution times and resource usage to aid in performance optimization. | --- @@ -57,7 +43,7 @@ To get started with Fossil Test, ensure you have the following installed: # ====================== [wrap-git] url = https://github.com/fossillogic/fossil-test.git - revision = v1.2.0 + revision = v1.2.1 [provide] fossil-test = fossil_test_dep @@ -83,14 +69,46 @@ The Fossil Test CLI provides an efficient way to run and manage tests directly f |----------------------------------|-----------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------| | `--version` | Displays the current version of Fossil Test. | Useful for verifying the version of the tool in use. | | `--help` | Shows help message with usage instructions. | Provides a quick reference for all available commands. | -| `--info` | Displays detailed information about the test run. | Includes information such as test count, duration, and configuration. | -| `reverse [enable/disable]` | Enables or disables reverse order of test execution. | Useful for debugging or ensuring the tests don't depend on execution order. | -| `shuffle [enable/disable]` | Enables or disables shuffling of test execution order. | Helps identify order-dependent issues in the test suite. | -| `dry-run [enable/disable]` | Enables or disables dry run mode, showing which tests will execute without running them. | Ideal for verifying test selection criteria before actual execution. | -| `repeat ` | Repeats the test suite a specified number of times. | Handy for stress-testing or reproducing intermittent failures. | -| `color [enable/disable]` | Enables or disables colored output. | Enhances readability in supported terminals. | -| `format ` | Selects the output format for test results. | Affects how test data is displayed; useful for visual or machine-parsed output. | -| `summary ` | Sets the level of summary output after test execution. | `ci` is minimal, `jellyfish` is smart test mode, and `plain` is default classic. | +| `--dry-run` | Perform a dry run without executing commands. | Ideal for verifying test selection criteria before actual execution. | +| `--host` | Shows info about the current host system. | Useful for looking up system you are running test on. | +| `run` | Execute tests with optional parameters. | Supports additional options like `--fail-fast` and `--repeat`. | +| `filter` | Filter tests based on criteria. | Options include filtering by test name, suite name, or tags. | +| `sort` | Sort tests by specified criteria. | Allows sorting in ascending or descending order. | +| `shuffle` | Shuffle tests with optional parameters. | Includes options for specifying a seed or shuffle criteria. | +| `color=` | Set color mode (enable, disable, auto). | Enhances readability in supported terminals. | +| `theme=` | Set the theme (fossil, catch, doctest, etc.). | Customizes the appearance of test output. | +| `verbose=` | Set verbosity level (plain, ci, doge). | Adjusts the level of detail in test output. | + +### Run Command Options +| Option | Description | Notes | +|----------------------|-----------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------| +| `--fail-fast` | Stop on the first failure. | Useful for quickly identifying and addressing critical issues. | +| `--only ` | Run only the specified test. | Focuses execution on a single test for debugging or validation purposes. | +| `--repeat ` | Repeat the test a specified number of times. | Useful for stress testing or verifying consistency across multiple runs. | +| `--help` | Show help for the run command. | Provides detailed usage instructions for the `run` command. | + +### Filter Command Options +| Option | Description | Notes | +|----------------------|-----------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------| +| `--test-name ` | Filter by test name. | Enables precise targeting of individual tests. | +| `--suite-name `| Filter by suite name. | Useful for running all tests within a specific suite. | +| `--tag ` | Filter by tag. | Allows grouping and execution of tests based on custom tags. | +| `--help` | Show help for the filter command. | Provides detailed usage instructions for the `filter` command. | + +### Sort Command Options +| Option | Description | Notes | +|----------------------|-----------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------| +| `--by ` | Sort by specified criteria. | Common criteria include execution time, name, or priority. | +| `--order ` | Sort in ascending or descending order. | Helps organize test execution based on preferred order. | +| `--help` | Show help for the sort command. | Provides detailed usage instructions for the `sort` command. | + +### Shuffle Command Options +| Option | Description | +|----------------------|-----------------------------------------------------------------------------------------------| +| `--seed ` | Specify the seed for shuffling. | +| `--count ` | Number of items to shuffle. | +| `--by ` | Shuffle by specified criteria. | +| `--help` | Show help for the shuffle command. | ## Configure Build Options diff --git a/code/logic/common.c b/code/logic/common.c new file mode 100644 index 00000000..bb55a4e4 --- /dev/null +++ b/code/logic/common.c @@ -0,0 +1,1603 @@ +/* + * ----------------------------------------------------------------------------- + * Project: Fossil Logic + * + * This file is part of the Fossil Logic project, which aims to develop high- + * performance, cross-platform applications and libraries. The code contained + * herein is subject to the terms and conditions defined in the project license. + * + * Author: Michael Gene Brockus (Dreamer) + * Date: 04/05/2014 + * + * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. + * ----------------------------------------------------------------------------- + */ +#include "fossil/pizza/common.h" +#include + +// ***************************************************************************** +// macro definitions +// ***************************************************************************** + +#define FOSSIL_PIZZA_VERSION "1.2.0" +#define FOSSIL_PIZZA_AUTHOR "Fossil Logic" +#define FOSSIL_PIZZA_WEBSITE "https://fossillogic.com" + +// ***************************************************************************** +// exported flags +// ***************************************************************************** + +int G_PIZZA_DRY_RUN = 0; +int G_PIZZA_FAIL_FAST = 0; +int G_PIZZA_SKIP = 0; +const char* G_PIZZA_ONLY = null; +int G_PIZZA_REPEAT = 0; +int G_PIZZA_THREADS = 1; +fossil_pizza_cli_theme_t G_PIZZA_THEME = PIZZA_THEME_FOSSIL; +fossil_pizza_cli_verbose_t G_PIZZA_VERBOSE = PIZZA_VERBOSE_PLAIN; + +// ***************************************************************************** +// command pallet +// ***************************************************************************** + +// Lookup tables for valid tags and criteria +static const char* VALID_TAGS[] = { + "fossil", // default tag + "jellyfish", // Jellyfish AI tag + "network", // Network-related tests + "database", // Database-related tests + "ui", // User Interface tests + "api", // API-related tests + "critical", // Critical tests + "non_critical", // Non-critical tests + null // Sentinel to mark the end +}; + +static const char* VALID_CRITERIA[] = { + "name", + "time", + "result", + null // Sentinel to mark the end +}; + +// TODO add --options flag to show all options for commands that take options such as color or theme + +static void _show_help(void) { + pizza_io_printf("{blue}Usage: pizza [options] [command]{reset}\n"); + pizza_io_printf("{blue}Options:{reset}\n"); + pizza_io_printf("{cyan} --version Show version information{reset}\n"); + pizza_io_printf("{cyan} --dry-run Perform a dry run without executing commands{reset}\n"); + pizza_io_printf("{cyan} --host Show information about the current host{reset}\n"); + pizza_io_printf("{cyan} --help Show this help message{reset}\n"); + pizza_io_printf("{blue}Commands:{reset}\n"); + pizza_io_printf("{cyan} run Execute tests with optional parameters{reset}\n"); + pizza_io_printf("{cyan} filter Filter tests based on criteria{reset}\n"); + pizza_io_printf("{cyan} sort Sort tests by specified criteria{reset}\n"); + pizza_io_printf("{cyan} shuffle Shuffle tests with optional parameters{reset}\n"); + pizza_io_printf("{cyan} color= Set color mode (enable, disable, auto){reset}\n"); + pizza_io_printf("{cyan} theme= Set the theme (fossil, catch, doctest, etc.){reset}\n"); + pizza_io_printf("{cyan} verbose= Set verbosity level (plain, ci, doge){reset}\n"); + exit(EXIT_SUCCESS); +} + +// TODO support wildcards for test cases under --only +// TODO support regex for test cases under --only +// TODO support listing multiple test cases under --only + +static void _show_subhelp_run(void) { + pizza_io_printf("{blue}Run command options:{reset}\n"); + pizza_io_printf("{cyan} --fail-fast Stop on the first failure{reset}\n"); + pizza_io_printf("{cyan} --only Run only the specified test{reset}\n"); + pizza_io_printf("{cyan} --repeat Repeat the test a specified number of times{reset}\n"); + pizza_io_printf("{cyan} --help Show help for run command{reset}\n"); + exit(EXIT_SUCCESS); +} + +// TODO support wildcards for test cases under --test-name +// TODO support regex for test cases under --test-name +// TODO support listing multiple test cases under --test-name +// TODO support wildcards for suite names under --suite-name +// TODO support regex for suite names under --suite-name +// TODO support listing multiple suite names under --suite-name +// TODO support regex for tags under --tag +// TODO support listing multiple tags under --tag + +static void _show_subhelp_filter(void) { + pizza_io_printf("{blue}Filter command options:{reset}\n"); + pizza_io_printf("{cyan} --test-name Filter by test name{reset}\n"); + pizza_io_printf("{cyan} --suite-name Filter by suite name{reset}\n"); + pizza_io_printf("{cyan} --tag Filter by tag{reset}\n"); + pizza_io_printf("{cyan} --help Show help for filter command{reset}\n"); + exit(EXIT_SUCCESS); +} + +static void _show_subhelp_sort(void) { + pizza_io_printf("{blue}Sort command options:{reset}\n"); + pizza_io_printf("{cyan} --by Sort by specified criteria{reset}\n"); + pizza_io_printf("{cyan} --order Sort in ascending or descending order{reset}\n"); + pizza_io_printf("{cyan} --help Show help for sort command{reset}\n"); + exit(EXIT_SUCCESS); +} + +static void _show_subhelp_shuffle(void) { + pizza_io_printf("{blue}Shuffle command options:{reset}\n"); + pizza_io_printf("{cyan} --seed Specify the seed for shuffling{reset}\n"); + pizza_io_printf("{cyan} --count Number of items to shuffle{reset}\n"); + pizza_io_printf("{cyan} --by Shuffle by specified criteria{reset}\n"); + pizza_io_printf("{cyan} --help Show help for shuffle command{reset}\n"); + exit(EXIT_SUCCESS); +} + +static void _show_subhelp_color(void) { + pizza_io_printf("{blue}Color command options:{reset}\n"); + pizza_io_printf("{cyan} enable Enable color output{reset}\n"); + pizza_io_printf("{cyan} disable Disable color output{reset}\n"); + pizza_io_printf("{cyan} auto Auto-detect color support{reset}\n"); + exit(EXIT_SUCCESS); +} + +static void _show_subhelp_theme(void) { + pizza_io_printf("{blue}Theme command options:{reset}\n"); + pizza_io_printf("{cyan} fossil Fossil theme (C, C++ Fossil Test Framework){reset}\n"); + pizza_io_printf("{cyan} catch Catch theme (C++ Test Framework){reset}\n"); + pizza_io_printf("{cyan} doctest Doctest theme (C++ Test Framework){reset}\n"); + pizza_io_printf("{cyan} cpputest CppUTest theme (C Test Framework){reset}\n"); + pizza_io_printf("{cyan} tap TAP theme (C Test Framework){reset}\n"); + pizza_io_printf("{cyan} gtest GoogleTest theme (C++ Test Framework){reset}\n"); + pizza_io_printf("{cyan} unity Unity theme (C Test Framework){reset}\n"); + exit(EXIT_SUCCESS); +} + +static void _show_subhelp_verbose(void) { + pizza_io_printf("{blue}Verbose command options:{reset}\n"); + pizza_io_printf("{cyan} plain Plain output{reset}\n"); + pizza_io_printf("{cyan} ci Continuous Integration output{reset}\n"); + pizza_io_printf("{cyan} doge High-transparency verbose output{reset}\n"); + exit(EXIT_SUCCESS); +} + +static void _show_version(void) { + pizza_io_printf("{blue}Pizza Test Version: {cyan}%s{reset}\n", FOSSIL_PIZZA_VERSION); + exit(EXIT_SUCCESS); +} + +// TODO add architecture and CPU information + +static void _show_host(void) { + pizza_sys_hostinfo_system_t system_info; + pizza_sys_hostinfo_memory_t memory_info; + pizza_sys_hostinfo_endianness_t endianness_info; + + if (pizza_sys_hostinfo_get_system(&system_info) == 0) { + pizza_io_printf("{blue}Operating System: {cyan}%s{reset}\n", system_info.os_name); + pizza_io_printf("{blue}OS Version: {cyan}%s{reset}\n", system_info.os_version); + pizza_io_printf("{blue}Kernel Version: {cyan}%s{reset}\n", system_info.kernel_version); + } else { + pizza_io_printf("{red}Error retrieving system information.{reset}\n"); + } + + if (pizza_sys_hostinfo_get_memory(&memory_info) == 0) { + pizza_io_printf("{blue}Total Memory: {cyan}%lu bytes{reset}\n", memory_info.total_memory); + pizza_io_printf("{blue}Free Memory: {cyan}%lu bytes{reset}\n", memory_info.free_memory); + } else { + pizza_io_printf("{red}Error retrieving memory information.{reset}\n"); + } + + if (pizza_sys_hostinfo_get_endianness(&endianness_info) == 0) { + pizza_io_printf("{blue}Endianness: {cyan}%s{reset}\n", + endianness_info.is_little_endian ? "Little-endian" : "Big-endian"); + } else { + pizza_io_printf("{red}Error retrieving endianness information.{reset}\n"); + } + + exit(EXIT_SUCCESS); +} + +fossil_pizza_pallet_t fossil_pizza_pallet_create(int argc, char** argv) { + fossil_pizza_pallet_t pallet = {0}; + + // Parse command-line arguments + for (int i = 1; i < argc; i++) { + if (pizza_io_cstr_compare(argv[i], "--dry-run") == 0) { + G_PIZZA_DRY_RUN = 1; + } else if (pizza_io_cstr_compare(argv[i], "--version") == 0) { + _show_version(); + } else if (pizza_io_cstr_compare(argv[i], "--help") == 0) { + _show_help(); + } else if (pizza_io_cstr_compare(argv[i], "--host") == 0) { + _show_host(); + } else if (pizza_io_cstr_compare(argv[i], "run") == 0) { + pallet.run.fail_fast = 0; + pallet.run.only = null; + pallet.run.repeat = 1; + + for (int j = i + 1; j < argc; j++) { + if (pizza_io_cstr_compare(argv[j], "--fail-fast") == 0) { + pallet.run.fail_fast = 1; + G_PIZZA_FAIL_FAST = 1; + } else if (pizza_io_cstr_compare(argv[j], "--only") == 0 && j + 1 < argc) { + pallet.run.only = argv[++j]; + G_PIZZA_ONLY = pallet.run.only; + } else if (pizza_io_cstr_compare(argv[j], "--repeat") == 0 && j + 1 < argc) { + pallet.run.repeat = atoi(argv[++j]); + G_PIZZA_REPEAT = pallet.run.repeat; + } else if (pizza_io_cstr_compare(argv[j], "--help") == 0) { + _show_subhelp_run(); + } else { + break; + } + } + } else if (pizza_io_cstr_compare(argv[i], "filter") == 0) { + pallet.filter.test_name = null; + pallet.filter.suite_name = null; + pallet.filter.tag = null; + + for (int j = i + 1; j < argc; j++) { + if (pizza_io_cstr_compare(argv[j], "--test-name") == 0 && j + 1 < argc) { + pallet.filter.test_name = argv[++j]; + } else if (pizza_io_cstr_compare(argv[j], "--suite-name") == 0 && j + 1 < argc) { + pallet.filter.suite_name = argv[++j]; + } else if (pizza_io_cstr_compare(argv[j], "--tag") == 0 && j + 1 < argc) { + const char* tag = argv[++j]; + int is_valid_tag = 0; + for (int k = 0; VALID_TAGS[k] != null; k++) { + if (pizza_io_cstr_compare(tag, VALID_TAGS[k]) == 0) { + is_valid_tag = 1; + break; + } + } + if (is_valid_tag) { + pallet.filter.tag = tag; + } else { + pizza_io_printf("{red}Error: Invalid tag '%s'.{reset}\n", tag); + exit(EXIT_FAILURE); + } + } else if (pizza_io_cstr_compare(argv[j], "--help") == 0) { + _show_subhelp_filter(); + } else { + break; + } + } + } else if (pizza_io_cstr_compare(argv[i], "sort") == 0) { + pallet.sort.by = null; + pallet.sort.order = null; + + for (int j = i + 1; j < argc; j++) { + if (pizza_io_cstr_compare(argv[j], "--by") == 0 && j + 1 < argc) { + const char* criteria = argv[++j]; + int is_valid_criteria = 0; + for (int k = 0; VALID_CRITERIA[k] != null; k++) { + if (pizza_io_cstr_compare(criteria, VALID_CRITERIA[k]) == 0) { + is_valid_criteria = 1; + break; + } + } + if (is_valid_criteria) { + pallet.sort.by = criteria; + } else { + pizza_io_printf("{red}Error: Invalid criteria '%s'.{reset}\n", criteria); + exit(EXIT_FAILURE); + } + } else if (pizza_io_cstr_compare(argv[j], "--order") == 0 && j + 1 < argc) { + pallet.sort.order = argv[++j]; + } else if (pizza_io_cstr_compare(argv[j], "--help") == 0) { + _show_subhelp_sort(); + } else { + break; + } + } + } else if (pizza_io_cstr_compare(argv[i], "shuffle") == 0) { + pallet.shuffle.seed = null; + pallet.shuffle.count = 0; + pallet.shuffle.by = null; + + for (int j = i + 1; j < argc; j++) { + if (pizza_io_cstr_compare(argv[j], "--seed") == 0 && j + 1 < argc) { + pallet.shuffle.seed = argv[++j]; + } else if (pizza_io_cstr_compare(argv[j], "--count") == 0 && j + 1 < argc) { + pallet.shuffle.count = atoi(argv[++j]); + } else if (pizza_io_cstr_compare(argv[j], "--by") == 0 && j + 1 < argc) { + pallet.shuffle.by = argv[++j]; + } else if (pizza_io_cstr_compare(argv[j], "--help") == 0) { + _show_subhelp_shuffle(); + } else { + break; + } + } + } else if (strncmp(argv[i], "color=", 6) == 0) { + if (pizza_io_cstr_compare(argv[i] + 6, "enable") == 0) { + PIZZA_IO_COLOR_ENABLE = 1; + } else if (pizza_io_cstr_compare(argv[i] + 6, "disable") == 0) { + PIZZA_IO_COLOR_ENABLE = 0; + } else if (pizza_io_cstr_compare(argv[i] + 6, "auto") == 0) { + if (isatty(STDOUT_FILENO)) { + PIZZA_IO_COLOR_ENABLE = 1; + } else { + PIZZA_IO_COLOR_ENABLE = 0; + } + } + } else if (pizza_io_cstr_compare(argv[i], "color") == 0) { + if (i + 1 < argc && pizza_io_cstr_compare(argv[i + 1], "--help") == 0) { + _show_subhelp_color(); + } + } else if (strncmp(argv[i], "theme=", 6) == 0) { + const char* theme_str = argv[i] + 6; + if (pizza_io_cstr_compare(theme_str, "fossil") == 0) { + pallet.theme = PIZZA_THEME_FOSSIL; + G_PIZZA_THEME = PIZZA_THEME_FOSSIL; + } else if (pizza_io_cstr_compare(theme_str, "catch") == 0) { + pallet.theme = PIZZA_THEME_CATCH; + G_PIZZA_THEME = PIZZA_THEME_CATCH; + } else if (pizza_io_cstr_compare(theme_str, "doctest") == 0) { + pallet.theme = PIZZA_THEME_DOCTEST; + G_PIZZA_THEME = PIZZA_THEME_DOCTEST; + } else if (pizza_io_cstr_compare(theme_str, "cpputest") == 0) { + pallet.theme = PIZZA_THEME_CPPUTEST; + G_PIZZA_THEME = PIZZA_THEME_CPPUTEST; + } else if (pizza_io_cstr_compare(theme_str, "tap") == 0) { + pallet.theme = PIZZA_THEME_TAP; + G_PIZZA_THEME = PIZZA_THEME_TAP; + } else if (pizza_io_cstr_compare(theme_str, "gtest") == 0) { + pallet.theme = PIZZA_THEME_GOOGLETEST; + G_PIZZA_THEME = PIZZA_THEME_GOOGLETEST; + } else if (pizza_io_cstr_compare(theme_str, "unity") == 0) { + pallet.theme = PIZZA_THEME_UNITY; + G_PIZZA_THEME = PIZZA_THEME_UNITY; + + } + } else if (pizza_io_cstr_compare(argv[i], "theme") == 0) { + if (i + 1 < argc && pizza_io_cstr_compare(argv[i + 1], "--help") == 0) { + _show_subhelp_theme(); + } + } else if (strncmp(argv[i], "verbose=", 8) == 0) { + const char* verbose_str = argv[i] + 8; + if (pizza_io_cstr_compare(verbose_str, "plain") == 0) { + pallet.verbose = PIZZA_VERBOSE_PLAIN; + G_PIZZA_VERBOSE = PIZZA_VERBOSE_PLAIN; + } else if (pizza_io_cstr_compare(verbose_str, "ci") == 0) { + pallet.verbose = PIZZA_VERBOSE_CI; + G_PIZZA_VERBOSE = PIZZA_VERBOSE_CI; + } else if (pizza_io_cstr_compare(verbose_str, "doge") == 0) { // means verbose for Pizza Test + pallet.verbose = PIZZA_VERBOSE_DOGE; + G_PIZZA_VERBOSE = PIZZA_VERBOSE_DOGE; + } + } else if (pizza_io_cstr_compare(argv[i], "verbose") == 0) { + if (i + 1 < argc && pizza_io_cstr_compare(argv[i + 1], "--help") == 0) { + _show_subhelp_verbose(); + } + } + } + + return pallet; +} + +// ***************************************************************************** +// Host information +// ***************************************************************************** + +int pizza_sys_hostinfo_get_system(pizza_sys_hostinfo_system_t *info) { + if (!info) return -1; +#ifdef _WIN32 + OSVERSIONINFO osvi; + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + if (!GetVersionEx(&osvi)) return -1; + snprintf(info->os_name, sizeof(info->os_name), "Windows"); + snprintf(info->os_version, sizeof(info->os_version), "%lu.%lu", osvi.dwMajorVersion, osvi.dwMinorVersion); + snprintf(info->kernel_version, sizeof(info->kernel_version), "%lu", osvi.dwBuildNumber); +#elif defined(__APPLE__) + struct utsname sysinfo; + if (uname(&sysinfo) != 0) return -1; + strncpy(info->os_name, sysinfo.sysname, sizeof(info->os_name) - 1); + strncpy(info->os_version, sysinfo.version, sizeof(info->os_version) - 1); + strncpy(info->kernel_version, sysinfo.release, sizeof(info->kernel_version) - 1); +#else + struct utsname sysinfo; + if (uname(&sysinfo) != 0) return -1; + strncpy(info->os_name, sysinfo.sysname, sizeof(info->os_name) - 1); + strncpy(info->os_version, sysinfo.version, sizeof(info->os_version) - 1); + strncpy(info->kernel_version, sysinfo.release, sizeof(info->kernel_version) - 1); +#endif + return 0; +} + +int pizza_sys_hostinfo_get_memory(pizza_sys_hostinfo_memory_t *info) { + if (!info) return -1; +#ifdef _WIN32 + MEMORYSTATUSEX statex; + statex.dwLength = sizeof(statex); + if (!GlobalMemoryStatusEx(&statex)) return -1; + info->total_memory = statex.ullTotalPhys; + info->free_memory = statex.ullAvailPhys; +#elif defined(__APPLE__) + int64_t memsize; + size_t len = sizeof(memsize); + if (sysctlbyname("hw.memsize", &memsize, &len, null, 0) != 0) return -1; + info->total_memory = memsize; + info->free_memory = 0; // macOS does not provide free memory info in the same way +#else + struct sysinfo sys_info; + if (sysinfo(&sys_info) != 0) return -1; + info->total_memory = sys_info.totalram; + info->free_memory = sys_info.freeram; +#endif + return 0; +} + +int pizza_sys_hostinfo_get_endianness(pizza_sys_hostinfo_endianness_t *info) { + if (!info) return -1; + uint16_t test = 0x0001; + info->is_little_endian = (*(uint8_t*)&test) ? 1 : 0; + return 0; +} + +// ***************************************************************************** +// soap sanitizer +// ***************************************************************************** + +#define MAX_CUSTOM_FILTERS 64 + +/** Lookup table for rot-brain words and their suggested replacements */ +static const struct { + const char *bad; + const char *suggested; +} FOSSIL_SOAP_SUGGESTIONS[] = { + {"rizz", "charisma"}, + {"skibidi", "dance"}, + {"yeet", "throw"}, + {"sus", "suspicious"}, + {"vibe", "atmosphere"}, + {"lit", "exciting"}, + {"no cap", "honestly"}, + {"bet", "okay"}, + {"fam", "family"}, + {"bruh", "brother"}, + {"flex", "show off"}, + {"ghost", "ignore"}, + {"goat", "legend"}, + {"gucci", "good"}, + {"hype", "exciting"}, + {"janky", "low-quality"}, + {"lowkey", "somewhat"}, + {"mood", "feeling"}, + {"salty", "bitter"}, + {"shade", "insult"}, + {"slay", "impress"}, + {"snatched", "stylish"}, + {"stan", "superfan"}, + {"tea", "gossip"}, + {"thirsty", "desperate"}, + {"woke", "aware"}, + {"yolo", "live once"}, + {"zaddy", "attractive man"}, + {"drip", "fashion"}, + {"fire", "amazing"}, + {"lol", "funny"}, + {"omg", "surprising"}, + {"brb", "be right back"}, + {"idk", "I don't know"}, + {"imo", "in my opinion"}, + {"lmao", "laughing"}, + {"nvm", "never mind"}, + {"tbh", "to be honest"}, + {"tldr", "too long"}, + {"ttyl", "talk to you later"}, + {"wyd", "what are you doing"}, + {"wtf", "what the heck"}, + {"yolo", "you only live once"}, + {"rot-brain", "stupid"}, + {"rot brain", "stupid"}, + {"rotbrain", "stupid"}, + {null, null} // Sentinel to mark the end +}; + +/** Grammar suggestions for common mistakes */ +static const struct { + const char *incorrect; + const char *correct; +} FOSSIL_SOAP_GRAMMAR_SUGGESTIONS[] = { + {"gonna", "going to"}, + {"ain't", "isn't"}, + {"should of", "should have"}, + {"could of", "could have"}, + {"not never", "never"}, + {"free gift", "gift"}, + {"very unique", "unique"}, + {null, null} // Sentinel to mark the end +}; + +/** Lookup table for sarcastic phrases */ +static const char *SARCASTIC_PHRASES[] = { + "Oh, great", + "Yeah, right", + "Nice job", + "Well done", + "Good luck with that", + "Sure, why not", + "Fantastic", + "Brilliant", + "Wonderful", + "Perfect", + null // Sentinel to mark the end +}; + +/** Lookup table for formal phrases */ +static const char *FORMAL_PHRASES[] = { + "Dear Sir or Madam", + "To whom it may concern", + "Yours sincerely", + "Yours faithfully", + "Best regards", + "Respectfully", + "I would like to", + "I am writing to", + "Please find attached", + "Thank you for your consideration", + null // Sentinel to mark the end +}; + +static char custom_storage[MAX_CUSTOM_FILTERS][64]; +static const char *custom_filters[MAX_CUSTOM_FILTERS] = {0}; + +/** Lookup table for words that need to be skipped due to misdetection */ +static const char *SKIP_WORDS[] = { + "limit", + "size", + null // Sentinel to mark the end +}; + +/** + * @brief Convert leetspeak to normal letters. + */ +static void pizza_io_soap_normalize_leetspeak(char *word) { + for (size_t i = 0; word[i] != '\0'; i++) { + switch (word[i]) { + case '0': word[i] = 'o'; break; + case '1': word[i] = 'i'; break; + case '3': word[i] = 'e'; break; + case '4': word[i] = 'a'; break; + case '5': word[i] = 's'; break; + case '7': word[i] = 't'; break; + case '$': word[i] = 's'; break; + } + } +} + +/** + * @brief Fuzzy matching using Levenshtein distance. + */ +static int fuzzy_match(const char *str1, const char *str2) { + size_t len1 = strlen(str1); + size_t len2 = strlen(str2); + size_t dist[len1 + 1][len2 + 1]; + + for (size_t i = 0; i <= len1; i++) dist[i][0] = i; + for (size_t j = 0; j <= len2; j++) dist[0][j] = j; + + for (size_t i = 1; i <= len1; i++) { + for (size_t j = 1; j <= len2; j++) { + int cost = (str1[i - 1] == str2[j - 1]) ? 0 : 1; + dist[i][j] = fmin(fmin(dist[i - 1][j] + 1, dist[i][j - 1] + 1), dist[i - 1][j - 1] + cost); + } + } + return dist[len1][len2]; +} + +/** + * @brief Check if a word should be skipped. + */ +static int should_skip_word(const char *word) { + for (size_t i = 0; SKIP_WORDS[i] != null; i++) { + if (pizza_io_cstr_compare(word, SKIP_WORDS[i]) == 0) { + return 1; + } + } + return 0; +} + +/** + * @brief Case-insensitive string comparison. + */ +static int custom_strcasecmp(const char *s1, const char *s2) { + while (*s1 && *s2) { + if (tolower((unsigned char)*s1) != tolower((unsigned char)*s2)) { + return tolower((unsigned char)*s1) - tolower((unsigned char)*s2); + } + s1++; + s2++; + } + return tolower((unsigned char)*s1) - tolower((unsigned char)*s2); +} + +/** + * @brief Case-insensitive substring search. + */ +static const char *custom_strcasestr(const char *haystack, const char *needle) { + if (!*needle) return haystack; + + for (; *haystack; haystack++) { + if (tolower((unsigned char)*haystack) == tolower((unsigned char)*needle)) { + const char *h = haystack, *n = needle; + while (*h && *n && tolower((unsigned char)*h) == tolower((unsigned char)*n)) { + h++; + n++; + } + if (!*n) return haystack; + } + } + return null; +} + +/** + * @brief Look up a suggested alternative for a given word, checking both custom filters and predefined suggestions. + */ +static const char *pizza_io_soap_get_suggestion(const char *word) { + if (should_skip_word(word)) { + return null; + } + + // Check in custom filters first + for (size_t i = 0; i < MAX_CUSTOM_FILTERS && custom_filters[i] != null; i++) { + if (custom_strcasecmp(word, custom_filters[i]) == 0) { + return custom_filters[i]; // Use the custom filter word itself as suggestion + } + if (fuzzy_match(word, custom_filters[i]) <= 2) { + return custom_filters[i]; // Return fuzzy match result + } + } + + // Check in predefined suggestions + for (size_t i = 0; FOSSIL_SOAP_SUGGESTIONS[i].bad != null; i++) { + if (custom_strcasecmp(word, FOSSIL_SOAP_SUGGESTIONS[i].bad) == 0) { + return FOSSIL_SOAP_SUGGESTIONS[i].suggested; + } + } + + // Check in grammar suggestions + for (size_t i = 0; FOSSIL_SOAP_GRAMMAR_SUGGESTIONS[i].incorrect != null; i++) { + if (custom_strcasecmp(word, FOSSIL_SOAP_GRAMMAR_SUGGESTIONS[i].incorrect) == 0) { + return FOSSIL_SOAP_GRAMMAR_SUGGESTIONS[i].correct; + } + } + + return null; +} + +/** + * @brief Sanitize input text by removing or replacing "rot-brain" and meme-based language. + * @param censor_char Character to use for censored words (e.g., "*" or "#"). + */ +char *pizza_io_soap_sanitize(const char *text) { + if (!text) return null; + + size_t len = strlen(text); + char *output = (char *)malloc(len * 2 + 1); // Allocate more space to handle replacements + if (!output) return null; + + size_t out_idx = 0; + char word[64]; + size_t word_idx = 0; + + for (size_t i = 0; text[i] != '\0'; i++) { + if (isalnum((unsigned char)text[i]) || text[i] == '\'' || text[i] == '-') { + word[word_idx++] = text[i]; + if (word_idx >= sizeof(word) - 1) word_idx = sizeof(word) - 2; + } else { + word[word_idx] = '\0'; + pizza_io_soap_normalize_leetspeak(word); + const char *suggested = pizza_io_soap_get_suggestion(word); + if (word_idx > 0 && suggested && !should_skip_word(word)) { + for (size_t j = 0; j < strlen(suggested); j++) { + output[out_idx++] = suggested[j]; + } + } else { + for (size_t j = 0; j < word_idx; j++) { + output[out_idx++] = word[j]; + } + } + output[out_idx++] = text[i]; + word_idx = 0; + } + } + word[word_idx] = '\0'; + pizza_io_soap_normalize_leetspeak(word); + const char *suggested = pizza_io_soap_get_suggestion(word); + if (word_idx > 0 && suggested && !should_skip_word(word)) { + for (size_t j = 0; j < strlen(suggested); j++) { + output[out_idx++] = suggested[j]; + } + } else { + for (size_t j = 0; j < word_idx; j++) { + output[out_idx++] = word[j]; + } + } + output[out_idx] = '\0'; + return output; +} + +char *pizza_io_soap_suggest(const char *text) { + if (!text) return null; + + size_t len = strlen(text); + char *output = (char *)malloc(len * 2 + 64); // Allocate more space to handle replacements + if (!output) return null; + + size_t out_idx = 0; + char word[64]; + size_t word_idx = 0; + + for (size_t i = 0; text[i] != '\0'; i++) { + if (isalnum((unsigned char)text[i]) || text[i] == '\'' || text[i] == '-') { + word[word_idx++] = text[i]; + if (word_idx >= sizeof(word) - 1) word_idx = sizeof(word) - 2; + } else { + word[word_idx] = '\0'; + pizza_io_soap_normalize_leetspeak(word); + const char *suggested = pizza_io_soap_get_suggestion(word); + if (word_idx > 0 && suggested && !should_skip_word(word)) { + strncpy(&output[out_idx], suggested, len * 2 + 64 - out_idx); + out_idx += strlen(suggested); + } else { + strncpy(&output[out_idx], word, len * 2 + 64 - out_idx); + out_idx += word_idx; + } + output[out_idx++] = text[i]; + word_idx = 0; + } + } + word[word_idx] = '\0'; + pizza_io_soap_normalize_leetspeak(word); + const char *suggested = pizza_io_soap_get_suggestion(word); + if (word_idx > 0 && suggested && !should_skip_word(word)) { + strncpy(&output[out_idx], suggested, len * 2 + 64 - out_idx); + out_idx += strlen(suggested); + } else { + strncpy(&output[out_idx], word, len * 2 + 64 - out_idx); + out_idx += word_idx; + } + output[out_idx] = '\0'; + return output; +} + +/** + * @brief Add a custom word or phrase to the filter. + */ +int pizza_io_soap_add_custom_filter(const char *phrase) { + for (size_t i = 0; i < MAX_CUSTOM_FILTERS; i++) { + if (custom_filters[i] == null) { + size_t j = 0; + while (phrase[j] != '\0' && j < sizeof(custom_storage[i]) - 1) { + custom_storage[i][j] = tolower((unsigned char)phrase[j]); + j++; + } + custom_storage[i][j] = '\0'; + custom_filters[i] = custom_storage[i]; + return 0; + } + } + return -1; +} + +/** + * @brief Clear all custom filters. + */ +void pizza_io_soap_clear_custom_filters(void) { + memset(custom_filters, 0, sizeof(custom_filters)); +} + +const char *pizza_io_soap_detect_tone(const char *text) { + for (size_t i = 0; SARCASTIC_PHRASES[i] != null; i++) { + if (custom_strcasestr(text, SARCASTIC_PHRASES[i])) { + return "sarcastic"; + } + } + + for (size_t i = 0; FORMAL_PHRASES[i] != null; i++) { + if (custom_strcasestr(text, FORMAL_PHRASES[i])) { + return "formal"; + } + } + + return "casual"; +} + +int pizza_io_is_rot_brain(const char *text) { + if (!text) return 0; + + for (size_t i = 0; FOSSIL_SOAP_SUGGESTIONS[i].bad != null; i++) { + if (custom_strcasestr(text, FOSSIL_SOAP_SUGGESTIONS[i].bad)) { + return 1; + } + } + return 0; +} + +// ***************************************************************************** +// memory management +// ***************************************************************************** + +pizza_sys_memory_t pizza_sys_memory_alloc(size_t size) { + if (size == 0) { + fprintf(stderr, "Error: pizza_sys_memory_alloc() - Cannot allocate zero bytes.\n"); + return null; + } + + pizza_sys_memory_t ptr = malloc(size); + if (!ptr) { + fprintf(stderr, "Error: pizza_sys_memory_alloc() - Memory allocation failed.\n"); + return null; + } + return ptr; +} + +pizza_sys_memory_t pizza_sys_memory_realloc(pizza_sys_memory_t ptr, size_t size) { + pizza_sys_memory_t new_ptr = realloc(ptr, size); + if (!new_ptr && size > 0) { + fprintf(stderr, "Error: pizza_sys_memory_realloc() - Memory reallocation failed.\n"); + return null; + } + return new_ptr; +} + +pizza_sys_memory_t pizza_sys_memory_calloc(size_t num, size_t size) { + if (num == 0 || size == 0) { + fprintf(stderr, "Error: pizza_sys_memory_calloc() - Cannot allocate zero elements or zero bytes.\n"); + return null; + } + + pizza_sys_memory_t ptr = calloc(num, size); + if (!ptr) { + fprintf(stderr, "Error: pizza_sys_memory_calloc() - Memory allocation failed.\n"); + return null; + } + return ptr; +} + +pizza_sys_memory_t pizza_sys_memory_init(pizza_sys_memory_t ptr, size_t size, int32_t value) { + if (!ptr) { + fprintf(stderr, "Error: pizza_sys_memory_init() - Pointer is null.\n"); + return null; + } + + if (size == 0) { + fprintf(stderr, "Error: pizza_sys_memory_init() - Cannot initialize zero bytes.\n"); + return null; + } + + return memset(ptr, value, size); +} + +void pizza_sys_memory_free(pizza_sys_memory_t ptr) { + if (!ptr) { + fprintf(stderr, "Error: pizza_sys_memory_free() - Pointer is null.\n"); + return; + } + free(ptr); // No need for null check, free() already handles null. +} + +pizza_sys_memory_t pizza_sys_memory_copy(pizza_sys_memory_t dest, const pizza_sys_memory_t src, size_t size) { + if (!dest || !src) { + fprintf(stderr, "Error: pizza_sys_memory_copy() - Source or destination is null.\n"); + return null; + } + + if (size == 0) { + fprintf(stderr, "Error: pizza_sys_memory_copy() - Cannot copy zero bytes.\n"); + return null; + } + + return memcpy(dest, src, size); +} + +pizza_sys_memory_t pizza_sys_memory_set(pizza_sys_memory_t ptr, int32_t value, size_t size) { + if (!ptr) { + fprintf(stderr, "Error: pizza_sys_memory_set() - Pointer is null.\n"); + return null; + } + + if (size == 0) { + fprintf(stderr, "Error: pizza_sys_memory_set() - Cannot set zero bytes.\n"); + return null; + } + + return memset(ptr, value, size); +} + +pizza_sys_memory_t pizza_sys_memory_dup(const pizza_sys_memory_t src, size_t size) { + if (!src || size == 0) { + fprintf(stderr, "Error: pizza_sys_memory_dup() - Invalid source or zero size.\n"); + return null; + } + + pizza_sys_memory_t dest = pizza_sys_memory_alloc(size); + if (!dest) { + return null; // Error already handled in pizza_sys_memory_alloc + } + + return memcpy(dest, src, size); +} + +void pizza_sys_memory_zero(pizza_sys_memory_t ptr, size_t size) { + if (!ptr || size == 0) { + fprintf(stderr, "Error: pizza_sys_memory_zero() - Invalid pointer or zero size.\n"); + return; + } + + memset(ptr, 0, size); +} + +int pizza_sys_memory_compare(const pizza_sys_memory_t ptr1, const pizza_sys_memory_t ptr2, size_t size) { + if (!ptr1 || !ptr2 || size == 0) { + fprintf(stderr, "Error: pizza_sys_memory_compare() - Invalid pointers or zero size.\n"); + return -1; // Return -1 for invalid input + } + + return memcmp(ptr1, ptr2, size); +} + +pizza_sys_memory_t pizza_sys_memory_move(pizza_sys_memory_t dest, const pizza_sys_memory_t src, size_t size) { + if (!dest || !src || size == 0) { + fprintf(stderr, "Error: pizza_sys_memory_move() - Invalid source or destination pointers, or zero size.\n"); + return null; + } + + return memmove(dest, src, size); +} + +pizza_sys_memory_t pizza_sys_memory_resize(pizza_sys_memory_t ptr, size_t old_size, size_t new_size) { + if (ptr == null) { + fprintf(stderr, "Error: pizza_sys_memory_resize() - Pointer is null.\n"); + return null; + } + + if (new_size == 0) { + pizza_sys_memory_free(ptr); + return null; + } + + // Allocate new memory + pizza_sys_memory_t new_ptr = pizza_sys_memory_realloc(ptr, new_size); + if (!new_ptr) { + // Allocation failed; return the original memory block + fprintf(stderr, "Error: pizza_sys_memory_resize() - Memory resize failed, original memory preserved.\n"); + return ptr; + } + + // Check if new size is larger, and if so, preserve the old data + if (new_size > old_size && ptr) { + // Initialize new memory with old data (if necessary) + memcpy(new_ptr, ptr, old_size); + } + + return new_ptr; +} + +bool pizza_sys_memory_is_valid(const pizza_sys_memory_t ptr) { + if (!ptr) { + return false; + } + // Optional: Add more validation logic if needed, but normally you'd rely on the caller to manage validity. + return true; +} + +// ***************************************************************************** +// output management +// ***************************************************************************** + +int32_t PIZZA_IO_COLOR_ENABLE = 1; // Flag to enable/disable color output +int32_t FOSSIL_IO_ATTR_ENABLE = 1; // Flag to enable/disable attribute output + +// Define color codes for output +#define FOSSIL_IO_COLOR_RESET "\033[0m" +#define FOSSIL_IO_COLOR_RED "\033[31m" +#define FOSSIL_IO_COLOR_GREEN "\033[32m" +#define FOSSIL_IO_COLOR_YELLOW "\033[33m" +#define FOSSIL_IO_COLOR_BLUE "\033[34m" +#define FOSSIL_IO_COLOR_MAGENTA "\033[35m" +#define FOSSIL_IO_COLOR_CYAN "\033[36m" +#define FOSSIL_IO_COLOR_WHITE "\033[37m" + +// Bright colors +#define FOSSIL_IO_COLOR_BRIGHT_RED "\033[91m" +#define FOSSIL_IO_COLOR_BRIGHT_GREEN "\033[92m" +#define FOSSIL_IO_COLOR_BRIGHT_YELLOW "\033[93m" +#define FOSSIL_IO_COLOR_BRIGHT_BLUE "\033[94m" +#define FOSSIL_IO_COLOR_BRIGHT_MAGENTA "\033[95m" +#define FOSSIL_IO_COLOR_BRIGHT_CYAN "\033[96m" +#define FOSSIL_IO_COLOR_BRIGHT_WHITE "\033[97m" + +// Define text attributes +#define FOSSIL_IO_ATTR_BOLD "\033[1m" +#define FOSSIL_IO_ATTR_UNDERLINE "\033[4m" +#define FOSSIL_IO_ATTR_REVERSED "\033[7m" +#define FOSSIL_IO_ATTR_BLINK "\033[5m" +#define FOSSIL_IO_ATTR_HIDDEN "\033[8m" +#define FOSSIL_IO_ATTR_NORMAL "\033[22m" // For reverting to normal text + +// Additional attributes +#define FOSSIL_IO_ATTR_ITALIC "\033[3m" +#define FOSSIL_IO_ATTR_STRIKETHROUGH "\033[9m" + +#define FOSSIL_IO_BUFFER_SIZE 1000 + +// Function to apply color +void pizza_io_apply_color(const char *color) { + if (pizza_io_cstr_compare(color, "red") == 0) { + printf(FOSSIL_IO_COLOR_RED); + } else if (pizza_io_cstr_compare(color, "green") == 0) { + printf(FOSSIL_IO_COLOR_GREEN); + } else if (pizza_io_cstr_compare(color, "yellow") == 0) { + printf(FOSSIL_IO_COLOR_YELLOW); + } else if (pizza_io_cstr_compare(color, "blue") == 0) { + printf(FOSSIL_IO_COLOR_BLUE); + } else if (pizza_io_cstr_compare(color, "magenta") == 0) { + printf(FOSSIL_IO_COLOR_MAGENTA); + } else if (pizza_io_cstr_compare(color, "cyan") == 0) { + printf(FOSSIL_IO_COLOR_CYAN); + } else if (pizza_io_cstr_compare(color, "white") == 0) { + printf(FOSSIL_IO_COLOR_WHITE); + } + // Bright colors + else if (pizza_io_cstr_compare(color, "bright_red") == 0) { + printf(FOSSIL_IO_COLOR_BRIGHT_RED); + } else if (pizza_io_cstr_compare(color, "bright_green") == 0) { + printf(FOSSIL_IO_COLOR_BRIGHT_GREEN); + } else if (pizza_io_cstr_compare(color, "bright_yellow") == 0) { + printf(FOSSIL_IO_COLOR_BRIGHT_YELLOW); + } else if (pizza_io_cstr_compare(color, "bright_blue") == 0) { + printf(FOSSIL_IO_COLOR_BRIGHT_BLUE); + } else if (pizza_io_cstr_compare(color, "bright_magenta") == 0) { + printf(FOSSIL_IO_COLOR_BRIGHT_MAGENTA); + } else if (pizza_io_cstr_compare(color, "bright_cyan") == 0) { + printf(FOSSIL_IO_COLOR_BRIGHT_CYAN); + } else if (pizza_io_cstr_compare(color, "bright_white") == 0) { + printf(FOSSIL_IO_COLOR_BRIGHT_WHITE); + } else { + printf(FOSSIL_IO_COLOR_RESET); // Reset to default if color not recognized + } +} + +// Function to apply text attributes (e.g., bold, underline) +void pizza_io_apply_attribute(const char *attribute) { + if (pizza_io_cstr_compare(attribute, "bold") == 0) { + printf(FOSSIL_IO_ATTR_BOLD); + } else if (pizza_io_cstr_compare(attribute, "underline") == 0) { + printf(FOSSIL_IO_ATTR_UNDERLINE); + } else if (pizza_io_cstr_compare(attribute, "reversed") == 0) { + printf(FOSSIL_IO_ATTR_REVERSED); + } else if (pizza_io_cstr_compare(attribute, "blink") == 0) { + printf(FOSSIL_IO_ATTR_BLINK); + } else if (pizza_io_cstr_compare(attribute, "hidden") == 0) { + printf(FOSSIL_IO_ATTR_HIDDEN); + } else if (pizza_io_cstr_compare(attribute, "normal") == 0) { + printf(FOSSIL_IO_ATTR_NORMAL); + } else if (pizza_io_cstr_compare(attribute, "italic") == 0) { + printf(FOSSIL_IO_ATTR_ITALIC); + } else if (pizza_io_cstr_compare(attribute, "strikethrough") == 0) { + printf(FOSSIL_IO_ATTR_STRIKETHROUGH); + } else { + printf(FOSSIL_IO_ATTR_NORMAL); // Reset to normal if attribute not recognized + } +} + +// Function to handle named positions (like top, bottom, left, right) +void pizza_io_apply_position(const char *pos) { + if (pizza_io_cstr_compare(pos, "top") == 0) { + // Apply position logic for top + printf("\033[H"); // Move cursor to the top + } else if (pizza_io_cstr_compare(pos, "bottom") == 0) { + // Apply position logic for bottom + printf("\033[999;1H"); // Move cursor to the bottom (example within reasonable bounds) + } else if (pizza_io_cstr_compare(pos, "left") == 0) { + // Apply position logic for left + printf("\033[1;1H"); // Move cursor to the top-left corner + } else if (pizza_io_cstr_compare(pos, "right") == 0) { + // Apply position logic for right + printf("\033[1;999H"); // Move cursor to the top-right corner (example within reasonable bounds) + } + // Add more positions if needed +} + +// Function to print text with attributes, colors, positions, and format specifiers +void pizza_io_print_with_attributes(const char *format, ...) { + va_list args; + va_start(args, format); + + // Create a buffer to hold the formatted string + char buffer[FOSSIL_IO_BUFFER_SIZE]; + vsnprintf(buffer, sizeof(buffer), format, args); + + // Variable to keep track of the current position in the buffer + const char *current_pos = buffer; + const char *start = null; + const char *end = null; + + // Iterate over the buffer and process color/attribute/position inside `{}` and format specifiers + while ((start = strchr(current_pos, '{')) != null) { + // Print text before '{' + printf("%.*s", (int)(start - current_pos), current_pos); + + // Find the matching '}' + end = strchr(start, '}'); + if (end) { + // Extract attributes inside '{}' + size_t length = end - start - 1; + char attributes[length + 1]; + strncpy(attributes, start + 1, length); + attributes[length] = '\0'; + + // Split by comma to separate color, attribute, or position + char *color = null; + char *attribute = null; + char *pos = null; + char *comma_pos = strchr(attributes, ','); + if (comma_pos) { + *comma_pos = '\0'; // null-terminate the first part + color = attributes; // Color or position part + attribute = comma_pos + 1; // Attribute part + } else { + color = attributes; // Only one part (could be color, attribute, or position) + } + + // Handle positions (like {pos:name}) + if (strstr(color, "pos:") == color) { + pos = color + 4; // Skip the "pos:" prefix + pizza_io_apply_position(pos); + } else { + // Apply color and/or attribute based on flags + if (PIZZA_IO_COLOR_ENABLE && color) { + pizza_io_apply_color(color); + } + if (FOSSIL_IO_ATTR_ENABLE && attribute) { + pizza_io_apply_attribute(attribute); + } + } + + // Move past '}' and continue processing + current_pos = end + 1; + } + } + + // Print remaining text after last '}' + printf("%s", current_pos); + + va_end(args); +} + +// Function to print a sanitized formatted string to a specific file stream with attributes +void pizza_io_fprint_with_attributes(pizza_fstream_t *stream, const char *str) { + if (str != null && stream != null) { + char sanitized_str[FOSSIL_IO_BUFFER_SIZE]; + strncpy(sanitized_str, str, sizeof(sanitized_str)); + sanitized_str[sizeof(sanitized_str) - 1] = '\0'; // Ensure null termination + + // Apply color and attribute logic (same as the print version) + pizza_io_print_with_attributes(sanitized_str); + + // Write the sanitized string to the stream + fputs(sanitized_str, stream->file); + } else { + fputs("cnullptr\n", stderr); + } +} + +// +// OUTPUT FUNCTIONS +// + +// Function to print a sanitized string with attributes inside {} +void pizza_io_puts(const char *str) { + if (str != null) { + char sanitized_str[FOSSIL_IO_BUFFER_SIZE]; + strncpy(sanitized_str, str, sizeof(sanitized_str)); + sanitized_str[sizeof(sanitized_str) - 1] = '\0'; // Ensure null termination + + // Print the sanitized string with attributes + pizza_io_print_with_attributes(sanitized_str); + } else { + fputs("cnullptr\n", stderr); + } +} + +// Function to print a single character +void pizza_io_putchar(char c) { + putchar(c); +} + +// Function to print sanitized formatted output with attributes +void pizza_io_printf(const char *format, ...) { + va_list args; + va_start(args, format); + + // Create a buffer to hold the formatted string + char buffer[FOSSIL_IO_BUFFER_SIZE]; + vsnprintf(buffer, sizeof(buffer), format, args); + + // Print the sanitized output with attributes + pizza_io_print_with_attributes(buffer); + + va_end(args); +} + +// Function to print a sanitized string to a specific file stream +void pizza_io_fputs(pizza_fstream_t *stream, const char *str) { + if (str != null && stream != null) { + char sanitized_str[FOSSIL_IO_BUFFER_SIZE]; + strncpy(sanitized_str, str, sizeof(sanitized_str)); + sanitized_str[sizeof(sanitized_str) - 1] = '\0'; // Ensure null termination + + // Apply color/attributes and sanitize the string before printing + pizza_io_fprint_with_attributes(stream, sanitized_str); + } else { + fputs("cnullptr\n", stderr); + } +} + +// Function to print a sanitized formatted string to a specific file stream +void pizza_io_fprintf(pizza_fstream_t *stream, const char *format, ...) { + va_list args; + va_start(args, format); + + // Create a buffer to hold the formatted string + char buffer[FOSSIL_IO_BUFFER_SIZE]; + vsnprintf(buffer, sizeof(buffer), format, args); + + // Print the sanitized formatted string with attributes to the specified stream + pizza_io_fprint_with_attributes(stream, buffer); + + va_end(args); +} + +// TUI PART + +void pizza_io_clear_screen(void) { + pizza_io_printf("\033[2J\033[H"); +} + +void pizza_io_move_cursor(int row, int col) { + pizza_io_printf("\033[%d;%dH", row, col); +} + +void pizza_io_hide_cursor(void) { + pizza_io_printf("\033[?25l"); +} + +void pizza_io_show_cursor(void) { + pizza_io_printf("\033[?25h"); +} + +void pizza_io_draw_horizontal_line(int length, char ch) { + for (int i = 0; i < length; ++i) { + putchar(ch); + } + putchar('\n'); +} + +void pizza_io_draw_vertical_line(int length, char ch) { + for (int i = 0; i < length; ++i) { + putchar(ch); + putchar('\n'); + } +} + +void pizza_io_flush(void) { + fflush(stdout); +} + +// ***************************************************************************** +// string management +// ***************************************************************************** + +cstr pizza_io_cstr_create(const char *init) { + if (!init) return null; + size_t length = strlen(init); + cstr str = (cstr)malloc(length + 1); + if (str) { + strcpy(str, init); + } + return str; +} + +void pizza_io_cstr_free(cstr str) { + if (str) { + free(str); + } +} + +cstr pizza_io_cstr_copy(ccstr str) { + if (!str) return null; + return pizza_io_cstr_create(str); +} + +cstr pizza_io_cstr_dup(ccstr str) { + if (!str) return null; + size_t length = strlen(str); + cstr new_str = (cstr)malloc(length + 1); + if (new_str) { + strcpy(new_str, str); + } + return new_str; +} + +cstr pizza_io_cstr_concat(ccstr s1, ccstr s2) { + if (!s1 || !s2) return null; + size_t length1 = strlen(s1); + size_t length2 = strlen(s2); + cstr str = (cstr)malloc(length1 + length2 + 1); + if (str) { + strcpy(str, s1); + strcpy(str + length1, s2); + } + return str; +} + +size_t pizza_io_cstr_length(ccstr str) { + if (!str) return 0; + return strlen(str); +} + +int pizza_io_cstr_compare(ccstr s1, ccstr s2) { + if (!s1 || !s2) return -1; + return strcmp(s1, s2); +} + +void pizza_io_cstr_trim(cstr str) { + if (!str) return; + size_t length = strlen(str); + size_t start = 0; + size_t end = length - 1; + while (start < length && isspace(str[start])) { + start++; + } + while (end > start && isspace(str[end])) { + end--; + } + size_t count = end - start + 1; + if (start > 0) { + memmove(str, str + start, count); + } + str[count] = '\0'; +} + +cstr *pizza_io_cstr_split(ccstr str, char delimiter, size_t *count) { + if (!str || !count) return null; + size_t length = strlen(str); + size_t num_delimiters = 0; + for (size_t i = 0; i < length; i++) { + if (str[i] == delimiter) { + num_delimiters++; + } + } + *count = num_delimiters + 1; + cstr *result = (cstr*)malloc(*count * sizeof(cstr)); + if (result) { + size_t start = 0; + size_t index = 0; + for (size_t i = 0; i < length; i++) { + if (str[i] == delimiter) { + size_t count = i - start; + result[index] = (cstr)malloc(count + 1); + if (result[index]) { + memcpy(result[index], str + start, count); + result[index][count] = '\0'; + start = i + 1; + index++; + } else { + // Free previously allocated memory on error + for (size_t j = 0; j < index; j++) { + free(result[j]); + } + free(result); + return null; + } + } + } + size_t count = length - start; + result[index] = (cstr)malloc(count + 1); + if (result[index]) { + memcpy(result[index], str + start, count); + result[index][count] = '\0'; + } else { + // Free previously allocated memory on error + for (size_t j = 0; j < index; j++) { + free(result[j]); + } + free(result); + return null; + } + } + return result; +} + +cstr pizza_io_cstr_replace(ccstr str, ccstr old, ccstr new_str) { + if (!str || !old || !new_str) return null; + size_t old_length = strlen(old); + size_t new_length = strlen(new_str); + size_t count = 0; + size_t index = 0; + size_t length = strlen(str); + while (index < length) { + if (strstr(str + index, old) == str + index) { + count++; + index += old_length; + } else { + index++; + } + } + cstr result = (cstr)malloc(length + count * (new_length - old_length) + 1); + if (result) { + index = 0; + size_t start = 0; + while (index < length) { + if (strstr(str + index, old) == str + index) { + strcpy(result + start, new_str); + start += new_length; + index += old_length; + } else { + result[start] = str[index]; + start++; + index++; + } + } + result[start] = '\0'; + } + return result; +} + +cstr pizza_io_cstr_to_upper(cstr str) { + if (!str) return null; + size_t length = strlen(str); + cstr result = (cstr)malloc(length + 1); + if (result) { + for (size_t i = 0; i < length; i++) { + result[i] = toupper(str[i]); + } + result[length] = '\0'; + } + return result; +} + +cstr pizza_io_cstr_to_lower(cstr str) { + if (!str) return null; + size_t length = strlen(str); + cstr result = (cstr)malloc(length + 1); + if (result) { + for (size_t i = 0; i < length; i++) { + result[i] = tolower(str[i]); + } + result[length] = '\0'; + } + return result; +} + +int pizza_io_cstr_starts_with(ccstr str, ccstr prefix) { + if (!str || !prefix) return 0; + size_t str_length = strlen(str); + size_t prefix_length = strlen(prefix); + if (prefix_length > str_length) { + return 0; + } + return strncmp(str, prefix, prefix_length) == 0; +} + +int pizza_io_cstr_ends_with(ccstr str, ccstr suffix) { + if (!str || !suffix) return 0; + size_t str_length = strlen(str); + size_t suffix_length = strlen(suffix); + if (suffix_length > str_length) { + return 0; + } + return strncmp(str + str_length - suffix_length, suffix, suffix_length) == 0; +} + +cstr pizza_io_cstr_substring(ccstr str, size_t start, size_t length) { + if (!str) return null; + size_t str_length = strlen(str); + if (start >= str_length) { + return null; + } + cstr result = (cstr)malloc(length + 1); + if (result) { + size_t count = str_length - start; + if (length < count) { + count = length; + } + memcpy(result, str + start, count); + result[count] = '\0'; + } + return result; +} + +cstr pizza_io_cstr_reverse(cstr str) { + if (!str) return null; + size_t length = strlen(str); + cstr result = (cstr)malloc(length + 1); + if (result) { + for (size_t i = 0; i < length; i++) { + result[i] = str[length - i - 1]; + } + result[length] = '\0'; + } + return result; +} + +int pizza_io_cstr_contains(ccstr str, ccstr substr) { + if (!str || !substr) return 0; + return strstr(str, substr) != null; +} + +cstr pizza_io_cstr_repeat(ccstr str, size_t count) { + if (!str || count == 0) return null; + size_t length = strlen(str); + size_t new_length = length * count; + cstr result = (cstr)malloc(new_length + 1); + if (result) { + for (size_t i = 0; i < count; i++) { + memcpy(result + i * length, str, length); + } + result[new_length] = '\0'; + } + return result; +} + +cstr pizza_io_cstr_strip(ccstr str, char ch) { + if (!str) return null; + size_t length = strlen(str); + size_t start = 0; + size_t end = length - 1; + while (start < length && str[start] == ch) { + start++; + } + while (end > start && str[end] == ch) { + end--; + } + size_t count = end - start + 1; + cstr result = (cstr)malloc(count + 1); + if (result) { + memcpy(result, str + start, count); + result[count] = '\0'; + } + return result; +} + +size_t pizza_io_cstr_count(ccstr str, ccstr substr) { + if (!str || !substr) return 0; + size_t count = 0; + size_t length = strlen(substr); + while ((str = strstr(str, substr)) != null) { + count++; + str += length; + } + return count; +} + +cstr pizza_io_cstr_pad_left(ccstr str, size_t total_length, char pad_char) { + if (!str || total_length == 0) return null; + size_t length = strlen(str); + if (length >= total_length) { + return pizza_io_cstr_copy(str); + } + size_t pad_length = total_length - length; + cstr result = (cstr)malloc(total_length + 1); + if (result) { + memset(result, pad_char, pad_length); + strcpy(result + pad_length, str); + result[total_length] = '\0'; + } + return result; +} + +cstr pizza_io_cstr_pad_right(ccstr str, size_t total_length, char pad_char) { + if (!str || total_length == 0) return null; + size_t length = strlen(str); + if (length >= total_length) { + return pizza_io_cstr_copy(str); + } + size_t pad_length = total_length - length; + cstr result = (cstr)malloc(total_length + 1); + if (result) { + strcpy(result, str); + memset(result + length, pad_char, pad_length); + result[total_length] = '\0'; + } + return result; +} diff --git a/code/logic/fossil/test/assume.h b/code/logic/fossil/pizza/assume.h similarity index 84% rename from code/logic/fossil/test/assume.h rename to code/logic/fossil/pizza/assume.h index 12a38b33..a6064f7f 100644 --- a/code/logic/fossil/test/assume.h +++ b/code/logic/fossil/pizza/assume.h @@ -7,9 +7,9 @@ * herein is subject to the terms and conditions defined in the project license. * * Author: Michael Gene Brockus (Dreamer) - * Date: 07/01/2024 + * Date: 04/05/2014 * - * Copyright (C) 2024 Fossil Logic. All rights reserved. + * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. * ----------------------------------------------------------------------------- */ #ifndef FOSSIL_TEST_ASSUME_TYPE_H @@ -22,25 +22,19 @@ #include #include #include -#include #include #ifdef __cplusplus extern "C" { #endif -// becuase Microsoft had to be diffrent -#ifdef _WIN32 -#define wcsncasecmp _wcsnicmp -#endif - // Used in floating-point asserts #define FOSSIL_TEST_FLOAT_EPSILON 1e-6 #define FOSSIL_TEST_DOUBLE_EPSILON 1e-9 // ************************************************** // -// Boolean ASSUMEions +// Boolean assumtions // // ************************************************** @@ -78,7 +72,7 @@ extern "C" { // ************************************************** // -// Floating point ASSUMEions +// Floating point assumtions // // ************************************************** @@ -304,7 +298,7 @@ extern "C" { // ************************************************** // -// Numaric ASSUMEions +// Numaric assumtions // // ************************************************** @@ -668,7 +662,7 @@ extern "C" { #define ASSUME_NOT_MORE_OR_EQUAL_O64(actual, expected) \ FOSSIL_TEST_ASSUME((uint64_t)(actual) < (uint64_t)(expected), "Expected " #actual " to not be more than or equal to " #expected) -// Hexadecimal ASSUMEions +// Hexadecimal assumtions /** * @brief Assumes that the given 8-bit hexadecimal values are equal. @@ -1752,25 +1746,229 @@ extern "C" { // ************************************************** // -// Null pointer ASSUMEions (_CNULL) +// Memory allocation assumptions +// +// ************************************************** + +/** + * @brief Assumes that the given memory is zeroed. + * + * @param ptr A pointer to the memory to check. + * @param size The size of the memory to check. + */ +#define ASSUME_ITS_ZERO_MEMORY(ptr, size) \ + FOSSIL_TEST_ASSUME(pizza_sys_memory_zero((ptr), (size)), "Expected memory at " #ptr " to be zeroed") + +/** + * @brief Assumes that the given memory is not zeroed. + * + * @param ptr A pointer to the memory to check. + * @param size The size of the memory to check. + */ +#define ASSUME_NOT_ZERO_MEMORY(ptr, size) \ + FOSSIL_TEST_ASSUME(!pizza_sys_memory_zero((ptr), (size)), "Expected memory at " #ptr " to not be zero") + +/** + * @brief Assumes that the given memory regions are equal. + * + * @param ptr1 A pointer to the first memory region. + * @param ptr2 A pointer to the second memory region. + * @param size The size of the memory regions to compare. + */ +#define ASSUME_ITS_EQUAL_MEMORY(ptr1, ptr2, size) \ + FOSSIL_TEST_ASSUME(pizza_sys_memory_compare((ptr1), (ptr2), (size)) == 0, "Expected memory regions " #ptr1 " and " #ptr2 " to be equal") + +/** + * @brief Assumes that the given memory regions are not equal. + * + * @param ptr1 A pointer to the first memory region. + * @param ptr2 A pointer to the second memory region. + * @param size The size of the memory regions to compare. + */ +#define ASSUME_ITS_NOT_EQUAL_MEMORY(ptr1, ptr2, size) \ + FOSSIL_TEST_ASSUME(pizza_sys_memory_compare((ptr1), (ptr2), (size)) != 0, "Expected memory regions " #ptr1 " and " #ptr2 " to not be equal") + +/** + * @brief Assumes that the given memory region is more than the expected memory region. + * + * @param ptr1 A pointer to the first memory region. + * @param ptr2 A pointer to the second memory region. + * @param size The size of the memory regions to compare. + */ +#define ASSUME_ITS_MORE_THAN_MEMORY(ptr1, ptr2, size) \ + FOSSIL_TEST_ASSUME(pizza_sys_memory_compare((ptr1), (ptr2), (size)) > 0, "Expected memory region " #ptr1 " to be more than " #ptr2) + +/** + * @brief Assumes that the given memory region is less than the expected memory region. + * + * @param ptr1 A pointer to the first memory region. + * @param ptr2 A pointer to the second memory region. + * @param size The size of the memory regions to compare. + */ +#define ASSUME_ITS_LESS_THAN_MEMORY(ptr1, ptr2, size) \ + FOSSIL_TEST_ASSUME(pizza_sys_memory_compare((ptr1), (ptr2), (size)) < 0, "Expected memory region " #ptr1 " to be less than " #ptr2) + +/** + * @brief Assumes that the given memory region is more than or equal to the expected memory region. + * + * @param ptr1 A pointer to the first memory region. + * @param ptr2 A pointer to the second memory region. + * @param size The size of the memory regions to compare. + */ +#define ASSUME_ITS_MORE_OR_EQUAL_MEMORY(ptr1, ptr2, size) \ + FOSSIL_TEST_ASSUME(pizza_sys_memory_compare((ptr1), (ptr2), (size)) >= 0, "Expected memory region " #ptr1 " to be more than or equal to " #ptr2) + +/** + * @brief Assumes that the given memory region is less than or equal to the expected memory region. + * + * @param ptr1 A pointer to the first memory region. + * @param ptr2 A pointer to the second memory region. + * @param size The size of the memory regions to compare. + */ +#define ASSUME_ITS_LESS_OR_EQUAL_MEMORY(ptr1, ptr2, size) \ + FOSSIL_TEST_ASSUME(pizza_sys_memory_compare((ptr1), (ptr2), (size)) <= 0, "Expected memory region " #ptr1 " to be less than or equal to " #ptr2) + +/** + * @brief Assumes that the given memory region is not more than the expected memory region. + * + * @param ptr1 A pointer to the first memory region. + * @param ptr2 A pointer to the second memory region. + * @param size The size of the memory regions to compare. + */ +#define ASSUME_NOT_MORE_THAN_MEMORY(ptr1, ptr2, size) \ + FOSSIL_TEST_ASSUME(pizza_sys_memory_compare((ptr1), (ptr2), (size)) <= 0, "Expected memory region " #ptr1 " to not be more than " #ptr2) + +/** + * @brief Assumes that the given memory region is not less than the expected memory region. + * + * @param ptr1 A pointer to the first memory region. + * @param ptr2 A pointer to the second memory region. + * @param size The size of the memory regions to compare. + */ +#define ASSUME_NOT_LESS_THAN_MEMORY(ptr1, ptr2, size) \ + FOSSIL_TEST_ASSUME(pizza_sys_memory_compare((ptr1), (ptr2), (size)) >= 0, "Expected memory region " #ptr1 " to not be less than " #ptr2) + +/** + * @brief Assumes that the given memory region is not more than or equal to the expected memory region. + * + * @param ptr1 A pointer to the first memory region. + * @param ptr2 A pointer to the second memory region. + * @param size The size of the memory regions to compare. + */ +#define ASSUME_NOT_MORE_OR_EQUAL_MEMORY(ptr1, ptr2, size) \ + FOSSIL_TEST_ASSUME(pizza_sys_memory_compare((ptr1), (ptr2), (size)) < 0, "Expected memory region " #ptr1 " to not be more than or equal to " #ptr2) + +/** + * @brief Assumes that the given memory region is not less than or equal to the expected memory region. + * + * @param ptr1 A pointer to the first memory region. + * @param ptr2 A pointer to the second memory region. + * @param size The size of the memory regions to compare. + */ +#define ASSUME_NOT_LESS_OR_EQUAL_MEMORY(ptr1, ptr2, size) \ + FOSSIL_TEST_ASSUME(pizza_sys_memory_compare((ptr1), (ptr2), (size)) > 0, "Expected memory region " #ptr1 " to not be less than or equal to " #ptr2) + +/** + * @brief Assumes that the given memory pointer is valid. + * + * @param ptr A pointer to the memory to check. + */ +#define ASSUME_ITS_VALID_MEMORY(ptr) \ + FOSSIL_TEST_ASSUME(pizza_sys_memory_is_valid((ptr)), "Expected memory pointer " #ptr " to be valid") + +/** + * @brief Assumes that the given memory pointer is not valid. + * + * @param ptr A pointer to the memory to check. + */ +#define ASSUME_NOT_VALID_MEMORY(ptr) \ + FOSSIL_TEST_ASSUME(!pizza_sys_memory_is_valid((ptr)), "Expected memory pointer " #ptr " to not be valid") + +// ************************************************** +// +// Null pointer assumtions (_CNULL) // // ************************************************** /** - * @brief Assumes that the given pointer is NULL. + * @brief Assumes that the given pointer is cnull. * * @param actual The pointer to be evaluated. */ #define ASSUME_ITS_CNULL(actual) \ - FOSSIL_TEST_ASSUME((actual) == NULL, "Expected " #actual " to be NULL") + FOSSIL_TEST_ASSUME((actual) == null, "Expected " #actual " to be cnull") /** - * @brief Assumes that the given pointer is not NULL. + * @brief Assumes that the given pointer is not cnull. * * @param actual The pointer to be evaluated. */ #define ASSUME_NOT_CNULL(actual) \ - FOSSIL_TEST_ASSUME((actual) != NULL, "Expected " #actual " to not be NULL") + FOSSIL_TEST_ASSUME((actual) != null, "Expected " #actual " to not be cnull") + +/** + * @brief Assumes that the given pointer is cnull. + * + * @param actual The pointer to be evaluated. + */ +#define ASSUME_ITS_CNULLABLE(actual) \ + FOSSIL_TEST_ASSUME((actual) == null, "Expected " #actual " to be cnull") + +/** + * @brief Assumes that the given pointer is not cnull. + * + * @param actual The pointer to be evaluated. + */ +#define ASSUME_NOT_CNULLABLE(actual) \ + FOSSIL_TEST_ASSUME((actual) != null, "Expected " #actual " to not be cnull") + +/** + * @brief Assumes that the given pointer is cnull. + * + * @param actual The pointer to be evaluated. + */ +#define ASSUME_ITS_CNONNULL(actual) \ + FOSSIL_TEST_ASSUME((actual) != null, "Expected " #actual " to not be cnull") + +/** + * @brief Assumes that the given pointer is not cnull. + * + * @param actual The pointer to be evaluated. + */ +#define ASSUME_NOT_CNONNULL(actual) \ + FOSSIL_TEST_ASSUME((actual) == null, "Expected " #actual " to be cnull") + +/** + * @brief Assumes that the given condition is likely. + * + * @param x The condition to be evaluated. + */ +#define ASSUME_ITS_LIKELY(x) \ + FOSSIL_TEST_ASSUME(likely(x), "Expected " #x " to be likely") + +/** + * @brief Assumes that the given condition is not likely. + * + * @param x The condition to be evaluated. + */ +#define ASSUME_NOT_LIKELY(x) \ + FOSSIL_TEST_ASSUME(!likely(x), "Expected " #x " to not be likely") + +/** + * @brief Assumes that the given condition is unlikely. + * + * @param x The condition to be evaluated. + */ +#define ASSUME_ITS_UNLIKELY(x) \ + FOSSIL_TEST_ASSUME(unlikely(x), "Expected " #x " to be unlikely") + +/** + * @brief Assumes that the given condition is not unlikely. + * + * @param x The condition to be evaluated. + */ +#define ASSUME_NOT_UNLIKELY(x) \ + FOSSIL_TEST_ASSUME(!unlikely(x), "Expected " #x " to not be unlikely") /** * @brief Assumes that the given pointers are equal. @@ -1846,7 +2044,7 @@ extern "C" { // ************************************************** // -// Range ASSUMEions +// Range assumtions // // ************************************************** @@ -2072,7 +2270,7 @@ extern "C" { #define ASSUME_NOT_WITHIN_RANGE_F64(value, min, max) \ FOSSIL_TEST_ASSUME((value) < (min) || (value) > (max), "Value " #value " is within range [" #min ", " #max "]") -// Byte char type ASSUMEions (uint8_t) +// Byte char type assumtions (uint8_t) /** * @brief Assumes that the given byte char value is within the specified range. @@ -2094,7 +2292,7 @@ extern "C" { #define ASSUME_NOT_WITHIN_RANGE_BCHAR(value, min, max) \ FOSSIL_TEST_ASSUME((uint8_t)(value) < (uint8_t)(min) || (uint8_t)(value) > (uint8_t)(max), "Value " #value " is within range [" #min ", " #max "]") -// Char type ASSUMEions (char) +// Char type assumtions (char) /** * @brief Assumes that the given char value is within the specified range. @@ -2116,87 +2314,255 @@ extern "C" { #define ASSUME_NOT_WITHIN_RANGE_CCHAR(value, min, max) \ FOSSIL_TEST_ASSUME((char)(value) < (char)(min) || (char)(value) > (char)(max), "Value " #value " is within range [" #min ", " #max "]") -// Wide char type ASSUMEions (wchar_t) +// ************************************************** +// +// String assumtions +// +// ************************************************** /** - * @brief Assumes that the given wide char value is within the specified range. + * @brief Assumes that the given C strings are equal. * - * @param value The wide char value to be evaluated. - * @param min The minimum value of the range. - * @param max The maximum value of the range. + * @param actual The actual C string. + * @param expected The expected C string. */ -#define ASSUME_ITS_WITHIN_RANGE_WCHAR(value, min, max) \ - FOSSIL_TEST_ASSUME((wchar_t)(value) >= (wchar_t)(min) && (wchar_t)(value) <= (wchar_t)(max), "Value " #value " is not within range [" #min ", " #max "]") +#define ASSUME_ITS_EQUAL_CSTR(actual, expected) \ + FOSSIL_TEST_ASSUME(strcmp((actual), (expected)) == 0, "Expected C string " #actual " to be equal to " #expected) /** - * @brief Assumes that the given wide char value is not within the specified range. + * @brief Assumes that the given C strings are not equal. * - * @param value The wide char value to be evaluated. - * @param min The minimum value of the range. - * @param max The maximum value of the range. + * @param actual The actual C string. + * @param expected The expected C string. */ -#define ASSUME_NOT_WITHIN_RANGE_WCHAR(value, min, max) \ - FOSSIL_TEST_ASSUME((wchar_t)(value) < (wchar_t)(min) || (wchar_t)(value) > (wchar_t)(max), "Value " #value " is within range [" #min ", " #max "]") +#define ASSUME_NOT_EQUAL_CSTR(actual, expected) \ + FOSSIL_TEST_ASSUME(strcmp((actual), (expected)) != 0, "Expected C string " #actual " to not be equal to " #expected) + +/** + * @brief Assumes that the length of the given C string is equal to the expected length. + * + * @param actual The actual C string. + * @param expected_len The expected length of the C string. + */ +#define ASSUME_ITS_LENGTH_EQUAL_CSTR(actual, expected_len) \ + FOSSIL_TEST_ASSUME(strlen((actual)) == (expected_len), "Expected length of C string " #actual " to be equal to " #expected_len) + +/** + * @brief Assumes that the length of the given C string is not equal to the expected length. + * + * @param actual The actual C string. + * @param expected_len The expected length of the C string. + */ +#define ASSUME_NOT_LENGTH_EQUAL_CSTR(actual, expected_len) \ + FOSSIL_TEST_ASSUME(strlen((actual)) != (expected_len), "Expected length of C string " #actual " to not be equal to " #expected_len) + +/** + * @brief Assumes that the given cstr starts with the specified prefix. + * + * @param str The cstr to be checked. + * @param prefix The prefix to check for. + */ +#define ASSUME_ITS_CSTR_STARTS_WITH(str, prefix) \ + FOSSIL_TEST_ASSUME(pizza_io_cstr_starts_with((str), (prefix)), "Expected cstr " #str " to start with prefix " #prefix) + +/** + * @brief Assumes that the given cstr does not start with the specified prefix. + * + * @param str The cstr to be checked. + * @param prefix The prefix to check for. + */ +#define ASSUME_NOT_CSTR_STARTS_WITH(str, prefix) \ + FOSSIL_TEST_ASSUME(!pizza_io_cstr_starts_with((str), (prefix)), "Expected cstr " #str " to not start with prefix " #prefix) + +/** + * @brief Assumes that the given cstr ends with the specified suffix. + * + * @param str The cstr to be checked. + * @param suffix The suffix to check for. + */ +#define ASSUME_ITS_CSTR_ENDS_WITH(str, suffix) \ + FOSSIL_TEST_ASSUME(pizza_io_cstr_ends_with((str), (suffix)), "Expected cstr " #str " to end with suffix " #suffix) + +/** + * @brief Assumes that the given cstr does not end with the specified suffix. + * + * @param str The cstr to be checked. + * @param suffix The suffix to check for. + */ +#define ASSUME_NOT_CSTR_ENDS_WITH(str, suffix) \ + FOSSIL_TEST_ASSUME(!pizza_io_cstr_ends_with((str), (suffix)), "Expected cstr " #str " to not end with suffix " #suffix) + +/** + * @brief Assumes that the given cstr contains the specified substring. + * + * @param str The cstr to be checked. + * @param substr The substring to check for. + */ +#define ASSUME_ITS_CSTR_CONTAINS(str, substr) \ + FOSSIL_TEST_ASSUME(pizza_io_cstr_contains((str), (substr)), "Expected cstr " #str " to contain substring " #substr) + +/** + * @brief Assumes that the given cstr does not contain the specified substring. + * + * @param str The cstr to be checked. + * @param substr The substring to check for. + */ +#define ASSUME_NOT_CSTR_CONTAINS(str, substr) \ + FOSSIL_TEST_ASSUME(!pizza_io_cstr_contains((str), (substr)), "Expected cstr " #str " to not contain substring " #substr) + +/** + * @brief Assumes that the given cstr contains the specified number of occurrences of a substring. + * + * @param str The cstr to be searched. + * @param substr The substring to search for. + * @param count The expected number of occurrences. + */ +#define ASSUME_ITS_CSTR_COUNT(str, substr, count) \ + FOSSIL_TEST_ASSUME(pizza_io_cstr_count((str), (substr)) == (count), "Expected cstr " #str " to contain " #count " occurrences of substring " #substr) + +/** + * @brief Assumes that the given cstr does not contain the specified number of occurrences of a substring. + * + * @param str The cstr to be searched. + * @param substr The substring to search for. + * @param count The expected number of occurrences. + */ +#define ASSUME_NOT_CSTR_COUNT(str, substr, count) \ + FOSSIL_TEST_ASSUME(pizza_io_cstr_count((str), (substr)) != (count), "Expected cstr " #str " to not contain " #count " occurrences of substring " #substr) // ************************************************** // -// String ASSUMEions +// Char assumtions // // ************************************************** /** - * @brief Assumes that the given wide char strings are equal. + * @brief Assumes that the given char values are equal. * - * @param actual The actual wide char string. - * @param expected The expected wide char string. + * @param actual The actual char value. + * @param expected The expected char value. */ -#define ASSUME_ITS_EQUAL_WSTR(actual, expected) \ - FOSSIL_TEST_ASSUME(wcscmp((actual), (expected)) == 0, "Expected wide string " #actual " to be equal to " #expected) +#define ASSUME_ITS_EQUAL_CHAR(actual, expected) \ + FOSSIL_TEST_ASSUME((char)(actual) == (char)(expected), "Expected char " #actual " to be equal to " #expected) /** - * @brief Assumes that the given wide char strings are not equal. + * @brief Assumes that the given char values are not equal. * - * @param actual The actual wide char string. - * @param expected The expected wide char string. + * @param actual The actual char value. + * @param expected The expected char value. */ -#define ASSUME_NOT_EQUAL_WSTR(actual, expected) \ - FOSSIL_TEST_ASSUME(wcscmp((actual), (expected)) != 0, "Expected wide string " #actual " to not be equal to " #expected) +#define ASSUME_NOT_EQUAL_CHAR(actual, expected) \ + FOSSIL_TEST_ASSUME((char)(actual) != (char)(expected), "Expected char " #actual " to not be equal to " #expected) /** - * @brief Assumes that the length of the given wide char string is equal to the expected length. + * @brief Assumes that the given char value is less than the expected value. * - * @param actual The actual wide char string. - * @param expected_len The expected length of the wide char string. + * @param actual The actual char value. + * @param expected The expected char value. */ -#define ASSUME_ITS_LENGTH_EQUAL_WSTR(actual, expected_len) \ - FOSSIL_TEST_ASSUME(wcslen((actual)) == (expected_len), "Expected length of wide string " #actual " to be equal to " #expected_len) +#define ASSUME_ITS_LESS_THAN_CHAR(actual, expected) \ + FOSSIL_TEST_ASSUME((char)(actual) < (char)(expected), "Expected char " #actual " to be less than " #expected) /** - * @brief Assumes that the given C strings are equal. + * @brief Assumes that the given char value is more than the expected value. * - * @param actual The actual C string. - * @param expected The expected C string. + * @param actual The actual char value. + * @param expected The expected char value. */ -#define ASSUME_ITS_EQUAL_CSTR(actual, expected) \ - FOSSIL_TEST_ASSUME(strcmp((actual), (expected)) == 0, "Expected C string " #actual " to be equal to " #expected) +#define ASSUME_ITS_MORE_THAN_CHAR(actual, expected) \ + FOSSIL_TEST_ASSUME((char)(actual) > (char)(expected), "Expected char " #actual " to be more than " #expected) /** - * @brief Assumes that the given C strings are not equal. + * @brief Assumes that the given char value is less than or equal to the expected value. * - * @param actual The actual C string. - * @param expected The expected C string. + * @param actual The actual char value. + * @param expected The expected char value. */ -#define ASSUME_NOT_EQUAL_CSTR(actual, expected) \ - FOSSIL_TEST_ASSUME(strcmp((actual), (expected)) != 0, "Expected C string " #actual " to not be equal to " #expected) +#define ASSUME_ITS_LESS_OR_EQUAL_CHAR(actual, expected) \ + FOSSIL_TEST_ASSUME((char)(actual) <= (char)(expected), "Expected char " #actual " to be less than or equal to " #expected) /** - * @brief Assumes that the length of the given C string is equal to the expected length. + * @brief Assumes that the given char value is more than or equal to the expected value. * - * @param actual The actual C string. - * @param expected_len The expected length of the C string. + * @param actual The actual char value. + * @param expected The expected char value. */ -#define ASSUME_ITS_LENGTH_EQUAL_CSTR(actual, expected_len) \ - FOSSIL_TEST_ASSUME(strlen((actual)) == (expected_len), "Expected length of C string " #actual " to be equal to " #expected_len) +#define ASSUME_ITS_MORE_OR_EQUAL_CHAR(actual, expected) \ + FOSSIL_TEST_ASSUME((char)(actual) >= (char)(expected), "Expected char " #actual " to be more than or equal to " #expected) + +/** + * @brief Assumes that the given char value is not less than the expected value. + * + * @param actual The actual char value. + * @param expected The expected char value. + */ +#define ASSUME_NOT_LESS_THAN_CHAR(actual, expected) \ + FOSSIL_TEST_ASSUME((char)(actual) >= (char)(expected), "Expected char " #actual " to not be less than " #expected) + +/** + * @brief Assumes that the given char value is not more than the expected value. + * + * @param actual The actual char value. + * @param expected The expected char value. + */ +#define ASSUME_NOT_MORE_THAN_CHAR(actual, expected) \ + FOSSIL_TEST_ASSUME((char)(actual) <= (char)(expected), "Expected char " #actual " to not be more than " #expected) + +/** + * @brief Assumes that the given char value is not less than or equal to the expected value. + * + * @param actual The actual char value. + * @param expected The expected char value. + */ +#define ASSUME_NOT_LESS_OR_EQUAL_CHAR(actual, expected) \ + FOSSIL_TEST_ASSUME((char)(actual) > (char)(expected), "Expected char " #actual " to not be less than or equal to " #expected) + +/** + * @brief Assumes that the given char value is not more than or equal to the expected value. + * + * @param actual The actual char value. + * @param expected The expected char value. + */ +#define ASSUME_NOT_MORE_OR_EQUAL_CHAR(actual, expected) \ + FOSSIL_TEST_ASSUME((char)(actual) < (char)(expected), "Expected char " #actual " to not be more than or equal to " #expected) + +// ************************************************** +// SOAP assumptions +// ************************************************ + +/** + * @brief Assumes that the given text does not contain "rot-brain" language. + * + * @param text The input text to check. + */ +#define ASSUME_NOT_SOAP_ROT_BRAIN(text) \ + FOSSIL_TEST_ASSUME(!pizza_io_is_rot_brain((text)), "Expected text " #text " to not contain 'rot-brain' language") + +/** + * @brief Assumes that the given text contains "rot-brain" language. + * + * @param text The input text to check. + */ +#define ASSUME_ITS_SOAP_ROT_BRAIN(text) \ + FOSSIL_TEST_ASSUME(pizza_io_is_rot_brain((text)), "Expected text " #text " to contain 'rot-brain' language") + +/** + * @brief Assumes that the tone of the given sentence is detected correctly. + * + * @param text The input text. + * @param expected_tone The expected tone ("formal", "casual", "sarcastic", etc.). + */ +#define ASSUME_ITS_SOAP_TONE_DETECTED(text, expected_tone) \ + FOSSIL_TEST_ASSUME(strcmp(pizza_io_soap_detect_tone((text)), (expected_tone)) == 0, "Expected tone of text " #text " to be " #expected_tone) + +/** + * @brief Assumes that the tone of the given sentence is not detected correctly. + * + * @param text The input text. + * @param expected_tone The expected tone ("formal", "casual", "sarcastic", etc.). + */ +#define ASSUME_NOT_SOAP_TONE_DETECTED(text, expected_tone) \ + FOSSIL_TEST_ASSUME(strcmp(pizza_io_soap_detect_tone((text)), (expected_tone)) != 0, "Expected tone of text " #text " to not be " #expected_tone) #ifdef __cplusplus } diff --git a/code/logic/fossil/pizza/common.h b/code/logic/fossil/pizza/common.h new file mode 100644 index 00000000..ee9acb71 --- /dev/null +++ b/code/logic/fossil/pizza/common.h @@ -0,0 +1,1005 @@ +/* + * ----------------------------------------------------------------------------- + * Project: Fossil Logic + * + * This file is part of the Fossil Logic project, which aims to develop high- + * performance, cross-platform applications and libraries. The code contained + * herein is subject to the terms and conditions defined in the project license. + * + * Author: Michael Gene Brockus (Dreamer) + * Date: 04/05/2014 + * + * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. + * ----------------------------------------------------------------------------- + */ +#ifndef FOSSIL_TEST_COMMON_H +#define FOSSIL_TEST_COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 + #include + #include +#elif defined(__APPLE__) + #include + #include + #include + #include + #include + #include +#else + #include + #include + #include + #include + #include + #include +#endif + +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 199309L +#endif + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// ***************************************************************************** +// Safe operations +// ***************************************************************************** + +// Ensure null pointer definitions across C and C++ environments +#ifndef FOSSIL_CNULL + +/** + * @brief Safe and consistent null pointer definition for modern C++ and C standards. + * + * This section defines `null` for both C and C++ environments. + * The definitions ensure compatibility across different language versions, providing + * a clear and consistent way to represent null pointers. + * + * - **C23 and Later:** In C23 (`__STDC_VERSION__ >= 202311L`), `null` is introduced + * as a type-safe null pointer constant. The `null` macro directly maps to this + * standard definition. + * + * - **Older C Standards (C11 and Below):** If C23 is not detected, `null` is defined + * using `((void*)0)`, which is the traditional representation of a null pointer. + * and portable representation of a null pointer in C. + * + * This abstraction guarantees that null pointer values are handled consistently + * across different compilers and platforms, reducing the risk of undefined behavior + * in pointer operations. + */ +#if __cplusplus >= 201103L || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) + #define null nullptr +#else + #define null ((void*)0) +#endif + +#endif // FOSSIL_CNULL + +/** + * @brief Nullify a pointer safely. + * + * Ensures that the pointer is explicitly set to `null`. + */ +#define nullify(ptr) ((ptr) = null) + +/** + * @brief Check if a pointer is not null safely. + * + * Prevents misuse of potentially null pointers. + */ +#define notnull(ptr) ((ptr) != null) + +/** + * @brief Option-like behavior to return a pointer or a default value. + * + * Mimics Rust's `Option::unwrap_or()` safely. + */ +#define unwrap_or(ptr, default_val) ((ptr) ? (ptr) : (default_val)) + +/** + * @brief Unwraps a pointer safely or terminates if it's null. + * + * Mimics Rust's `Option::unwrap()`. + */ +#define unwrap(ptr) ((notnull(ptr)) ? (ptr) : (fprintf(stderr, "Fatal error: called unwrap() on a null pointer at %s:%d\n", __FILE__, __LINE__), exit(EXIT_FAILURE), null)) + +/** + * @brief Safely casts one pointer type to another with null-checking. + * + * Mimics Rust's `as` with additional null safety. If the input is `null`, + * it returns `null` instead of attempting an invalid cast. + * + * @param type The target type for the cast. + * @param ptr The pointer to cast. + * @return The casted pointer or `null` if the input pointer is null. + */ +#ifdef __cplusplus + #define safe_cast(type, ptr) ((notnull(ptr)) ? (static_cast(ptr)) : null) +#else + #define safe_cast(type, ptr) ((notnull(ptr)) ? ((type)(ptr)) : null) +#endif + +/** + * @brief Marks a variable as intentionally unused to prevent warnings. + */ +#ifndef unused + #if defined(__GNUC__) || defined(__clang__) + #define unused(x) (void)(x) + #else + #define unused(x) /* no-op */ + #endif +#endif + +/** + * @brief Compiler hints for nullable and nonnull values. + * + * Provides stronger safety checks at compile time. + */ +#if defined(__clang__) || defined(__GNUC__) + #define nullable __attribute__((nullable)) + #define nonnull __attribute__((nonnull)) +#elif defined(_MSC_VER) + #define nullable _Null_terminated_ + #define nonnull _In_ +#else + #define nullable + #define nonnull +#endif + +/** + * @brief Compiler branch prediction hints for likely and unlikely conditions. + * + * Helps the compiler optimize branches based on expected conditions. + */ +#if defined(__GNUC__) || defined(__clang__) + #define likely(x) __builtin_expect(!!(x), 1) + #define unlikely(x) __builtin_expect(!!(x), 0) +#else + #define likely(x) (x) + #define unlikely(x) (x) +#endif + +// Safe string and character constants + +/** + * @brief Null terminators for C and wide strings. + */ +#define term '\0' + +/** + * @brief Newline constants for C and wide strings. + */ +#define newline '\n' + +/** + * @brief Empty string constants for C and wide strings. + */ +#define cempty "" + +/** + * @brief Ensure safe cleanup by nullifying pointers after use. + * + * Mimics Rust's memory safety using explicit pointer management. + */ +#define drop(ptr) do { nullify(ptr); } while (0) + +/** + * @brief Panic behavior for immediate program termination with error message. + * + * This macro causes the program to immediately terminate with an error message, + * similar to Rust's `panic!()` functionality. + * + * @param msg The message to display when panicking. + */ +#define panic(msg) (pizza_io_fprintf(PIZZA_STDERR, "Panic: %s\n", msg), exit(EXIT_FAILURE)) + +/** + * @brief Mimics Rust's Option type. + * + * The `coptional` macro represents a nullable pointer that can be either `null` or a valid pointer. + * It can be used to model optional values that may or may not be present. + */ +#define coptional(ptr) ((ptr) ? (ptr) : null) + +/** + * @brief `Option` structure to mimic Rust's `Option`. + * + * This structure allows representation of an optional value where it can either contain a value + * (`Some`) or be `None` (`null`). + */ +typedef struct { + void* value; // The value held by the Option (could be a pointer to any type) + int is_some; // Flag indicating whether the Option is `Some` (1) or `None` (0) +} Option; + +/** + * @brief Creates an `Option` with a value (Some). + * + * @param val The value to wrap in the Option. + * @return The created `Option` containing the value. + */ +#ifdef __cplusplus + #define some(val) (Option{val, 1}) +#else + #define some(val) ((Option){(void*)(val), 1}) +#endif + +/** + * @brief Creates an empty `Option` (None). + * + * @return An `Option` representing `None`. + */ +#ifdef __cplusplus + #define none() (Option{null, 0}) +#else + #define none() ((Option){null, 0}) +#endif + +/** + * @brief Unwraps the `Option`. If it's `Some`, return the value; if it's `None`, panic. + * + * Mimics Rust's `Option::unwrap()`. + * + * @param opt The `Option` to unwrap. + * @return The value inside the `Option`. + */ +#define unwrap_option(opt) ((opt).is_some ? (opt).value : (fprintf(stderr, "Panic: Unwrapped a None value at %s:%d\n", __FILE__, __LINE__), exit(EXIT_FAILURE), null)) + +/** + * @brief Returns the value inside the `Option` or a default value if it's `None`. + * + * Mimics Rust's `Option::unwrap_or()`. + * + * @param opt The `Option` to unwrap. + * @param default_val The default value to return if the `Option` is `None`. + * @return The value inside the `Option`, or the default value if `None`. + */ +#define unwrap_or_option(opt, default_val) ((opt).is_some ? (opt).value : (default_val)) + +// ***************************************************************************** +// Command Pallet +// ***************************************************************************** + +typedef enum { + PIZZA_THEME_FOSSIL, // C,C++ Fossil Test Framework + PIZZA_THEME_CATCH, // C++ Test Framework + PIZZA_THEME_DOCTEST, // C++ Test Framework + PIZZA_THEME_CPPUTEST, // C Test Framework + PIZZA_THEME_TAP, // C Test Framework + PIZZA_THEME_GOOGLETEST, // C++ Test Framework + PIZZA_THEME_UNITY // C Test Framework +} fossil_pizza_cli_theme_t; + +typedef enum { + PIZZA_VERBOSE_PLAIN, + PIZZA_VERBOSE_CI, + PIZZA_VERBOSE_DOGE +} fossil_pizza_cli_verbose_t; + +typedef struct { + int dry_run; // Flag for dry run mode + struct { + int fail_fast; // Flag for --fail-fast + const char* only; // Value for --only + int repeat; // Value for --repeat + } run; // Run command flags + + struct { + const char* test_name; // Value for --test-name + const char* suite_name;// Value for --suite-name + const char* tag; // Value for --tag + } filter; // Filter command flags + + struct { + const char* by; // Value for --by + const char* order; // Value for --order + } sort; // Sort command flags + + struct { + const char* seed; // Value for --seed + int count; // Value for --count + const char* by; // Value for --by + } shuffle; // Shuffle command flags + fossil_pizza_cli_theme_t theme; // Theme option + fossil_pizza_cli_verbose_t verbose; // Verbose option +} fossil_pizza_pallet_t; + +// ***************************************************************************** +// exported flags +// ***************************************************************************** + +extern int G_PIZZA_DRY_RUN; +extern int G_PIZZA_FAIL_FAST; +extern int G_PIZZA_SKIP; +extern const char* G_PIZZA_ONLY; +extern int G_PIZZA_REPEAT; +extern fossil_pizza_cli_theme_t G_PIZZA_THEME; +extern fossil_pizza_cli_verbose_t G_PIZZA_VERBOSE; + +/** + * @brief Parses command line arguments and populates the pallet structure. + * + * This function processes command line arguments and fills the pallet structure + * with the parsed commands, flags, options, and configurations. + * + * @param pallet Pointer to the pallet structure to populate. + * @param argc The number of command line arguments. + * @param argv The command line arguments. + */ +fossil_pizza_pallet_t fossil_pizza_pallet_create(int argc, char** argv); + +// ***************************************************************************** +// Host information +// ***************************************************************************** + +// Memory information structure +typedef struct { + uint64_t total_memory; // in bytes + uint64_t free_memory; // in bytes +} pizza_sys_hostinfo_memory_t; + +// Endianness information structure +typedef struct { + int is_little_endian; // 1 if little-endian, 0 if big-endian +} pizza_sys_hostinfo_endianness_t; + +// System information structure +typedef struct { + char os_name[128]; + char os_version[128]; + char kernel_version[128]; +} pizza_sys_hostinfo_system_t; + +/** + * Retrieve system information. + * + * @param info A pointer to a structure that will be filled with system information. + * @return 0 on success, or a negative error code on failure. + */ +int pizza_sys_hostinfo_get_system(pizza_sys_hostinfo_system_t *info); + +/** + * Retrieve memory information. + * + * @param info A pointer to a structure that will be filled with memory information. + * @return 0 on success, or a negative error code on failure. + */ +int pizza_sys_hostinfo_get_memory(pizza_sys_hostinfo_memory_t *info); + +/** + * Retrieve endianness information. + * + * @param info A pointer to a structure that will be filled with endianness information. + * @return 0 on success, or a negative error code on failure. + */ +int pizza_sys_hostinfo_get_endianness(pizza_sys_hostinfo_endianness_t *info); + +// ***************************************************************************** +// Soap sanitizer +// ***************************************************************************** + +/** + * @brief Sanitize input text by removing or replacing "rot-brain" and meme-based language. + * + * @param text The input text to sanitize. + * @return A dynamically allocated sanitized string (must be freed by the caller). + */ +char *pizza_io_soap_sanitize(const char *text); + +/** + * @brief Suggest proper alternatives for rot-brain words or grammar fixes. + * + * @param text The input text. + * @return A dynamically allocated string with suggestions (must be freed by the caller). + */ +char *pizza_io_soap_suggest(const char *text); + +/** + * @brief Add a custom word or phrase to the filter. + * + * @param phrase The phrase to add. + * @return 0 on success, nonzero on failure. + */ +int pizza_io_soap_add_custom_filter(const char *phrase); + +/** + * @brief Clear all custom filters. + */ +void pizza_io_soap_clear_custom_filters(void); + +/** + * @brief Detect the tone of a sentence. + * + * @param text The input text. + * @return A string representing the detected tone ("formal", "casual", "sarcastic", etc.). + */ +const char *pizza_io_soap_detect_tone(const char *text); + +/** + * @brief Checks if the given text contains "rot-brain" language. + * + * @param text The input text to check. + * @return 1 if the text contains "rot-brain" language, 0 otherwise. + */ +int pizza_io_is_rot_brain(const char *text); + +// ***************************************************************************** +// Memory management +// ***************************************************************************** + +// Define pizza_sys_memory_t as void* +typedef void* pizza_sys_memory_t; + +/** + * Allocate memory. + * + * @param size The size of the memory to allocate. + * @return A pointer to the allocated memory. + * @throws Error message and exits if allocation fails. + */ +pizza_sys_memory_t pizza_sys_memory_alloc(size_t size); + +/** + * Reallocate memory. + * + * @param ptr A pointer to the previously allocated memory. + * @param size The new size of the memory to allocate. + * @return A pointer to the reallocated memory. + * @throws Error message and exits if reallocation fails or if the pointer is NULL. + */ +pizza_sys_memory_t pizza_sys_memory_realloc(pizza_sys_memory_t ptr, size_t size); + +/** + * Allocate and zero memory. + * + * @param num The number of elements to allocate. + * @param size The size of each element. + * @return A pointer to the allocated and zeroed memory. + * @throws Error message and exits if allocation fails. + */ +pizza_sys_memory_t pizza_sys_memory_calloc(size_t num, size_t size); + +/** + * Initialize allocated memory to a known state. + * + * @param ptr A pointer to the allocated memory. + * @param size The size of the memory. + * @param value The value to initialize the memory with (e.g., zero). + * @return A pointer to the initialized memory. + */ +pizza_sys_memory_t pizza_sys_memory_init(pizza_sys_memory_t ptr, size_t size, int32_t value); + +/** + * Free memory. + * + * @param ptr A pointer to the memory to free. + * @throws Error message and exits if the pointer is NULL. + */ +void pizza_sys_memory_free(pizza_sys_memory_t ptr); + +/** + * Copy memory. + * + * @param dest A pointer to the destination memory. + * @param src A pointer to the source memory. + * @param size The size of the memory to copy. + * @return A pointer to the destination memory. + * @throws Error message and exits if copying fails or if either source or destination is NULL. + */ +pizza_sys_memory_t pizza_sys_memory_copy(pizza_sys_memory_t dest, const pizza_sys_memory_t src, size_t size); + +/** + * Set memory. + * + * @param ptr A pointer to the memory to set. + * @param value The value to set. + * @param size The size of the memory to set. + * @return A pointer to the memory. + * @throws Error message and exits if setting fails or if the pointer is NULL. + */ +pizza_sys_memory_t pizza_sys_memory_set(pizza_sys_memory_t ptr, int32_t value, size_t size); + +/** + * Duplicate memory. + * + * @param src A pointer to the memory to duplicate. + * @param size The size of the memory to duplicate. + * @return A pointer to the duplicated memory. + * @throws Error message and exits if duplication fails or if the source is NULL. + */ +pizza_sys_memory_t pizza_sys_memory_dup(const pizza_sys_memory_t src, size_t size); + +/** + * Zero memory. + * + * @param ptr A pointer to the memory to zero. + * @param size The size of the memory to zero. + * @throws Error message and exits if the pointer is NULL. + */ +void pizza_sys_memory_zero(pizza_sys_memory_t ptr, size_t size); + +/** + * Compare memory. + * + * @param ptr1 A pointer to the first memory. + * @param ptr2 A pointer to the second memory. + * @param size The size of the memory to compare. + * @return The result of the comparison. + * @throws Error message and exits if the pointers are NULL or if the size is zero. + */ +int pizza_sys_memory_compare(const pizza_sys_memory_t ptr1, const pizza_sys_memory_t ptr2, size_t size); + +/** + * Move memory. + * + * @param dest A pointer to the destination memory. + * @param src A pointer to the source memory. + * @param size The size of the memory to move. + * @return A pointer to the destination memory. + * @throws Error message and exits if moving fails or if either source or destination is NULL. + */ +pizza_sys_memory_t pizza_sys_memory_move(pizza_sys_memory_t dest, const pizza_sys_memory_t src, size_t size); + +/** + * Resize memory. + * + * @param ptr A pointer to the memory to resize. + * @param old_size The old size of the memory. + * @param new_size The new size of the memory. + * @return A pointer to the resized memory. + * @throws Error message and exits if resizing fails or if the pointer is NULL. + */ +pizza_sys_memory_t pizza_sys_memory_resize(pizza_sys_memory_t ptr, size_t old_size, size_t new_size); + +/** + * Check if a memory pointer is valid. + * + * @param ptr A pointer to the memory. + * @return 1 if the memory is valid, 0 otherwise. + */ +bool pizza_sys_memory_is_valid(const pizza_sys_memory_t ptr); + +// ***************************************************************************** +// output management +// ***************************************************************************** + +/** + * Structure representing a file stream. + */ +typedef struct { + FILE *file; // Pointer to the FILE structure for the stream + char filename[500]; // Array to store the filename +} pizza_fstream_t; + +extern pizza_fstream_t *PIZZA_STDIN; +extern pizza_fstream_t *PIZZA_STDOUT; +extern pizza_fstream_t *PIZZA_STDERR; + +#define PIZZA_STDIN (PIZZA_STDIN) +#define PIZZA_STDOUT (PIZZA_STDOUT) +#define PIZZA_STDERR (PIZZA_STDERR) + +extern int32_t PIZZA_IO_COLOR_ENABLE; // Flag to enable/disable color output + +/** + * This code provides a robust set of functions for formatting and manipulating terminal output, + * allowing developers to apply color, text attributes (like bold, underline, etc.), and cursor positioning + * to improve the visual appeal and functionality of text-based applications. The functionality is primarily + * based on ANSI escape sequences, which are widely supported in most terminal environments (such as Linux terminals, + * macOS Terminal, and Windows terminals that support ANSI escape codes). + * + * The core concept behind this system is a simple markup language that allows developers to specify formatting + * instructions within the text output. These instructions are enclosed in curly braces `{}` within the format string + * and are processed dynamically to change the appearance of the text. The markup supports various styles, such as: + * + * 1. **Colors** - The code includes a set of predefined color codes for changing the foreground color of text. + * Supported colors include basic colors (e.g., red, green, blue) and bright colors (e.g., bright red, bright green, etc.). + * The colors are implemented using ANSI escape sequences and can be easily extended to support more colors if needed. + * + * 2. **Attributes** - Text attributes can be applied to the text to change its appearance. These attributes include: + * - **Bold** (for making text bold) + * - **Underline** (for adding an underline to the text) + * - **Reversed** (for inverting the colors of the text and background) + * - **Blink** (for making text blink) + * - **Hidden** (for hiding the text) + * - **Normal** (for reverting text back to its normal form, removing any attributes) + * + * 3. **Positioning** - The code introduces a flexible way to manipulate the position of the text in the terminal using + * named positions such as `top`, `bottom`, `left`, and `right`. These positions allow for text to be dynamically + * placed at specific locations on the terminal screen, enhancing the user experience for applications requiring + * more control over text layout and movement. The positions are marked with `pos:` followed by the desired + * position name (e.g., `{pos:top}` or `{pos:left}`). + * + * 4. **Flexibility and Extendability** - The markup language allows for the use of multiple color and attribute + * specifications in a single string. The color and attribute specifications can be combined, for instance, + * `{red,bold}` for red and bold text, or `{green,underline}` for green and underlined text. This allows for + * fine-grained control over the text output. The system is flexible enough to be extended with more attributes, + * colors, and positioning options as required. + * + * 5. **Implementation Details** - The function `pizza_io_print_with_attributes` processes the format string + * provided to it, looking for `{}` markers. When it encounters a `{}`, it checks if the enclosed string specifies + * a color, attribute, or position, and then calls the respective helper functions (`pizza_io_apply_color` and + * `pizza_io_apply_attribute`) to modify the terminal output accordingly. If a position marker is found (e.g., + * `{pos:top}`), it adjusts the cursor position in the terminal to the specified location. The code uses standard + * C string manipulation functions like `strchr`, `strncpy`, and `vsnprintf` to process the format string and apply + * the requested changes to the output. + * + * In summary, this system provides a highly customizable and intuitive way to format terminal text with colors, + * attributes, and positions, making it ideal for developers who want to build visually rich and interactive + * command-line interfaces. The markup-based approach is simple to use and can be easily extended to meet the + * needs of more complex applications. + */ + +/** + * Redirects the output to a specified stream. + * + * This function allows you to change the default output destination to a custom stream. + * It is useful when you want to redirect output to a file or another output stream. + * + * @param stream The output stream where subsequent output should be redirected. + */ +void pizza_io_redirect_output(pizza_fstream_t *stream); + +/** + * Prints a string to the output. + * + * This function outputs the provided string `str` to the terminal or console. It is a simple utility function + * that can be used for printing plain text to the screen. The string is printed as-is, with no formatting or + * color modifications applied. + * + * @param str The string to be printed. This should be a null-terminated string. + */ +void pizza_io_puts(const char *str); + +/** + * Prints a formatted string to the output. + * + * This function allows for formatted output, similar to `printf`. It takes a format string that can include + * format specifiers (e.g., `%d`, `%s`, `%f`), and the additional arguments provided will be formatted accordingly. + * The function uses a variable argument list (`...`) to handle a wide variety of format specifiers and argument types. + * + * The format string can also include custom formatting markers enclosed in curly braces `{}`, such as `{red}` for + * color or `{bold}` for text attributes, which will be processed and applied to the output. + * + * Example usage: + * ```c + * pizza_io_printf("Hello, %s! Your score is %d\n", "Alice", 95); + * ``` + * + * @param format The format string, which contains the text to be printed, along with format specifiers. + * @param ... The additional arguments to be formatted. These arguments are inserted into the format string + * in the order they appear, based on the format specifiers. + */ +void pizza_io_printf(const char *format, ...); + +/** + * Prints a character to the output. + * + * This function is a basic utility to print a single character to the output. It is especially useful when you + * need to print individual characters rather than strings or formatted text. + * + * Example usage: + * ```c + * pizza_io_putchar('A'); + * ``` + * + * @param c The character to be printed. This should be a single character. + */ +void pizza_io_putchar(char c); + +/** + * Prints a string to the specified output stream. + * + * This function is similar to `pizza_io_puts`, but instead of printing to the standard output, it allows you + * to specify an output stream (like a file or a custom output stream). This can be useful when writing to files + * or other output destinations. + * + * Example usage: + * ```c + * FILE *file = fopen("output.txt", "w"); + * pizza_io_fputs(file, "Hello, File Output!\n"); + * fclose(file); + * ``` + * + * @param stream The output stream where the string should be printed. This should be a valid pointer to a `FILE` object. + * @param str The string to be printed. This should be a null-terminated string. + */ +void pizza_io_fputs(pizza_fstream_t *stream, const char *str); + +/** + * Prints a formatted string to the specified output stream. + * + * This function is similar to `pizza_io_printf`, but instead of printing to the standard output, it allows you + * to specify an output stream. The format string can include format specifiers and custom formatting markers, just + * like `pizza_io_printf`. This can be useful when writing formatted text to files or other output destinations. + * + * Example usage: + * ```c + * FILE *file = fopen("output.txt", "w"); + * pizza_io_fprintf(file, "Hello, %s! Your score is %d\n", "Alice", 95); + * fclose(file); + * ``` + * + * @param stream The output stream where the formatted string should be printed. This should be a valid pointer to a `FILE` object. + * @param format The format string, which contains the text to be printed, along with format specifiers. + * @param ... The additional arguments to be formatted. These arguments are inserted into the format string + * in the order they appear, based on the format specifiers. + */ +void pizza_io_fprintf(pizza_fstream_t *stream, const char *format, ...); + +// TUI part of the API + +/** + * Clears the terminal screen. + * + * This function sends the ANSI escape sequence to clear the terminal screen + * and move the cursor to the top-left corner. It is useful when creating full-screen + * terminal applications or refreshing the display. + */ +void pizza_io_clear_screen(void); + +/** + * Moves the cursor to a specific row and column on the terminal. + * + * @param row The row position (starting from 1). + * @param col The column position (starting from 1). + */ +void pizza_io_move_cursor(int row, int col); + +/** + * Hides the cursor from the terminal screen. + * + * This is useful for creating cleaner UIs without a blinking cursor. + */ +void pizza_io_hide_cursor(void); + +/** + * Shows the cursor on the terminal screen. + */ +void pizza_io_show_cursor(void); + +/** + * Draws a horizontal line using a specified character. + * + * @param length The number of characters to draw. + * @param ch The character to use for drawing. + */ +void pizza_io_draw_horizontal_line(int length, char ch); + +/** + * Draws a vertical line using a specified character. + * + * @param length The number of characters to draw. + * @param ch The character to use for drawing. + */ +void pizza_io_draw_vertical_line(int length, char ch); + +/** + * Flushes the output stream, ensuring all buffered text is written. + * + * Useful when mixing multiple output functions or when printing from threads. + */ +void pizza_io_flush(void); + +// ***************************************************************************** +// string management +// ***************************************************************************** + +/* Type definitions */ +typedef char* cstr; +typedef const char* ccstr; + +/** + * @brief Creates a new cstr with the given initial value. + * + * @param init The initial value for the cstr. + * @return A new cstr initialized with the given value. + */ +cstr pizza_io_cstr_create(const char *init); + +/** + * @brief Frees the memory allocated for the given cstr. + * + * @param str The cstr to be freed. + */ +void pizza_io_cstr_free(cstr str); + +/** + * @brief Creates a copy of the given cstr. + * + * @param str The cstr to be copied. + * @return A new cstr that is a copy of the given cstr. + */ +cstr pizza_io_cstr_copy(ccstr str); + +/** + * @brief Duplicates the given cstr. + * + * @param str The cstr to be duplicated. + * @return A new cstr that is a duplicate of the given cstr. + */ +cstr pizza_io_cstr_dup(ccstr str); + +/** + * @brief Concatenates two cstrings into a new cstr. + * + * @param s1 The first cstr. + * @param s2 The second cstr. + * @return A new cstr that is the concatenation of s1 and s2. + */ +cstr pizza_io_cstr_concat(ccstr s1, ccstr s2); + +/** + * @brief Returns the length of the given cstr. + * + * @param str The cstr whose length is to be determined. + * @return The length of the given cstr. + */ +size_t pizza_io_cstr_length(ccstr str); + +/** + * @brief Compares two cstrings. + * + * @param s1 The first cstr. + * @param s2 The second cstr. + * @return An integer less than, equal to, or greater than zero if s1 is found, respectively, to be less than, to match, or be greater than s2. + */ +int pizza_io_cstr_compare(ccstr s1, ccstr s2); + +/** + * @brief Trims whitespace from the beginning and end of the given cstr. + * + * @param str The cstr to be trimmed. + */ +void pizza_io_cstr_trim(cstr str); + +/** + * @brief Splits the given cstr by the specified delimiter. + * + * @param str The cstr to be split. + * @param delimiter The character to split the cstr by. + * @param count Pointer to a size_t variable where the number of resulting substrings will be stored. + * @return An array of cstrings resulting from the split operation. + */ +cstr *pizza_io_cstr_split(ccstr str, char delimiter, size_t *count); + +/** + * @brief Replaces all occurrences of a substring within a cstr with another substring. + * + * @param str The original cstr. + * @param old The substring to be replaced. + * @param new_str The substring to replace with. + * @return A new cstr with the replacements made. + */ +cstr pizza_io_cstr_replace(ccstr str, ccstr old, ccstr new_str); + +/** + * @brief Converts all characters in the given cstr to uppercase. + * + * @param str The cstr to be converted. + * @return The cstr with all characters converted to uppercase. + */ +cstr pizza_io_cstr_to_upper(cstr str); + +/** + * @brief Converts all characters in the given cstr to lowercase. + * + * @param str The cstr to be converted. + * @return The cstr with all characters converted to lowercase. + */ +cstr pizza_io_cstr_to_lower(cstr str); + +/** + * @brief Checks if the given cstr starts with the specified prefix. + * + * @param str The cstr to be checked. + * @param prefix The prefix to check for. + * @return 1 if the cstr starts with the prefix, 0 otherwise. + */ +int pizza_io_cstr_starts_with(ccstr str, ccstr prefix); + +/** + * @brief Checks if the given cstr ends with the specified suffix. + * + * @param str The cstr to be checked. + * @param suffix The suffix to check for. + * @return 1 if the cstr ends with the suffix, 0 otherwise. + */ +int pizza_io_cstr_ends_with(ccstr str, ccstr suffix); + +/** + * @brief Extracts a substring from the given cstr. + * + * @param str The original cstr. + * @param start The starting index of the substring. + * @param length The length of the substring. + * @return A new cstr that is the specified substring of the original cstr. + */ +cstr pizza_io_cstr_substring(ccstr str, size_t start, size_t length); + +/** + * @brief Reverses the given cstr. + * + * @param str The cstr to be reversed. + * @return A new cstr that is the reverse of the given cstr. + */ +cstr pizza_io_cstr_reverse(cstr str); + +/** + * @brief Checks if the given cstr contains the specified substring. + * + * @param str The cstr to be checked. + * @param substr The substring to check for. + * @return 1 if the cstr contains the substring, 0 otherwise. + */ +int pizza_io_cstr_contains(ccstr str, ccstr substr); + +/** + * @brief Repeats the given cstr the specified number of times. + * + * @param str The cstr to be repeated. + * @param count The number of times to repeat the cstr. + * @return A new cstr that is the original cstr repeated the specified number of times. + */ +cstr pizza_io_cstr_repeat(ccstr str, size_t count); + +/** + * @brief Strips the given character from the beginning and end of the cstr. + * + * @param str The cstr to be stripped. + * @param ch The character to strip. + * @return A new cstr that is the original cstr with the specified character stripped from the beginning and end. + */ +cstr pizza_io_cstr_strip(ccstr str, char ch); + +/** + * @brief Counts the number of occurrences of a substring within the given cstr. + * + * @param str The cstr to be searched. + * @param substr The substring to search for. + * @return The number of occurrences of the substring within the cstr. + */ +size_t pizza_io_cstr_count(ccstr str, ccstr substr); + +/** + * @brief Pads the given cstr with the specified character on the left side. + * + * @param str The cstr to be padded. + * @param total_length The total length of the resulting cstr. + * @param pad_char The character to pad with. + * @return A new cstr that is the original cstr padded on the left side. + */ +cstr pizza_io_cstr_pad_left(ccstr str, size_t total_length, char pad_char); + +/** + * @brief Pads the given cstr with the specified character on the right side. + * + * @param str The cstr to be padded. + * @param total_length The total length of the resulting cstr. + * @param pad_char The character to pad with. + * @return A new cstr that is the original cstr padded on the right side. + */ +cstr pizza_io_cstr_pad_right(ccstr str, size_t total_length, char pad_char); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/code/logic/fossil/pizza/framework.h b/code/logic/fossil/pizza/framework.h new file mode 100644 index 00000000..8845b5ba --- /dev/null +++ b/code/logic/fossil/pizza/framework.h @@ -0,0 +1,24 @@ +/* + * ----------------------------------------------------------------------------- + * Project: Fossil Logic + * + * This file is part of the Fossil Logic project, which aims to develop high- + * performance, cross-platform applications and libraries. The code contained + * herein is subject to the terms and conditions defined in the project license. + * + * Author: Michael Gene Brockus (Dreamer) + * Date: 04/05/2014 + * + * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. + * ----------------------------------------------------------------------------- + */ +#ifndef FOSSIL_TEST_FRAMEWORK_H +#define FOSSIL_TEST_FRAMEWORK_H + +#include "assume.h" +#include "sanity.h" +#include "mark.h" +#include "test.h" +#include "mock.h" + +#endif diff --git a/code/logic/fossil/test/marking.h b/code/logic/fossil/pizza/mark.h similarity index 61% rename from code/logic/fossil/test/marking.h rename to code/logic/fossil/pizza/mark.h index c8ce261b..e5158f87 100644 --- a/code/logic/fossil/test/marking.h +++ b/code/logic/fossil/pizza/mark.h @@ -7,15 +7,15 @@ * herein is subject to the terms and conditions defined in the project license. * * Author: Michael Gene Brockus (Dreamer) - * Date: 07/01/2024 + * Date: 04/05/2014 * - * Copyright (C) 2024 Fossil Logic. All rights reserved. + * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. * ----------------------------------------------------------------------------- */ -#ifndef FOSSIL_MARK_BENCHMARK_H -#define FOSSIL_MARK_BENCHMARK_H +#ifndef FOSSIL_MARK_H +#define FOSSIL_MARK_H -#include "internal.h" +#include "common.h" #ifdef __cplusplus extern "C" { @@ -352,6 +352,232 @@ uint64_t fossil_test_stop_benchmark(void); */ #define _TEST_DURATION_YOC(elapsed, actual) _TEST_DURATION((char*)"yoctoseconds", elapsed, actual) +// ***************************************************************************** +// Public API Macros +// ***************************************************************************** + +/** + * @brief Define macro for marking a benchmark. + * + * This macro is used to mark a benchmark with a given name. It initializes + * the benchmark structure and sets the name of the benchmark. + * + * @param name The name of the benchmark. + */ +#define MARK_BENCHMARK(name) \ + _MARK_BENCHMARK(name) + +/** + * @brief Define macro for starting a benchmark. + * + * This macro is used to start a benchmark with a given name. It starts the + * timer for the benchmark. + * + * @param name The name of the benchmark. + */ +#define MARK_START(name) \ + _MARK_START(name) + +/** + * @brief Define macro for stopping a benchmark. + * + * This macro is used to stop a benchmark with a given name. It stops the + * timer for the benchmark. + * + * @param name The name of the benchmark. + */ +#define MARK_STOP(name) \ + _MARK_STOP(name) + +/** + * @brief Define macro for reporting a benchmark. + * + * This macro is used to report the results of a benchmark with a given name. + * It prints the benchmark name and the elapsed time. + * + * @param name The name of the benchmark. + */ +#define MARK_REPORT(name) \ + _MARK_REPORT(name) + +/** + * @brief Define macro for scoped benchmarking. + * + * This macro is used to create a scoped benchmark with a given name. It + * initializes the scoped benchmark structure and sets the benchmark to be + * used. + * + * @param name The name of the benchmark. + */ +#define MARK_SCOPED(name) \ + _MARK_SCOPED(name) + +// ================================================================= +// Bench specific commands +// ================================================================= + +/** + * @brief Define macro for starting a benchmark. + * + * This macro is used to mark the start of a benchmark. It typically initializes + * any necessary resources or variables required for benchmarking. + */ +#define TEST_BENCHMARK() \ + _TEST_BENCHMARK() + +/** + * @brief Define macro for getting the current time. + * + * This macro is used to retrieve the current time, which is typically used + * in conjunction with TEST_BENCHMARK to calculate the elapsed time for a benchmark. + */ +#define TEST_CURRENT_TIME() \ + _TEST_CURRENT_TIME() + +/** + * @brief Define macro for reporting test duration with a given timeout. + * + * This macro is used to report the duration of a test with a given timeout. + * It takes the timeout duration, elapsed time, and actual duration as arguments + * and reports the results, typically in the form of logs or console output. + * + * @param duration The duration unit (e.g., "minutes", "seconds"). + * @param elapsed The elapsed time since the benchmark started. + * @param actual The actual duration of the test. + */ +#define TEST_DURATION(duration, elapsed, actual) \ + _TEST_DURATION(duration, elapsed, actual) + +/** + * @brief Define macro for reporting test duration in minutes. + * + * This macro is a shorthand for reporting test duration in minutes using TEST_DURATION. + * It takes the elapsed time and actual duration as arguments and reports the results + * in minutes. + * + * @param elapsed The elapsed time since the benchmark started. + * @param actual The actual duration of the test. + */ +#define TEST_DURATION_MIN(elapsed, actual) \ + _TEST_DURATION_MIN(elapsed, actual) + +/** + * @brief Define macro for reporting test duration in seconds. + * + * This macro is a shorthand for reporting test duration in seconds using TEST_DURATION. + * It takes the elapsed time and actual duration as arguments and reports the results + * in seconds. + * + * @param elapsed The elapsed time since the benchmark started. + * @param actual The actual duration of the test. + */ +#define TEST_DURATION_SEC(elapsed, actual) \ + _TEST_DURATION_SEC(elapsed, actual) + +/** + * @brief Define macro for reporting test duration in milliseconds. + * + * This macro is a shorthand for reporting test duration in milliseconds using TEST_DURATION. + * It takes the elapsed time and actual duration as arguments and reports the results + * in milliseconds. + * + * @param elapsed The elapsed time since the benchmark started. + * @param actual The actual duration of the test. + */ +#define TEST_DURATION_MIL(elapsed, actual) \ + _TEST_DURATION_MIL(elapsed, actual) + +/** + * @brief Define macro for reporting test duration in microseconds. + * + * This macro is a shorthand for reporting test duration in microseconds using TEST_DURATION. + * It takes the elapsed time and actual duration as arguments and reports the results + * in microseconds. + * + * @param elapsed The elapsed time since the benchmark started. + * @param actual The actual duration of the test. + */ +#define TEST_DURATION_MIC(elapsed, actual) \ + _TEST_DURATION_MIC(elapsed, actual) + +/** + * @brief Define macro for reporting test duration in nanoseconds. + * + * This macro is a shorthand for reporting test duration in nanoseconds using TEST_DURATION. + * It takes the elapsed time and actual duration as arguments and reports the results + * in nanoseconds. + * + * @param elapsed The elapsed time since the benchmark started. + * @param actual The actual duration of the test. + */ +#define TEST_DURATION_NAN(elapsed, actual) \ + _TEST_DURATION_NAN(elapsed, actual) + +/** + * @brief Define macro for reporting test duration in picoseconds. + * + * This macro is a shorthand for reporting test duration in picoseconds using TEST_DURATION. + * It takes the elapsed time and actual duration as arguments and reports the results + * in picoseconds. + * + * @param elapsed The elapsed time since the benchmark started. + * @param actual The actual duration of the test. + */ +#define TEST_DURATION_PIC(elapsed, actual) \ + _TEST_DURATION_PIC(elapsed, actual) + +/** + * @brief Define macro for reporting test duration in femtoseconds. + * + * This macro is a shorthand for reporting test duration in femtoseconds using TEST_DURATION. + * It takes the elapsed time and actual duration as arguments and reports the results + * in femtoseconds. + * + * @param elapsed The elapsed time since the benchmark started. + * @param actual The actual duration of the test. + */ +#define TEST_DURATION_FEM(elapsed, actual) \ + _TEST_DURATION_FEM(elapsed, actual) + +/** + * @brief Define macro for reporting test duration in attoseconds. + * + * This macro is a shorthand for reporting test duration in attoseconds using TEST_DURATION. + * It takes the elapsed time and actual duration as arguments and reports the results + * in attoseconds. + * + * @param elapsed The elapsed time since the benchmark started. + * @param actual The actual duration of the test. + */ +#define TEST_DURATION_ATT(elapsed, actual) \ + _TEST_DURATION_ATT(elapsed, actual) + +/** + * @brief Define macro for reporting test duration in zeptoseconds. + * + * This macro is a shorthand for reporting test duration in zeptoseconds using TEST_DURATION. + * It takes the elapsed time and actual duration as arguments and reports the results + * in zeptoseconds. + * + * @param elapsed The elapsed time since the benchmark started. + * @param actual The actual duration of the test. + */ +#define TEST_DURATION_ZEP(elapsed, actual) \ + _TEST_DURATION_ZEP(elapsed, actual) + +/** + * @brief Define macro for reporting test duration in yoctoseconds. + * + * This macro is a shorthand for reporting test duration in yoctoseconds using TEST_DURATION. + * It takes the elapsed time and actual duration as arguments and reports the results + * in yoctoseconds. + * + * @param elapsed The elapsed time since the benchmark started. + * @param actual The actual duration of the test. + */ +#define TEST_DURATION_YOC(elapsed, actual) \ + _TEST_DURATION_YOC(elapsed, actual) + #ifdef __cplusplus } #endif diff --git a/code/logic/fossil/test/mocking.h b/code/logic/fossil/pizza/mock.h similarity index 53% rename from code/logic/fossil/test/mocking.h rename to code/logic/fossil/pizza/mock.h index 38805cb4..753cb36b 100644 --- a/code/logic/fossil/test/mocking.h +++ b/code/logic/fossil/pizza/mock.h @@ -1,23 +1,21 @@ /* * ----------------------------------------------------------------------------- - * File: framework.hpp * Project: Fossil Logic - * Description: This file implments the framework for mockup testing. * * This file is part of the Fossil Logic project, which aims to develop high- * performance, cross-platform applications and libraries. The code contained * herein is subject to the terms and conditions defined in the project license. * * Author: Michael Gene Brockus (Dreamer) - * Date: 07/01/2024 + * Date: 04/05/2014 * - * Copyright (C) 2024 Fossil Logic. All rights reserved. + * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. * ----------------------------------------------------------------------------- */ -#ifndef FOSSIL_MOCK_MOCKUP_H -#define FOSSIL_MOCK_MOCKUP_H +#ifndef FOSSIL_MOCK_H +#define FOSSIL_MOCK_H -#include "internal.h" +#include "common.h" #ifdef __cplusplus extern "C" { @@ -27,9 +25,53 @@ extern "C" { // Type declarations // ***************************************************************************** +// --- Pizza Data Types --- +typedef enum { + FOSSIL_MOCK_PIZZA_TYPE_I8, + FOSSIL_MOCK_PIZZA_TYPE_I16, + FOSSIL_MOCK_PIZZA_TYPE_I32, + FOSSIL_MOCK_PIZZA_TYPE_I64, + FOSSIL_MOCK_PIZZA_TYPE_U8, + FOSSIL_MOCK_PIZZA_TYPE_U16, + FOSSIL_MOCK_PIZZA_TYPE_U32, + FOSSIL_MOCK_PIZZA_TYPE_U64, + FOSSIL_MOCK_PIZZA_TYPE_HEX, + FOSSIL_MOCK_PIZZA_TYPE_OCTAL, + FOSSIL_MOCK_PIZZA_TYPE_FLOAT, + FOSSIL_MOCK_PIZZA_TYPE_DOUBLE, + FOSSIL_MOCK_PIZZA_TYPE_WSTR, + FOSSIL_MOCK_PIZZA_TYPE_CSTR, + FOSSIL_MOCK_PIZZA_TYPE_CCHAR, + FOSSIL_MOCK_PIZZA_TYPE_WCHAR, + FOSSIL_MOCK_PIZZA_TYPE_BOOL, + FOSSIL_MOCK_PIZZA_TYPE_SIZE, + FOSSIL_MOCK_PIZZA_TYPE_ANY +} fossil_mock_pizza_type_t; + +typedef struct { + char *data; + bool mutable_flag; +} fossil_mock_pizza_value_t; + +typedef struct { + char* name; + char* description; + char* id; +} fossil_mock_pizza_attribute_t; + +typedef struct { + fossil_mock_pizza_type_t type; + fossil_mock_pizza_value_t value; + fossil_mock_pizza_attribute_t attribute; +} fossil_mock_pizza_t; + +// ****************************************************************************** +// Mock call structure +// ***************************************************************************** + typedef struct fossil_mock_call_t { char *function_name; - char **arguments; + fossil_mock_pizza_t *arguments; // Use pizza type for arguments int num_args; struct fossil_mock_call_t *next; } fossil_mock_call_t; @@ -66,7 +108,7 @@ void fossil_mock_destroy(fossil_mock_calllist_t *list); * @param arguments The arguments passed to the function. * @param num_args The number of arguments. */ -void fossil_mock_add_call(fossil_mock_calllist_t *list, const char *function_name, char **arguments, int num_args); +void fossil_mock_add_call(fossil_mock_calllist_t *list, const char *function_name, fossil_mock_pizza_t *arguments, int num_args); /** * Prints the contents of a fossil_mock_calllist_t. @@ -169,4 +211,92 @@ void fossil_mock_print(fossil_mock_calllist_t *list); typedef struct name #endif +// ***************************************************************************** +// Public API Macros +// ***************************************************************************** + +/** + * @brief Macro for initializing the mock list. + * + * This macro initializes the mock list by calling the fossil_mock_init function. + * + * @param list The mock list to initialize. + */ +#define MOCK_INIT(list) \ + _MOCK_INIT(list) + +/** + * @brief Macro for destroying the mock list. + * + * This macro destroys the mock list by calling the fossil_mock_destroy function. + * + * @param list The mock list to destroy. + */ +#define MOCK_DESTROY(list) \ + _MOCK_DESTROY(list) + +/** + * @brief Macro for adding a mock function call to the mock list. + * + * This macro adds a mock function call to the mock list by calling the fossil_mock_add_call function. + * + * @param list The mock list to add the call to. + * @param func The mock function to add the call for. + * @param args The arguments of the mock function call. + * @param num_args The number of arguments in the mock function call. + */ +#define MOCK_ADD_CALL(list, func, args, num_args) \ + _MOCK_ADD_CALL(list, func, args, num_args) + +/** + * @brief Macro for printing the mock list. + * + * This macro prints the mock list by calling the fossil_mock_print function. + * + * @param list The mock list to print. + */ +#define MOCK_PRINT(list) \ + _MOCK_PRINT(list) + +/** + * @def FOSSIL_MOCK_FUNC + * @brief Macro for creating a mock function with the specified return type, name, and parameters. + * + * This macro simplifies the creation of mock functions by defining a function with the given return + * type, name, and parameters. The function name will be prefixed with "fossil_mockup_" to clearly indicate + * that it is a mock function. + * + * @param return_type The return type of the mock function. + * @param name The name of the mock function. + * @param ... The parameters of the mock function in the format: (type1 param1, type2 param2, ...). + * @return The return type specified for the mock function. + */ +#define FOSSIL_MOCK_FUNC(return_type, name, ...) \ + _FOSSIL_MOCK_FUNC(return_type, name, __VA_ARGS__) + +/** + * @def FOSSIL_MOCK_ALIAS + * @brief Macro for creating a type alias based on an existing type. + * + * This macro creates a type alias for a given existing type. + * + * @param new_type The name of the new type alias. + * @param existing_type The existing type to create an alias for. + */ +#define FOSSIL_MOCK_ALIAS(new_type, existing_type) \ + _FOSSIL_MOCK_ALIAS(new_type, existing_type) + +/** + * @def FOSSIL_MOCK_STRUCT + * + * @brief Macro for creating a mock struct with the specified name and members. + * + * This macro simplifies the creation of mock structs by defining a struct with the given name + * and members. The struct name will be prefixed with "fossil_mockup_" to clearly indicate that it is a mock struct. + * + * @param name The name of the mock struct. + */ +#define FOSSIL_MOCK_STRUCT(name) \ + _FOSSIL_MOCK_STRUCT(name) + #endif // FOSSIL_MOCK_FRAMEWORK_H diff --git a/code/logic/fossil/pizza/sanity.h b/code/logic/fossil/pizza/sanity.h new file mode 100644 index 00000000..6e84f1e2 --- /dev/null +++ b/code/logic/fossil/pizza/sanity.h @@ -0,0 +1,240 @@ +/* + * ----------------------------------------------------------------------------- + * Project: Fossil Logic + * + * This file is part of the Fossil Logic project, which aims to develop high- + * performance, cross-platform applications and libraries. The code contained + * herein is subject to the terms and conditions defined in the project license. + * + * Author: Michael Gene Brockus (Dreamer) + * Date: 04/05/2014 + * + * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. + * ----------------------------------------------------------------------------- + */ +#ifndef FOSSIL_SANITY_H +#define FOSSIL_SANITY_H + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Executes a system command and returns the exit code. + * + * This function takes a command string as input, executes it in the system shell, + * and returns the exit code of the command. The exit code can be used to determine + * whether the command executed successfully or encountered an error. + * + * @param command A null-terminated string representing the system command to execute. + * The command should be formatted as it would be entered in a terminal. + * @return int The exit code of the executed command. A value of 0 typically indicates + * success, while non-zero values indicate errors or specific exit statuses. + */ +int fossil_sanity_sys_execute(const char* command); + +/** + * @brief Retrieves the process ID of the current process. + * + * This function returns the unique identifier (PID) of the process in which it is called. + * The PID can be used for various purposes, such as debugging, inter-process communication, + * or process management. + * + * @return int The process ID of the current process. + */ +int fossil_sanity_sys_getpid(void); + +/** + * @brief Suspends the execution of the current thread for a specified duration. + * + * This function pauses the execution of the calling thread for the given number of + * milliseconds. It is useful for introducing delays or waiting for a specific amount + * of time before proceeding with further execution. + * + * @param milliseconds The number of milliseconds to sleep. Must be a non-negative value. + * A value of 0 may yield the processor to other threads without delay. + */ +void fossil_sanity_sys_sleep(int milliseconds); + +/** + * @brief Creates an empty file at the specified location. + * + * This function attempts to create an empty file with the given filename. If the file + * already exists, its contents may be overwritten or truncated depending on the system's + * behavior. The function returns a status code indicating success or failure. + * + * @param filename A null-terminated string representing the path to the file to be created. + * The path can be relative or absolute. + * @return int Returns 0 on successful creation of the file. Returns a negative value if + * the file could not be created due to errors such as insufficient permissions + * or invalid file paths. + */ +int fossil_sanity_sys_create_file(const char* filename); + +/** + * @brief Checks whether a file exists at the specified location. + * + * This function determines if a file exists at the given path. It can be used to verify + * the presence of a file before performing operations such as reading or writing. The + * function does not differentiate between regular files, directories, or other file types. + * + * @param filename A null-terminated string representing the path to the file to check. + * The path can be relative or absolute. + * @return int Returns 1 if the file exists, and 0 if it does not exist. Note that this + * function does not check for file accessibility or permissions. + */ +int fossil_sanity_sys_file_exists(const char* filename); + +#ifdef __cplusplus +} +#endif + +// ***************************************************************************** +// Private API Macros +// ***************************************************************************** + +/** + * @brief Executes a system command and returns the exit code. + * + * This function takes a command string as input, executes it in the system shell, + * and returns the exit code of the command. The exit code can be used to determine + * whether the command executed successfully or encountered an error. + * + * @param command A null-terminated string representing the system command to execute. + * The command should be formatted as it would be entered in a terminal. + * @return int The exit code of the executed command. A value of 0 typically indicates + * success, while non-zero values indicate errors or specific exit statuses. + */ +#define _FOSSIL_SANITY_SYS_EXECUTE(command) \ + fossil_sanity_sys_execute(command) + +/** + * @brief Retrieves the process ID of the current process. + * + * This function returns the unique identifier (PID) of the process in which it is called. + * The PID can be used for various purposes, such as debugging, inter-process communication, + * or process management. + * + * @return int The process ID of the current process. + */ +#define _FOSSIL_SANITY_SYS_GETPID() \ + fossil_sanity_sys_getpid() + +/** + * @brief Suspends the execution of the current thread for a specified duration. + * + * This function pauses the execution of the calling thread for the given number of + * milliseconds. It is useful for introducing delays or waiting for a specific amount + * of time before proceeding with further execution. + * + * @param milliseconds The number of milliseconds to sleep. Must be a non-negative value. + * A value of 0 may yield the processor to other threads without delay. + */ +#define _FOSSIL_SANITY_SYS_SLEEP(milliseconds) \ + fossil_sanity_sys_sleep(milliseconds) + +/** + * @brief Creates an empty file at the specified location. + * + * This function attempts to create an empty file with the given filename. If the file + * already exists, its contents may be overwritten or truncated depending on the system's + * behavior. The function returns a status code indicating success or failure. + * + * @param filename A null-terminated string representing the path to the file to be created. + * The path can be relative or absolute. + * @return int Returns 0 on successful creation of the file. Returns a negative value if + * the file could not be created due to errors such as insufficient permissions + * or invalid file paths. + */ +#define _FOSSIL_SANITY_SYS_CREATE_FILE(filename) \ + fossil_sanity_sys_create_file(filename) + +/** + * @brief Checks whether a file exists at the specified location. + * + * This function determines if a file exists at the given path. It can be used to verify + * the presence of a file before performing operations such as reading or writing. The + * function does not differentiate between regular files, directories, or other file types. + * + * @param filename A null-terminated string representing the path to the file to check. + * The path can be relative or absolute. + * @return int Returns 1 if the file exists, and 0 if it does not exist. Note that this + * function does not check for file accessibility or permissions. + */ +#define _FOSSIL_SANITY_SYS_FILE_EXISTS(filename) \ + fossil_sanity_sys_file_exists(filename) + +// ***************************************************************************** +// Public API Macros +// ***************************************************************************** + +/** + * @brief Executes a system command and returns the exit code. + * + * This macro is a wrapper around the _FOSSIL_SANITY_SYS_EXECUTE function. + * It is used to execute a system command and retrieve its exit code. + * + * @param command A null-terminated string representing the system command to execute. + * The command should be formatted as it would be entered in a terminal. + * @return int The exit code of the executed command. A value of 0 typically indicates + * success, while non-zero values indicate errors or specific exit statuses. + */ +#define FOSSIL_SANITY_SYS_EXECUTE(command) \ + _FOSSIL_SANITY_SYS_EXECUTE(command) + +/** + * @brief Retrieves the process ID of the current process. + * + * This macro is a wrapper around the _FOSSIL_SANITY_SYS_GETPID function. + * It is used to retrieve the unique identifier (PID) of the current process. + * + * @return int The process ID of the current process. + */ +#define FOSSIL_SANITY_SYS_GETPID() \ + _FOSSIL_SANITY_SYS_GETPID() + +/** + * @brief Suspends the execution of the current thread for a specified duration. + * + * This macro is a wrapper around the _FOSSIL_SANITY_SYS_SLEEP function. + * It is used to pause the execution of the calling thread for a given number + * of milliseconds. + * + * @param milliseconds The number of milliseconds to sleep. Must be a non-negative value. + * A value of 0 may yield the processor to other threads without delay. + */ +#define FOSSIL_SANITY_SYS_SLEEP(milliseconds) \ + _FOSSIL_SANITY_SYS_SLEEP(milliseconds) + +/** + * @brief Creates an empty file at the specified location. + * + * This macro is a wrapper around the _FOSSIL_SANITY_SYS_CREATE_FILE function. + * It is used to create an empty file with the given filename. + * + * @param filename A null-terminated string representing the path to the file to be created. + * The path can be relative or absolute. + * @return int Returns 0 on successful creation of the file. Returns a negative value if + * the file could not be created due to errors such as insufficient permissions + * or invalid file paths. + */ +#define FOSSIL_SANITY_SYS_CREATE_FILE(filename) \ + _FOSSIL_SANITY_SYS_CREATE_FILE(filename) + +/** + * @brief Checks whether a file exists at the specified location. + * + * This macro is a wrapper around the _FOSSIL_SANITY_SYS_FILE_EXISTS function. + * It is used to check if a file exists at the given path. + * + * @param filename A null-terminated string representing the path to the file to check. + * The path can be relative or absolute. + * @return int Returns 1 if the file exists, and 0 if it does not exist. Note that this + * function does not check for file accessibility or permissions. + */ +#define FOSSIL_SANITY_SYS_FILE_EXISTS(filename) \ + _FOSSIL_SANITY_SYS_FILE_EXISTS(filename) + +#endif // FOSSIL_SANITY_H diff --git a/code/logic/fossil/pizza/test.h b/code/logic/fossil/pizza/test.h new file mode 100644 index 00000000..0db0c031 --- /dev/null +++ b/code/logic/fossil/pizza/test.h @@ -0,0 +1,935 @@ +/* + * ----------------------------------------------------------------------------- + * Project: Fossil Logic + * + * This file is part of the Fossil Logic project, which aims to develop high- + * performance, cross-platform applications and libraries. The code contained + * herein is subject to the terms and conditions defined in the project license. + * + * Author: Michael Gene Brockus (Dreamer) + * Date: 04/05/2014 + * + * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. + * ----------------------------------------------------------------------------- + */ +#ifndef FOSSIL_TEST_H +#define FOSSIL_TEST_H + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// --- Return codes --- +enum { + FOSSIL_PIZZA_SUCCESS = 0, + FOSSIL_PIZZA_FAILURE = -1 +}; + +typedef enum { + FOSSIL_PIZZA_CASE_EMPTY = 0, + FOSSIL_PIZZA_CASE_PASS, + FOSSIL_PIZZA_CASE_FAIL, + FOSSIL_PIZZA_CASE_TIMEOUT, + FOSSIL_PIZZA_CASE_SKIPPED, + FOSSIL_PIZZA_CASE_UNEXPECTED +} fossil_pizza_case_result_t; + +// --- Score Struct --- +typedef struct { + int passed; + int failed; + int skipped; + int timeout; + int unexpected; + int empty; +} fossil_pizza_score_t; + +// --- Test Case --- +typedef struct { + char* name; + char* tags; + char* criteria; + void (*setup)(void); + void (*teardown)(void); + void (*run)(void); + uint64_t elapsed_ns; + fossil_pizza_case_result_t result; +} fossil_pizza_case_t; + +// In fossil_pizza_suite_t +typedef struct { + char* suite_name; + fossil_pizza_case_t* cases; + size_t count; + size_t capacity; + void (*setup)(void); + void (*teardown)(void); + uint64_t time_elapsed_ns; + int total_score; + int total_possible; + fossil_pizza_score_t score; +} fossil_pizza_suite_t; + +// In fossil_pizza_engine_t +typedef struct { + fossil_pizza_suite_t* suites; + size_t count; + size_t capacity; + int score_total; + int score_possible; + fossil_pizza_score_t score; + fossil_pizza_pallet_t pallet; +} fossil_pizza_engine_t; + +// --- Initialization --- + +/** * Initializes a new fossil_pizza_engine_t instance. + * @param engine Pointer to the engine to initialize. + * @param argc The number of command line arguments. + * @param argv The command line arguments. + * @return 0 on success, -1 on failure. + */ +int fossil_pizza_start(fossil_pizza_engine_t* engine, int argc, char** argv); + +// --- Adding Test Suites and Cases --- + +/** Adds a test suite to the engine. + * @param engine Pointer to the engine instance. + * @param suite The suite to add. + * @return 0 on success, -1 on failure. + */ +int fossil_pizza_add_suite(fossil_pizza_engine_t* engine, fossil_pizza_suite_t suite); + +/** Adds a test case to a suite. + * @param suite Pointer to the suite instance. + * @param test_case Pointer to the test case to add. + * @return 0 on success, -1 on failure. + */ +int fossil_pizza_add_case(fossil_pizza_suite_t* suite, fossil_pizza_case_t test_case); + +// --- Execution --- + +/** Runs a single test suite. + * @param suite Pointer to the suite instance. + * @return 0 on success, -1 on failure. + */ +int fossil_pizza_run_suite(const fossil_pizza_engine_t* engine, fossil_pizza_suite_t* suite); + +/** Runs all test suites in the engine. + * @param engine Pointer to the engine instance. + * @return 0 on success, -1 on failure. + */ +int fossil_pizza_run_all(fossil_pizza_engine_t* engine); + +// --- Summary + Teardown --- + +/** Prints a summary of the test results. + * @param engine Pointer to the engine instance. + */ +void fossil_pizza_summary(const fossil_pizza_engine_t* engine); + +/** Cleans up and ends the test engine. + * @param engine Pointer to the engine instance. + */ +int32_t fossil_pizza_end(fossil_pizza_engine_t* engine); + +/** + * @brief Internal function to handle assertions with anomaly detection. + * + * This function is used internally by the test framework to handle assertions + * and detect duplicate assertions. It is not intended to be called directly. + * + * @param condition The condition to check. + * @param message The message to display if the condition is false. + * @param file The file name where the assertion occurred. + * @param line The line number where the assertion occurred. + * @param func The function name where the assertion occurred. + */ +void pizza_test_assert_internal(bool condition, const char *message, const char *file, int line, const char *func); + +// ********************************************************************************************* +// internal messages +// ********************************************************************************************* + +/** + * @brief Internal function to handle the "given" step in a test case. + * + * This function is used to set up the context for a test case. It is not + * intended to be called directly. + * + * @param description The description of the given step. + */ +void _given(const char *description); + +/** + * @brief Internal function to handle the "when" step in a test case. + * + * This function is used to set up the action for a test case. It is not + * intended to be called directly. + * + * @param description The description of the when step. + */ +void _when(const char *description); + +/** + * @brief Internal function to handle the "then" step in a test case. + * + * This function is used to set up the expected outcome for a test case. It is + * not intended to be called directly. + * + * @param description The description of the then step. + */ +void _then(const char *description); + +/** + * @brief Internal function to handle the "skip" step in a test case. + * + * This function is used to skip a test case. It is not intended to be called + * directly. + * + * @param description The description of the skip step. + */ +void _on_skip(const char *description); + +#ifdef __cplusplus +} +#endif + +// ********************************************************************************************* +// Private API Macros +// ********************************************************************************************* + +/** @brief Macro to define a test case. + * + * This macro is used to define a test case, which is a single unit of testing + * that verifies a specific functionality or behavior. The test case can be + * executed independently or as part of a test suite. + * + * @param test_name The name of the test case to define. + */ + + #ifdef __cplusplus + #define _FOSSIL_TEST(test_name) \ + extern "C" void test_name##_run(void); \ + static fossil_pizza_case_t test_case_##test_name = { \ + (cstr)#test_name, \ + (cstr)"fossil", \ + (cstr)"name", \ + nullptr, \ + nullptr, \ + test_name##_run, \ + 0, \ + FOSSIL_PIZZA_CASE_EMPTY \ + }; \ + extern "C" void test_name##_run(void) + #else + #define _FOSSIL_TEST(test_name) \ + void test_name##_run(void); \ + static fossil_pizza_case_t test_case_##test_name = { \ + .name = #test_name, \ + .tags = "fossil", \ + .criteria = "name", \ + .setup = NULL, \ + .teardown = NULL, \ + .run = test_name##_run, \ + .elapsed_ns = 0, \ + .result = FOSSIL_PIZZA_CASE_EMPTY \ + }; \ + void test_name##_run(void) + #endif + + /** @brief Macro to set a test case's tags. + * + * This macro is used to specify tags for a test case. Tags can be used to + * categorize or filter test cases based on specific criteria. + * + * @param test_name The name of the test case. + * @param tags The tags to assign to the test case. + */ + #define _FOSSIL_TEST_SET_TAGS(test_name, tags) \ + test_case_##test_name.tags = tags + + /** @brief Macro to set a test case's skip message. + * + * This macro is used to specify a skip message for a test case. The skip + * message indicates that the test case is intentionally skipped and provides + * a reason for the skip. + * + * @param test_name The name of the test case. + * @param skip The skip message to assign to the test case. + */ +#define _FOSSIL_TEST_SET_SKIP(test_name, skip) \ + test_case_##test_name.result = FOSSIL_PIZZA_CASE_SKIPPED; \ + test_case_##test_name.tags = skip \ + test_case_##test_name.teardown = _on_skip(skip) + +/** @brief Macro to set a test case's criteria. + * + * This macro is used to specify criteria for a test case. The criteria can be + * used to filter or categorize test cases based on specific conditions or + * requirements. + * + * @param test_name The name of the test case. + * @param criteria The criteria to assign to the test case. + */ +#define _FOSSIL_TEST_SET_CRITERIA(test_name, criteria) \ + test_case_##test_name.criteria = criteria + +/** @brief Macro to set a test case's setup function. + * + * This macro is used to specify a setup function for a test case. The setup + * function will be called before the test case is executed, allowing for any + * necessary initialization or preparation. + * + * @param test_name The name of the test case. + * @param before The name of the setup function to call before the test case. + */ +#define _FOSSIL_TEST_SET_BEFORE(test_name, before) \ + test_case_##test_name.setup = setup_before_##before + +/** @brief Macro to set a test case's setup function. + * + * This macro is used to specify a setup function for a test case. The setup + * function will be called before the test case is executed, allowing for any + * necessary initialization or preparation. + * + * @param test_name The name of the test case. + * @param before The name of the setup function to call before the test case. + */ +#define _FOSSIL_TEST_SET_AFTER(test_name, after) \ + test_case_##test_name.teardown = teardown_after_##after + +/** @brief Macro to define a test suite. + * + * This macro is used to define a test suite, which is a collection of test cases + * that are related to each other. The test suite can be executed as a whole to + * verify the correctness of a group of functionalities. + * + * @param suite_name The name of the suite to define. + */ +#ifdef __cplusplus +#define _FOSSIL_SUITE(suite) \ + void setup_##suite(void); \ + void teardown_##suite(void); \ + static fossil_pizza_suite_t suite_##suite { \ + (cstr)#suite, \ + nullptr, \ + 0, \ + 0, \ + setup_##suite, \ + teardown_##suite, \ + 0, \ + 0, \ + 0, \ + {0, 0, 0, 0, 0, 0} \ + } +#else +#define _FOSSIL_SUITE(suite) \ + void setup_##suite(void); \ + void teardown_##suite(void); \ + static fossil_pizza_suite_t suite_##suite = { \ + .suite_name = #suite, \ + .cases = NULL, \ + .count = 0, \ + .capacity = 0, \ + .setup = setup_##suite, \ + .teardown = teardown_##suite, \ + .time_elapsed_ns = 0, \ + .total_score = 0, \ + .total_possible = 0, \ + .score = {0, 0, 0, 0, 0, 0} \ + } +#endif + +/** @brief Macro to define a test setup function. + * + * This macro is used to define a setup function for a test case or suite. The + * setup function will be called before the test case or suite is executed, and + * can be used to initialize resources or set up the test environment. + * + * @param test_setup The name of the setup function to define. + */ +#define _FOSSIL_SETUP(test_setup) \ + void setup_##test_setup(void) + +/** @brief Macro to define a test setup function. + * + * This macro is used to define a setup function for a test case or suite. The + * setup function will be called before the test case or suite is executed, and + * can be used to initialize resources or set up the test environment. + * + * @param test_setup The name of the setup function to define. + */ +#define _FOSSIL_TEARDOWN(test_teardown) \ + void teardown_##test_teardown(void) + +/** @brief Macro to define a test setup function. + * + * This macro is used to define a setup function for a test case or suite. The + * setup function will be called before the test case or suite is executed, and + * can be used to initialize resources or set up the test environment. + * + * @param test_setup The name of the setup function to define. + */ +#define _FOSSIL_BEFORE(test_setup) \ + void setup_before_##test_setup(void) + + /** @brief Macro to define a test teardown function. + * + * This macro is used to define a teardown function for a test case or suite. The + * teardown function will be called after the test case or suite has been executed, and + * can be used to clean up resources or reset the test environment. + * + * @param test_teardown The name of the teardown function to define. + */ + #define _FOSSIL_AFTER(test_teardown) \ + void teardown_after_##test_teardown(void) + + /** @brief Macro to add a test case to a specific suite. + * + * This macro is used to add a test case to a specific test suite that has been + * defined in the test engine. It will link the test case to the suite and + * ensure it is executed when the suite is run. + * + * @param suite The name of the suite to which the test case will be added. + * @param test_case The name of the test case to add. + */ + #define _FOSSIL_TEST_ADD(suite, test_case) \ + fossil_pizza_add_case(&suite_##suite, test_case_##test_case) + + /** @brief Macro to run a specific test suite. + * + * This macro is used to run a specific test suite that has been defined in the + * test engine. It will execute all test cases within the specified suite and + * print the results. + * + * @param suite The name of the suite to run. + */ + #define _FOSSIL_RUN_SUITE(suite) \ + fossil_pizza_run_suite(&engine, &suite_##suite) + + /** @brief Macro to start the test engine. + * + * This macro is used to initialize and start the test engine. It should be called + * at the beginning of the test execution to set up the environment for running + * tests. + * + * @param argc The number of command line arguments. + * @param argv The command line arguments. + */ + #define _FOSSIL_TEST_START(argc, argv) \ + fossil_pizza_engine_t engine; \ + if (fossil_pizza_start(&engine, argc, argv) != FOSSIL_PIZZA_SUCCESS) { \ + return FOSSIL_PIZZA_FAILURE; \ + } + + /** @brief Macro to print a summary of the test results. + * + * This macro is used to print a summary of the test results after all tests have + * been run. It provides an overview of the number of tests passed, failed, and + * skipped. + * + * @param engine The engine instance containing the test results. + */ + #define _FOSSIL_SUMMARY() \ + fossil_pizza_summary(&engine) + + /** @brief Macro to run all test suites in the engine. + * + * This macro is used to run all test suites that have been added to the test + * engine. It will execute each suite and its associated test cases, collecting + * results and printing a summary at the end. + * + * @param engine The engine instance containing the test suites. + */ + #define _FOSSIL_RUN_ALL() \ + fossil_pizza_run_all(&engine) + + /** @brief Macro to end the test engine. + * + * This macro is used to clean up and end the test engine after all tests have + * been run. It should be called at the end of the test execution. + * + * @param engine The engine instance to end. + */ + #define _FOSSIL_END() \ + fossil_pizza_end(&engine) + + + /** + * @brief Macro to register a test suite with the engine. + * + * This macro is used to register a test suite with the test engine. It will + * ensure that the suite is added to the engine and can be executed when the + * test runner is run. + * + * @param suite The name of the suite to register. + */ + #define _FOSSIL_TEST_REGISTER(suite) \ + fossil_pizza_add_suite(engine, suite_##suite) + + /** + * @brief Macro to define a test group. + * + * This macro is used to define a test group, which is a collection of test cases + * that are related to each other. The test group can be executed as a whole to + * verify the correctness of a group of functionalities. + * + * @param name The name of the test group. + */ + #ifdef __cplusplus + #define _FOSSIL_TEST_GROUP(name) \ + extern "C" void name##_test_group(fossil_pizza_engine_t *engine) + #else + #define _FOSSIL_TEST_GROUP(name) \ + void name##_test_group(fossil_pizza_engine_t *engine) + #endif + + /** + * @brief Macro to export a test group. + * + * This macro is used to export a test group from a test file. The test group + * will be available to other test files that import it. + * + * @param name The name of the test group to export. + */ + #ifdef __cplusplus + #define _FOSSIL_TEST_EXPORT(name) \ + extern "C" void name##_test_group(fossil_pizza_engine_t *engine) + #else + #define _FOSSIL_TEST_EXPORT(name) \ + void name##_test_group(fossil_pizza_engine_t *engine) + #endif + + /** + * @brief Macro to import a test group. + * + * This macro is used to import a test group into the test runner. The test group + * will be executed when the test runner is run. + * + * @param name The name of the test group to import. + */ + #ifdef __cplusplus + #define _FOSSIL_TEST_IMPORT(name) \ + extern "C" void name##_test_group(fossil_pizza_engine_t *engine) + #else + #define _FOSSIL_TEST_IMPORT(name) \ + name##_test_group(&engine) + #endif + + /** + * @brief Macro to assume a condition in a test runner. + * This macro is used to assert that a specific condition is true within a test + * runner. If the condition is false, the test runner will output the specified + * message and may abort the execution of the test case or test suite. + */ + #define _FOSSIL_TEST_ASSUME(condition, message) \ + pizza_test_assert_internal((condition), (message), __FILE__, __LINE__, __func__) + + /** + * @brief Macro to assume a condition in a test runner. + * This macro is used to assert that a specific condition is true within a test + * runner. If the condition is false, the test runner will output the specified + * message and may abort the execution of the test case or test suite. + */ + #define _FOSSIL_TEST_ASSERT(condition, message) \ + pizza_test_assert_internal((condition), (message), __FILE__, __LINE__, __func__) + + + /** + * @brief Macro for defining a Given step in a behavior-driven development test. + * + * This macro is used to define a Given step in a behavior-driven development test. + * The Given step is used to specify the initial context of a test case. + * + * @param description The description of the Given step. + */ + #define _GIVEN(description) \ + if (0) { \ + _given(description); \ + } + + /** + * @brief Macro for defining a When step in a behavior-driven development test. + * + * This macro is used to define a When step in a behavior-driven development test. + * The When step is used to specify the action that is being tested. + * + * @param description The description of the When step. + */ + #define _WHEN(description) \ + if (0) { \ + _when(description); \ + } + + /** + * @brief Macro for defining a Then step in a behavior-driven development test. + * + * This macro is used to define a Then step in a behavior-driven development test. + * The Then step is used to specify the expected outcome of a test case. + * + * @param description The description of the Then step. + */ + #define _THEN(description) \ + if (0) { \ + _then(description); \ + } + +// ***************************************************************************** +// Public API Macros +// ***************************************************************************** + +/** @brief Macro to define a test case. + * + * This macro is used to define a test case, which is a single unit of testing + * that verifies a specific functionality or behavior. The test case can be + * executed independently or as part of a test suite. + * + * @param test_name The name of the test case to define. + */ + +#define FOSSIL_TEST(test_name) \ + _FOSSIL_TEST(test_name) + +/** @brief Macro to set a test case's tags. + * + * This macro is used to specify tags for a test case. Tags can be used to + * categorize or filter test cases based on specific criteria. + * + * @param test_name The name of the test case. + * @param tags The tags to assign to the test case. + */ +#define FOSSIL_TEST_SET_TAGS(test_name, tags) \ + _FOSSIL_TEST_SET_TAGS(test_name, tags) + +/** @brief Macro to set a test case's skip message. + * + * This macro is used to specify a skip message for a test case. The skip + * message indicates that the test case is intentionally skipped and provides + * a reason for the skip. + * + * @param test_name The name of the test case. + * @param skip The skip message to assign to the test case. + */ +#define FOSSIL_TEST_SET_SKIP(test_name, skip) \ + _FOSSIL_TEST_SET_SKIP(test_name, skip) + +/** @brief Macro to set a test case's criteria. + * + * This macro is used to specify criteria for a test case. The criteria can be + * used to filter or categorize test cases based on specific conditions or + * requirements. + * + * @param test_name The name of the test case. + * @param criteria The criteria to assign to the test case. + */ +#define FOSSIL_TEST_SET_CRITERIA(test_name, criteria) \ + _FOSSIL_TEST_SET_CRITERIA(test_name, criteria) + +/** @brief Macro to set a test case's setup function. + * + * This macro is used to specify a setup function for a test case. The setup + * function will be called before the test case is executed, allowing for any + * necessary initialization or preparation. + * + * @param test_name The name of the test case. + * @param before The name of the setup function to call before the test case. + */ +#define FOSSIL_TEST_SET_BEFORE(test_name, before) \ + _FOSSIL_TEST_SET_BEFORE(test_name, before) + +/** @brief Macro to set a test case's setup function. + * + * This macro is used to specify a setup function for a test case. The setup + * function will be called before the test case is executed, allowing for any + * necessary initialization or preparation. + * + * @param test_name The name of the test case. + * @param before The name of the setup function to call before the test case. + */ +#define FOSSIL_TEST_SET_AFTER(test_name, after) \ + _FOSSIL_TEST_SET_AFTER(test_name, after) + +/** @brief Macro to define a test suite. + * + * This macro is used to define a test suite, which is a collection of test cases + * that are related to each other. The test suite can be executed as a whole to + * verify the correctness of a group of functionalities. + * + * @param suite_name The name of the suite to define. + */ +#define FOSSIL_SUITE(suite) \ + _FOSSIL_SUITE(suite) + +/** @brief Macro to define a test setup function. + * + * This macro is used to define a setup function for a test case or suite. The + * setup function will be called before the test case or suite is executed, and + * can be used to initialize resources or set up the test environment. + * + * @param test_setup The name of the setup function to define. + */ +#define FOSSIL_SETUP(test_setup) \ + _FOSSIL_SETUP(test_setup) + +/** @brief Macro to define a test teardown function. + * + * This macro is used to define a teardown function for a test case or suite. The + * teardown function will be called after the test case or suite has been executed, and + * can be used to clean up resources or reset the test environment. + * + * @param test_teardown The name of the teardown function to define. + */ +#define FOSSIL_TEARDOWN(test_teardown) \ + _FOSSIL_TEARDOWN(test_teardown) + +/** @brief Macro to define a test setup function. + * + * This macro is used to define a setup function for a test case or suite. The + * setup function will be called before the test case or suite is executed, and + * can be used to initialize resources or set up the test environment. + * + * @param test_setup The name of the setup function to define. + */ +#define FOSSIL_BEFORE(test_setup) \ + _FOSSIL_BEFORE(test_setup) + +/** @brief Macro to define a test teardown function. + * + * This macro is used to define a teardown function for a test case or suite. The + * teardown function will be called after the test case or suite has been executed, and + * can be used to clean up resources or reset the test environment. + * + * @param test_teardown The name of the teardown function to define. + */ +#define FOSSIL_AFTER(test_teardown) \ + _FOSSIL_AFTER(test_teardown) + +/** @brief Macro to add a test case to a specific suite. + * + * This macro is used to add a test case to a specific test suite that has been + * defined in the test engine. It will link the test case to the suite and + * ensure it is executed when the suite is run. + * + * @param suite The name of the suite to which the test case will be added. + * @param test_case The name of the test case to add. + */ +#define FOSSIL_TEST_ADD(suite, test_case) \ + _FOSSIL_TEST_ADD(suite, test_case) + +/** @brief Macro to run a specific test suite. + * + * This macro is used to run a specific test suite that has been defined in the + * test engine. It will execute all test cases within the specified suite and + * print the results. + * + * @param suite The name of the suite to run. + */ +#define FOSSIL_RUN_SUITE(suite) \ + _FOSSIL_RUN_SUITE(suite) + +/** @brief Macro to start the test engine. + * + * This macro is used to initialize and start the test engine. It should be called + * at the beginning of the test execution to set up the environment for running + * tests. + * + * @param argc The number of command line arguments. + * @param argv The command line arguments. + */ +#define FOSSIL_TEST_START(argc, argv) \ + _FOSSIL_TEST_START(argc, argv) + +/** @brief Macro to print a summary of the test results. + * + * This macro is used to print a summary of the test results after all tests have + * been run. It provides an overview of the number of tests passed, failed, and + * skipped. + * + * @param engine The engine instance containing the test results. + */ +#define FOSSIL_SUMMARY() \ + _FOSSIL_SUMMARY() + +/** @brief Macro to run all test suites in the engine. + * + * This macro is used to run all test suites that have been added to the test + * engine. It will execute each suite and its associated test cases, collecting + * results and printing a summary at the end. + * + * @param engine The engine instance containing the test suites. + */ +#define FOSSIL_RUN_ALL() \ + _FOSSIL_RUN_ALL() + +/** @brief Macro to end the test engine. + * + * This macro is used to clean up and end the test engine after all tests have + * been run. It should be called at the end of the test execution. + * + * @param engine The engine instance to end. + */ +#define FOSSIL_END() \ + _FOSSIL_END() + +/** + * @brief Macro to register a test suite with the engine. + * + * This macro is used to register a test suite with the test engine. It will + * ensure that the suite is added to the engine and can be executed when the + * test runner is run. + * + * @param suite The name of the suite to register. + */ +#define FOSSIL_TEST_REGISTER(suite) \ + _FOSSIL_TEST_REGISTER(suite) + +/** + * @brief Macro to define a test group. + * + * This macro is used to define a test group, which is a collection of test cases + * that are related to each other. The test group can be executed as a whole to + * verify the correctness of a group of functionalities. + * + * @param name The name of the test group. + */ +#define FOSSIL_TEST_GROUP(name) \ + _FOSSIL_TEST_GROUP(name) + +/** + * @brief Macro to export a test group. + * + * This macro is used to export a test group from a test file. The test group + * will be available to other test files that import it. + * + * @param name The name of the test group to export. + */ +#define FOSSIL_TEST_EXPORT(name) \ + _FOSSIL_TEST_EXPORT(name) + +/** + * @brief Macro to import a test group. + * + * This macro is used to import a test group into the test runner. The test group + * will be executed when the test runner is run. + * + * @param name The name of the test group to import. + */ +#define FOSSIL_TEST_IMPORT(name) \ + _FOSSIL_TEST_IMPORT(name) + +/** + * @brief Macro to assume a condition in a test runner. + * This macro is used to assert that a specific condition is true within a test + * runner. If the condition is false, the test runner will output the specified + * message and may abort the execution of the test case or test suite. + */ +#define FOSSIL_TEST_ASSUME(condition, message) \ + _FOSSIL_TEST_ASSUME(condition, message) + +/** + * @brief Macro to assert a condition in a test runner. + * This macro is used to assert that a specific condition is true within a test + * runner. If the condition is false, the test runner will output the specified + * message and may abort the execution of the test case or test suite. + */ +#define FOSSIL_TEST_ASSERT(condition, message) \ + _FOSSIL_TEST_ASSERT(condition, message) + +/** + * @brief Macro for defining a Given step in a behavior-driven development test. + * + * This macro is used to define a Given step in a behavior-driven development test. + * The Given step is used to specify the initial context of a test case. + * + * @param description The description of the Given step. + */ +#define GIVEN(description) \ + _GIVEN(description) + +/** + * @brief Macro for defining a When step in a behavior-driven development test. + * + * This macro is used to define a When step in a behavior-driven development test. + * The When step is used to specify the action that is being tested. + * + * @param description The description of the When step. + */ +#define WHEN(description) \ + _WHEN(description) + +/** + * @brief Macro for defining a Then step in a behavior-driven development test. + * + * This macro is used to define a Then step in a behavior-driven development test. + * The Then step is used to specify the expected outcome of a test case. + * + * @param description The description of the Then step. + */ +#define THEN(description) \ + _THEN(description) + +// ********************************************************************************* +// Deprecated Macro Names (to be removed in 2.0.0) changing my mind is futile +// ********************************************************************************* + +// TODO remove these macros in 2.0.0 + +/** + * @brief Deprecated macro to define a test case. + * + * This macro is deprecated and will be removed in version 2.0.0. Use FOSSIL_TEST instead. + * + * @param test_name The name of the test case to define. + */ +#define FOSSIL_TEST_CASE(test_name) \ + FOSSIL_TEST(test_name) + +/** + * @brief Deprecated macro to define a test suite. + * + * This macro is deprecated and will be removed in version 2.0.0. Use FOSSIL_SUITE instead. + * + * @param suite The name of the suite to define. + */ +#define FOSSIL_TEST_SUITE(suite) \ + FOSSIL_SUITE(suite) + +/** + * @brief Deprecated macro to run all test cases. + * + * This macro is deprecated and will be removed in version 2.0.0. Use FOSSIL_RUN_ALL instead. + */ +#define FOSSIL_TEST_RUN() \ + FOSSIL_RUN_ALL() + +/** + * @brief Deprecated macro to print a summary of test results. + * + * This macro is deprecated and will be removed in version 2.0.0. Use FOSSIL_SUMMARY instead. + */ +#define FOSSIL_TEST_SUMMARY() \ + FOSSIL_SUMMARY() + +/** + * @brief Deprecated macro to end the test engine. + * + * This macro is deprecated and will be removed in version 2.0.0. Use FOSSIL_END instead. + */ +#define FOSSIL_TEST_END() \ + FOSSIL_END() + +/** + * @brief Deprecated macro to skip a test case. + * + * This macro is deprecated and will be removed in version 2.0.0. Use FOSSIL_TEST_SET_SKIP instead. + * + * @param test_name The name of the test case to skip. + * @param skip The skip message to assign to the test case. + */ +#define FOSSIL_TEST_SKIP(test_name, skip) \ + FOSSIL_TEST_SET_SKIP(test_name, skip) + +#endif diff --git a/code/logic/fossil/test/framework.h b/code/logic/fossil/test/framework.h deleted file mode 100644 index d4af179a..00000000 --- a/code/logic/fossil/test/framework.h +++ /dev/null @@ -1,507 +0,0 @@ -/* - * ----------------------------------------------------------------------------- - * Project: Fossil Logic - * - * This file is part of the Fossil Logic project, which aims to develop high- - * performance, cross-platform applications and libraries. The code contained - * herein is subject to the terms and conditions defined in the project license. - * - * Author: Michael Gene Brockus (Dreamer) - * Date: 07/01/2024 - * - * Copyright (C) 2024 Fossil Logic. All rights reserved. - * ----------------------------------------------------------------------------- - */ -#ifndef FOSSIL_TEST_FRAMEWORK_H -#define FOSSIL_TEST_FRAMEWORK_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "marking.h" -#include "testing.h" -#include "mocking.h" -#include "assume.h" - -/** - * Macro to define a given step in a test case. - * This macro is used to define a given step in a test case. The given step - * should contain the setup logic required to prepare the environment for the - * test case. - */ -#define GIVEN(description) _GIVEN(description) - -/** - * Macro to define a when step in a test case. - * This macro is used to define a when step in a test case. The when step should - * contain the logic to execute the functionality that is being tested. - */ -#define WHEN(description) _WHEN(description) - -/** - * Macro to define a then step in a test case. - * This macro is used to define a then step in a test case. The then step should - * contain the logic to verify the correctness of the functionality that was - * tested. - */ -#define THEN(description) _THEN(description) - -/** - * Macro to define a test group. - * This macro is used to define a test group, which is a collection of test - * cases that are related to each other. The test group can be executed as a - * whole to verify the correctness of a group of functionalities. - */ -#define FOSSIL_TEST_GROUP(name) \ - _FOSSIL_TEST_GROUP(name) - -/** - * Macro to export a test group. - * This macro is used to export a test group from a test file. The test group - * will be available to other test files that import it. - */ -#define FOSSIL_TEST_EXPORT(name) \ - _FOSSIL_TEST_EXPORT(name) - -/** - * Macro to import a test group. - * This macro is used to import a test group into the test runner. The test group - * will be executed when the test runner is run. - */ -#define FOSSIL_TEST_IMPORT(name) \ - _FOSSIL_TEST_IMPORT(name) - -/** - * Macro to start the test runner. - * This macro is used to start the test runner, which will initialize the test - * framework and prepare to run all test cases in the test suite. - */ -#define FOSSIL_TEST_START(argc, argv) \ - _FOSSIL_TEST_START(argc, argv) - -/** - * Macro to run all test cases in the test suite. - * This macro is used to run all test cases in the test suite. The test cases - * will be executed in sequence, and the results will be output to the console. - */ -#define FOSSIL_TEST_RUN() \ - _FOSSIL_TEST_RUN() - -/** - * Macro to print a summary of the test results. - * This macro is used to print a summary of the test results after all test - * cases have been executed. The summary will include the number of test cases - * that passed, failed, and were skipped. - */ -#define FOSSIL_TEST_SUMMARY() \ - _FOSSIL_TEST_SUMMARY() - -/** - * Macro to end the test runner. - * This macro is used to end the test runner, which will clean up the test - * framework and return the appropriate exit code based on the test results. - */ -#define FOSSIL_TEST_END() \ - _FOSSIL_TEST_END() - -/** - * Macro to define a test case. - * This macro is used to declare a test case function that will be executed - * as part of the test suite. The test case function should contain the logic - * to verify the correctness of a specific functionality. - */ -#define FOSSIL_TEST_ADD(suite, test_case) \ - _FOSSIL_TEST_ADD(suite, test_case) - -/** - * Macro to define a test suite. - * This macro is used to declare a test suite, which is a collection of test - * cases that are related to each other. The test suite can be executed as a - * whole to verify the correctness of a group of functionalities. - */ -#define FOSSIL_TEST_SUITE(suite_name) \ - _FOSSIL_TEST_SUITE(suite_name) - -/** - * Macro to register a test suite with the test framework. - * This macro is used to register a test suite with the test framework. The test - * suite will be added to the list of test suites that will be executed by the - * test runner. - */ -#define FOSSIL_TEST_REGISTER(suite) \ - _FOSSIL_TEST_REGISTER(suite) - -/** - * Macro to define a setup function for a test. - * This macro is used to declare a setup function that will be executed before - * each test case in a test suite. The setup function should contain the logic - * to initialize the environment or state required for the test cases. - */ -#define FOSSIL_SETUP(name) \ - _FOSSIL_TEST_SETUP(name) - -/** - * Macro to define a teardown function for a test. - * This macro is used to declare a teardown function that will be executed after - * each test case in a test suite. The teardown function should contain the logic - * to clean up the environment or state after the test cases have been executed. - */ -#define FOSSIL_TEARDOWN(name) \ - _FOSSIL_TEST_TEARDOWN(name) - -/** - * Macro to define a test case. - * This macro is used to declare a test case function that will be executed - * as part of the test suite. The test case function should contain the logic - * to verify the correctness of a specific functionality. - */ -#define FOSSIL_TEST_CASE(name) \ - _FOSSIL_TEST_CASE(name) - -/** - * Macro to skip a test case. - * This macro is used to skip a test case in the test runner. The test case will - * be marked as skipped, and the specified message will be output to the console. - */ -#define FOSSIL_TEST_SKIP(test_name, message) \ - _FOSSIL_TEST_SKIP(test_name, message) - -/** - * Macro to assume a condition in a test runner. - * This macro is used to assert that a specific condition is true within a test - * runner. If the condition is false, the test runner will output the specified - * message and may abort the execution of the test case or test suite. - */ -#define FOSSIL_TEST_ASSUME(condition, message) \ - _FOSSIL_TEST_ASSUME(condition, message) - -/** - * Macro to assert a condition in a test runner. - * This macro is used to assert that a specific condition is true within a test - * runner. If the condition is false, the test runner will output the specified - * message and abort the execution of the test case or test suite. - */ -#define FOSSIL_TEST_ASSERT(condition, message) \ - _FOSSIL_TEST_ASSUME(condition, message) - - -// ***************************************************************************** -// Mocking framework -// ***************************************************************************** - -/** - * @brief Macro for initializing the mock list. - * - * This macro initializes the mock list by calling the fossil_mock_init function. - * - * @param list The mock list to initialize. - */ -#define MOCK_INIT(list) \ - _MOCK_INIT(list) - -/** - * @brief Macro for destroying the mock list. - * - * This macro destroys the mock list by calling the fossil_mock_destroy function. - * - * @param list The mock list to destroy. - */ -#define MOCK_DESTROY(list) \ - _MOCK_DESTROY(list) - -/** - * @brief Macro for adding a mock function call to the mock list. - * - * This macro adds a mock function call to the mock list by calling the fossil_mock_add_call function. - * - * @param list The mock list to add the call to. - * @param func The mock function to add the call for. - * @param args The arguments of the mock function call. - * @param num_args The number of arguments in the mock function call. - */ -#define MOCK_ADD_CALL(list, func, args, num_args) \ - _MOCK_ADD_CALL(list, func, args, num_args) - -/** - * @brief Macro for printing the mock list. - * - * This macro prints the mock list by calling the fossil_mock_print function. - * - * @param list The mock list to print. - */ -#define MOCK_PRINT(list) \ - _MOCK_PRINT(list) - -/** - * @def FOSSIL_MOCK_FUNC - * @brief Macro for creating a mock function with the specified return type, name, and parameters. - * - * This macro simplifies the creation of mock functions by defining a function with the given return - * type, name, and parameters. The function name will be prefixed with "fossil_mockup_" to clearly indicate - * that it is a mock function. - * - * @param return_type The return type of the mock function. - * @param name The name of the mock function. - * @param ... The parameters of the mock function in the format: (type1 param1, type2 param2, ...). - * @return The return type specified for the mock function. - */ -#define FOSSIL_MOCK_FUNC(return_type, name, ...) \ - _FOSSIL_MOCK_FUNC(return_type, name, __VA_ARGS__) - -/** - * @def FOSSIL_MOCK_ALIAS - * @brief Macro for creating a type alias based on an existing type. - * - * This macro creates a type alias for a given existing type. - * - * @param new_type The name of the new type alias. - * @param existing_type The existing type to create an alias for. - */ -#define FOSSIL_MOCK_ALIAS(new_type, existing_type) \ - _FOSSIL_MOCK_ALIAS(new_type, existing_type) - -/** - * @def FOSSIL_MOCK_STRUCT - * - * @brief Macro for creating a mock struct with the specified name and members. - * - * This macro simplifies the creation of mock structs by defining a struct with the given name - * and members. The struct name will be prefixed with "fossil_mockup_" to clearly indicate that it is a mock struct. - * - * @param name The name of the mock struct. - */ -#define FOSSIL_MOCK_STRUCT(name) \ - _FOSSIL_MOCK_STRUCT(name) - -// ***************************************************************************** -// Benchmark framework -// ***************************************************************************** - -/** - * @brief Define macro for marking a benchmark. - * - * This macro is used to mark a benchmark with a given name. It initializes - * the benchmark structure and sets the name of the benchmark. - * - * @param name The name of the benchmark. - */ -#define MARK_BENCHMARK(name) \ - _MARK_BENCHMARK(name) - -/** - * @brief Define macro for starting a benchmark. - * - * This macro is used to start a benchmark with a given name. It starts the - * timer for the benchmark. - * - * @param name The name of the benchmark. - */ -#define MARK_START(name) \ - _MARK_START(name) - -/** - * @brief Define macro for stopping a benchmark. - * - * This macro is used to stop a benchmark with a given name. It stops the - * timer for the benchmark. - * - * @param name The name of the benchmark. - */ -#define MARK_STOP(name) \ - _MARK_STOP(name) - -/** - * @brief Define macro for reporting a benchmark. - * - * This macro is used to report the results of a benchmark with a given name. - * It prints the benchmark name and the elapsed time. - * - * @param name The name of the benchmark. - */ -#define MARK_REPORT(name) \ - _MARK_REPORT(name) - -/** - * @brief Define macro for scoped benchmarking. - * - * This macro is used to create a scoped benchmark with a given name. It - * initializes the scoped benchmark structure and sets the benchmark to be - * used. - * - * @param name The name of the benchmark. - */ -#define MARK_SCOPED(name) \ - _MARK_SCOPED(name) - -// ================================================================= -// Bench specific commands -// ================================================================= - -/** - * @brief Define macro for starting a benchmark. - * - * This macro is used to mark the start of a benchmark. It typically initializes - * any necessary resources or variables required for benchmarking. - */ -#define TEST_BENCHMARK() \ - _TEST_BENCHMARK() - -/** - * @brief Define macro for getting the current time. - * - * This macro is used to retrieve the current time, which is typically used - * in conjunction with TEST_BENCHMARK to calculate the elapsed time for a benchmark. - */ -#define TEST_CURRENT_TIME() \ - _TEST_CURRENT_TIME() - -/** - * @brief Define macro for reporting test duration with a given timeout. - * - * This macro is used to report the duration of a test with a given timeout. - * It takes the timeout duration, elapsed time, and actual duration as arguments - * and reports the results, typically in the form of logs or console output. - * - * @param duration The duration unit (e.g., "minutes", "seconds"). - * @param elapsed The elapsed time since the benchmark started. - * @param actual The actual duration of the test. - */ -#define TEST_DURATION(duration, elapsed, actual) \ - _TEST_DURATION(duration, elapsed, actual) - -/** - * @brief Define macro for reporting test duration in minutes. - * - * This macro is a shorthand for reporting test duration in minutes using TEST_DURATION. - * It takes the elapsed time and actual duration as arguments and reports the results - * in minutes. - * - * @param elapsed The elapsed time since the benchmark started. - * @param actual The actual duration of the test. - */ -#define TEST_DURATION_MIN(elapsed, actual) \ - _TEST_DURATION_MIN(elapsed, actual) - -/** - * @brief Define macro for reporting test duration in seconds. - * - * This macro is a shorthand for reporting test duration in seconds using TEST_DURATION. - * It takes the elapsed time and actual duration as arguments and reports the results - * in seconds. - * - * @param elapsed The elapsed time since the benchmark started. - * @param actual The actual duration of the test. - */ -#define TEST_DURATION_SEC(elapsed, actual) \ - _TEST_DURATION_SEC(elapsed, actual) - -/** - * @brief Define macro for reporting test duration in milliseconds. - * - * This macro is a shorthand for reporting test duration in milliseconds using TEST_DURATION. - * It takes the elapsed time and actual duration as arguments and reports the results - * in milliseconds. - * - * @param elapsed The elapsed time since the benchmark started. - * @param actual The actual duration of the test. - */ -#define TEST_DURATION_MIL(elapsed, actual) \ - _TEST_DURATION_MIL(elapsed, actual) - -/** - * @brief Define macro for reporting test duration in microseconds. - * - * This macro is a shorthand for reporting test duration in microseconds using TEST_DURATION. - * It takes the elapsed time and actual duration as arguments and reports the results - * in microseconds. - * - * @param elapsed The elapsed time since the benchmark started. - * @param actual The actual duration of the test. - */ -#define TEST_DURATION_MIC(elapsed, actual) \ - _TEST_DURATION_MIC(elapsed, actual) - -/** - * @brief Define macro for reporting test duration in nanoseconds. - * - * This macro is a shorthand for reporting test duration in nanoseconds using TEST_DURATION. - * It takes the elapsed time and actual duration as arguments and reports the results - * in nanoseconds. - * - * @param elapsed The elapsed time since the benchmark started. - * @param actual The actual duration of the test. - */ -#define TEST_DURATION_NAN(elapsed, actual) \ - _TEST_DURATION_NAN(elapsed, actual) - -/** - * @brief Define macro for reporting test duration in picoseconds. - * - * This macro is a shorthand for reporting test duration in picoseconds using TEST_DURATION. - * It takes the elapsed time and actual duration as arguments and reports the results - * in picoseconds. - * - * @param elapsed The elapsed time since the benchmark started. - * @param actual The actual duration of the test. - */ -#define TEST_DURATION_PIC(elapsed, actual) \ - _TEST_DURATION_PIC(elapsed, actual) - -/** - * @brief Define macro for reporting test duration in femtoseconds. - * - * This macro is a shorthand for reporting test duration in femtoseconds using TEST_DURATION. - * It takes the elapsed time and actual duration as arguments and reports the results - * in femtoseconds. - * - * @param elapsed The elapsed time since the benchmark started. - * @param actual The actual duration of the test. - */ -#define TEST_DURATION_FEM(elapsed, actual) \ - _TEST_DURATION_FEM(elapsed, actual) - -/** - * @brief Define macro for reporting test duration in attoseconds. - * - * This macro is a shorthand for reporting test duration in attoseconds using TEST_DURATION. - * It takes the elapsed time and actual duration as arguments and reports the results - * in attoseconds. - * - * @param elapsed The elapsed time since the benchmark started. - * @param actual The actual duration of the test. - */ -#define TEST_DURATION_ATT(elapsed, actual) \ - _TEST_DURATION_ATT(elapsed, actual) - -/** - * @brief Define macro for reporting test duration in zeptoseconds. - * - * This macro is a shorthand for reporting test duration in zeptoseconds using TEST_DURATION. - * It takes the elapsed time and actual duration as arguments and reports the results - * in zeptoseconds. - * - * @param elapsed The elapsed time since the benchmark started. - * @param actual The actual duration of the test. - */ -#define TEST_DURATION_ZEP(elapsed, actual) \ - _TEST_DURATION_ZEP(elapsed, actual) - -/** - * @brief Define macro for reporting test duration in yoctoseconds. - * - * This macro is a shorthand for reporting test duration in yoctoseconds using TEST_DURATION. - * It takes the elapsed time and actual duration as arguments and reports the results - * in yoctoseconds. - * - * @param elapsed The elapsed time since the benchmark started. - * @param actual The actual duration of the test. - */ -#define TEST_DURATION_YOC(elapsed, actual) \ - _TEST_DURATION_YOC(elapsed, actual) - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/code/logic/fossil/test/internal.h b/code/logic/fossil/test/internal.h deleted file mode 100644 index a7dad9e0..00000000 --- a/code/logic/fossil/test/internal.h +++ /dev/null @@ -1,164 +0,0 @@ -/* - * ----------------------------------------------------------------------------- - * Project: Fossil Logic - * - * This file is part of the Fossil Logic project, which aims to develop high- - * performance, cross-platform applications and libraries. The code contained - * herein is subject to the terms and conditions defined in the project license. - * - * Author: Michael Gene Brockus (Dreamer) - * Date: 07/01/2024 - * - * Copyright (C) 2024 Fossil Logic. All rights reserved. - * ----------------------------------------------------------------------------- - */ -#ifndef FOSSIL_TEST_CORE_H -#define FOSSIL_TEST_CORE_H - -#define MAX_NAME_LENGTH 256 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Define color codes for output -#define FOSSIL_TEST_COLOR_RESET "\033[0m" -#define FOSSIL_TEST_COLOR_RED "\033[31m" -#define FOSSIL_TEST_COLOR_GREEN "\033[32m" -#define FOSSIL_TEST_COLOR_YELLOW "\033[33m" -#define FOSSIL_TEST_COLOR_BLUE "\033[34m" -#define FOSSIL_TEST_COLOR_MAGENTA "\033[35m" -#define FOSSIL_TEST_COLOR_CYAN "\033[36m" -#define FOSSIL_TEST_COLOR_WHITE "\033[37m" - -// Define text attributes -#define FOSSIL_TEST_ATTR_BOLD "\033[1m" -#define FOSSIL_TEST_ATTR_UNDERLINE "\033[4m" -#define FOSSIL_TEST_ATTR_REVERSED "\033[7m" -#define FOSSIL_TEST_ATTR_BLINK "\033[5m" -#define FOSSIL_TEST_ATTR_HIDDEN "\033[8m" -#define FOSSIL_TEST_ATTR_NORMAL "\033[22m" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - FOSSIL_TEST_FORMAT_PLAIN, - FOSSIL_TEST_FORMAT_CI, - FOSSIL_TEST_FORMAT_JELLYFISH -} fossil_test_format_t; - -typedef enum { - FOSSIL_TEST_SUMMARY_PLAIN, - FOSSIL_TEST_SUMMARY_CI, - FOSSIL_TEST_SUMMARY_JELLYFISH -} fossil_test_summary_t; - -/** - * @struct fossil_test_options_t - * @brief Structure to hold various options for fossil testing. - * - * This structure contains various flags and parameters that control the behavior of the fossil testing framework. - * - * @var fossil_test_options_t::show_version - * Flag to indicate if the version information should be displayed. - * - * @var fossil_test_options_t::show_help - * Flag to indicate if the help information should be displayed. - * - * @var fossil_test_options_t::show_info - * Flag to indicate if additional information should be displayed. - * - * @var fossil_test_options_t::reverse - * Flag to indicate if the order of tests should be reversed. - * - * @var fossil_test_options_t::repeat_enabled - * Flag to indicate if test repetition is enabled. - * - * @var fossil_test_options_t::repeat_count - * Number of times to repeat the tests if repetition is enabled. - * - * @var fossil_test_options_t::shuffle_enabled - * Flag to indicate if the tests should be shuffled. - * - * @var fossil_test_options_t::dry_run - * Flag to indicate if the tests should be run in dry-run mode (no actual execution). - * - * @var fossil_test_options_t::fail_fast - * Flag to enable fail-fast behavior, stopping test execution after the first failure. - * - * @var fossil_test_options_t::quiet - * Flag to suppress most non-essential output for minimal console logging. - * - * @var fossil_test_options_t::color_output - * Flag to enable or disable colorized output in the console. - */ -typedef struct { - bool show_version; - bool show_help; - bool show_info; - bool reverse; - bool repeat_enabled; - int32_t repeat_count; - bool shuffle_enabled; - bool dry_run; - bool color_output; - fossil_test_summary_t summary; // Replaces 'quiet' - fossil_test_format_t format; // Store the format type -} fossil_test_options_t; - -/** - * Prints a string to the output. - * - * @param str The string to be printed. - */ -void internal_test_puts(const char *str); - -/** - * Prints a formatted string to the output. - * - * @param format The format string. - * @param ... The additional arguments to be formatted. - */ -void internal_test_printf(const char *format, ...); - -/** - * Prints a string to the output with a specified color. - * - * @param color The color code to be applied. - * @param format The format string. - * @param ... The additional arguments to be formatted. - */ -void internal_test_print_color(const char *color, const char *format, ...); - -/** - * Prints a character to the output. - * - * @param c The character to be printed. - */ -void internal_test_putchar(char c); - -/** - * Prints a character to the output with a specified color. - * - * @param c The character to be printed. - * @param color The color code to be applied. - */ -void internal_test_putchar_color(char c, const char *color); - -// Set global color output flag -void internal_test_set_color_output(bool enabled); - -#ifdef __cplusplus -} -#endif - -#endif // FOSSIL_TEST_CORE_H diff --git a/code/logic/fossil/test/testing.h b/code/logic/fossil/test/testing.h deleted file mode 100644 index 6fd8fcdd..00000000 --- a/code/logic/fossil/test/testing.h +++ /dev/null @@ -1,547 +0,0 @@ -/* - * ----------------------------------------------------------------------------- - * Project: Fossil Logic - * - * This file is part of the Fossil Logic project, which aims to develop high- - * performance, cross-platform applications and libraries. The code contained - * herein is subject to the terms and conditions defined in the project license. - * - * Author: Michael Gene Brockus (Dreamer) - * Date: 07/01/2024 - * - * Copyright (C) 2024 Fossil Logic. All rights reserved. - * ----------------------------------------------------------------------------- - */ -#ifndef FOSSIL_TEST_INTERNAL_H -#define FOSSIL_TEST_INTERNAL_H - -#include "internal.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @enum test_status - * @brief Enumeration to represent the status of a test. - * - * This enumeration defines the possible status values for a test, including - * pass, fail, skip, empty, and timeout. - */ -typedef enum { - TEST_STATUS_PASS, - TEST_STATUS_FAIL, - TEST_STATUS_SKIP, - TEST_STATUS_EMPTY, - TEST_STATUS_TTIMEOUT -} fossil_test_status_t; - -/** - * @struct test_case - * @brief Structure to hold a test case. - * - * This structure contains fields to hold information about a test case, including - * the name, test function, setup function, teardown function, status, failure message, - * execution time, and a pointer to the next test case. - * - * @var test_case::name - * Test case name - * - * @var test_case::test_func - * Pointer to test function - * - * @var test_case::setup_func - * Pointer to setup function (optional) - * - * @var test_case::teardown_func - * Pointer to teardown function (optional) - * - * @var test_case::status - * Test status (pass, fail, skip) - * - * @var test_case::failure_message - * Failure message (if any) - * - * @var test_case::execution_time - * Execution time of the test - * - * @var test_case::next - * Pointer to next test case in the list - */ -typedef struct test_case { - const char *name; - void (*test_func)(void); - void (*setup_func)(void); - void (*teardown_func)(void); - fossil_test_status_t status; - const char *failure_message; - double execution_time; - struct test_case *next; -} fossil_test_case_t; - -/** - * @struct fossil_test_suite_t - * @brief Structure to hold a test suite. - * - * This structure contains fields to hold information about a test suite, including - * the name, suite setup function, suite teardown function, total execution time, - * list of test cases, and a pointer to the next test suite. - * - * @var fossil_test_suite_t::name - * Suite name - * - * @var fossil_test_suite_t::suite_setup_func - * Pointer to suite setup function (optional) - * - * @var fossil_test_suite_t::suite_teardown_func - * Pointer to suite teardown function (optional) - * - * @var fossil_test_suite_t::total_execution_time - * Total execution time of all test cases - * - * @var fossil_test_suite_t::tests - * List of test cases - * - * @var fossil_test_suite_t::next - * Pointer to next suite in the list - */ -typedef struct fossil_test_suite_t { - const char *name; - void (*suite_setup_func)(void); - void (*suite_teardown_func)(void); - double total_execution_time; - fossil_test_case_t *tests; - struct fossil_test_suite_t *next; -} fossil_test_suite_t; - -/** - * @struct fossil_test_env - * @brief Structure to hold the environment for fossil tests. - * - * This structure contains various fields to manage and track the state of - * test execution, including options, counts of different test outcomes, - * execution times, and a list of test suites. - * - * @var fossil_test_env::options - * Configuration options for the fossil test environment. - * - * @var fossil_test_env::env - * Environment buffer for handling non-local jumps (e.g., setjmp/longjmp). - * - * @var fossil_test_env::total_tests - * Total number of tests to be executed. - * - * @var fossil_test_env::pass_count - * Count of tests that have passed. - * - * @var fossil_test_env::fail_count - * Count of tests that have failed. - * - * @var fossil_test_env::skip_count - * Count of tests that have been skipped. - * - * @var fossil_test_env::empty_count - * Count of tests that are empty (i.e., no test cases). - * - * @var fossil_test_env::timeout_count - * Count of tests that have timed out. - * - * @var fossil_test_env::unexpected_count - * Count of tests that have encountered unexpected errors. - * - * @var fossil_test_env::start_execution_time - * Timestamp marking the start of test execution. - * - * @var fossil_test_env::end_execution_time - * Timestamp marking the end of test execution. - * - * @var fossil_test_env::test_suites - * Pointer to the list of test suites to be executed. - */ -typedef struct fossil_test_env { - fossil_test_options_t options; - jmp_buf env; - int32_t total_tests; - int32_t pass_count; - int32_t fail_count; - int32_t skip_count; - int32_t empty_count; - int32_t timeout_count; - int32_t unexpected_count; - double start_execution_time; - double end_execution_time; - fossil_test_suite_t *test_suites; -} fossil_test_env_t; - -// ***************************************************************************** -// Function declarations -// ***************************************************************************** - -/** - * @brief Creates a new test suite. - * - * @param name The name of the test suite. - * @return A pointer to the created test suite. - */ -fossil_test_suite_t* fossil_test_create_suite(const char *name); - -/** - * @brief Registers a test suite with the test environment. - * - * @param env The test environment. - * @param suite The test suite to register. - */ -void fossil_test_register_suite(fossil_test_env_t *env, fossil_test_suite_t *suite); - -/** - * @brief Adds a test case to a test suite. - * - * @param suite The test suite. - * @param test_case The test case to add. - */ -void fossil_test_add_case(fossil_test_suite_t *suite, fossil_test_case_t *test_case); - -/** - * @brief Removes a test case from a test suite. - * - * @param suite The test suite. - * @param test_case The test case to remove. - */ -void fossil_test_remove_case(fossil_test_suite_t *suite, fossil_test_case_t *test_case); - -/** - * @brief Sets up a test case. - * - * @param test_case The test case to set up. - */ -void fossil_test_case_setup(fossil_test_case_t *test_case); - -/** - * @brief Tears down a test case. - * - * @param test_case The test case to tear down. - */ -void fossil_fossil_test_case_teardown(fossil_test_case_t *test_case); - -/** - * @brief Runs a test case. - * - * @param test_case The test case to run. - * @param env The test environment. - */ -void fossil_test_run_case(fossil_test_case_t *test_case, fossil_test_env_t *env); - -/** - * @brief Runs all test cases in a test suite. - * - * @param suite The test suite to run. - * @param env The test environment. - */ -void fossil_test_run_suite(fossil_test_suite_t *suite, fossil_test_env_t *env); - -/** - * @brief Internal function to handle assertions with anomaly detection. - * - * This function is used internally by the test framework to handle assertions - * and detect duplicate assertions. It is not intended to be called directly. - * - * @param condition The condition to check. - * @param message The message to display if the condition is false. - * @param file The file name where the assertion occurred. - * @param line The line number where the assertion occurred. - * @param func The function name where the assertion occurred. - */ -void fossil_test_assert_internal(bool condition, const char *message, const char *file, int line, const char *func); - -/** - * @brief Initializes the test environment. - * - * @param env The test environment to initialize. - */ -void fossil_test_init(fossil_test_env_t *env, int argc, char **argv); - -/** - * @brief Prints a summary of the test results. - * - * @param env The test environment. - */ -void fossil_test_summary(fossil_test_env_t *env); - -/** - * @brief Runs all test suites and test cases in the test environment. - * - * @param env The test environment. - */ -void fossil_test_run_all(fossil_test_env_t *env); - -// ***************************************************************************** -// Macro definitions -// ***************************************************************************** - -/** - * @brief Macro to assume a condition in a test runner. - * This macro is used to assert that a specific condition is true within a test - * runner. If the condition is false, the test runner will output the specified - * message and may abort the execution of the test case or test suite. - */ -#define _FOSSIL_TEST_ASSUME(condition, message) \ - fossil_test_assert_internal((condition), (message), __FILE__, __LINE__, __func__) - -/** - * @brief Macro to fail a test case. - * - * This macro is used to fail a test case with a specific message. The test case - * will be marked as failed, and the message will be displayed in the test results. - */ -#define _FOSSIL_TEST_SKIP(test_name, message) \ - test_name##_test_case.status = TEST_STATUS_SKIP; \ - test_name##_test_case.failure_message = message; - -/** - * @brief Macro to define a test case. - * - * This macro is used to define a test case, which is a single unit of testing - * that verifies the correctness of a specific functionality. The test case - * should contain the logic to set up the environment, execute the functionality, - * and verify the results. - * - * @param test_name The name of the test case. - */ -#ifdef __cplusplus -#define _FOSSIL_TEST_CASE(test_name) \ - void test_name##_test_func(void); \ - fossil_test_case_t test_name##_test_case = { \ - #test_name, \ - test_name##_test_func, \ - nullptr, \ - nullptr, \ - TEST_STATUS_PASS, \ - nullptr, \ - 0.0, \ - nullptr \ - }; \ - void test_name##_test_func(void) -#else -#define _FOSSIL_TEST_CASE(test_name) \ - void test_name##_test_func(void); \ - fossil_test_case_t test_name##_test_case = { \ - .name = #test_name, \ - .test_func = test_name##_test_func, \ - .setup_func = NULL, \ - .teardown_func = NULL, \ - .status = TEST_STATUS_PASS, \ - .failure_message = NULL, \ - .execution_time = 0.0, \ - .next = NULL \ - }; \ - void test_name##_test_func(void) -#endif - -/** - * @brief Macro to define a test suite. - * - * This macro is used to define a test suite, which is a collection of test cases - * that are related to each other. The test suite can be executed as a whole to - * verify the correctness of a group of functionalities. - * - * @param suite_name The name of the test suite. - */ -#ifdef __cplusplus -#define _FOSSIL_TEST_SUITE(suite_name) \ - void suite_name##_setup_func(void); \ - void suite_name##_teardown_func(void); \ - fossil_test_suite_t suite_name = { \ - #suite_name, \ - suite_name##_setup_func, \ - suite_name##_teardown_func, \ - 0.0, \ - nullptr, \ - nullptr \ - } -#else -#define _FOSSIL_TEST_SUITE(suite_name) \ - void suite_name##_setup_func(void); \ - void suite_name##_teardown_func(void); \ - fossil_test_suite_t suite_name = { \ - .name = #suite_name, \ - .suite_setup_func = suite_name##_setup_func, \ - .suite_teardown_func = suite_name##_teardown_func, \ - .total_execution_time = 0.0, \ - .tests = NULL, \ - .next = NULL \ - } -#endif - -/** - * @brief Macro to define a setup function for a test. - * - * This macro is used to declare a setup function that will be executed before - * each test case in a test suite. The setup function should contain the logic - * to initialize the environment or state required for the test cases. - * - * @param name The name of the setup function. - */ -#define _FOSSIL_TEST_SETUP(name) \ - void name##_setup_func(void) - -/** - * @brief Macro to define a teardown function for a test. - * - * This macro is used to declare a teardown function that will be executed after - * each test case in a test suite. The teardown function should contain the logic - * to clean up the environment or state after the test cases have been executed. - * - * @param name The name of the teardown function. - */ -#define _FOSSIL_TEST_TEARDOWN(name) \ - void name##_teardown_func(void) - -/** - * @brief Macro to register a test suite with the test framework. - * - * This macro is used to register a test suite with the test framework. The test - * suite will be added to the list of test suites that will be executed by the - * test runner. - * - * @param suite The test suite to register. - */ -#define _FOSSIL_TEST_REGISTER(suite) \ - fossil_test_register_suite(_env, &suite) - -/** - * @brief Macro to add a test case to a test suite. - * - * This macro is used to add a test case to a test suite. The test case will be - * executed when the test suite is run. - * - * @param suite The test suite to add the test case to. - * @param test The test case to add. - */ -#define _FOSSIL_TEST_ADD(suite, test) \ - fossil_test_add_case(&suite, &(test##_test_case)) - -/** - * @brief Macro to define a test group. - * - * This macro is used to define a test group, which is a collection of test cases - * that are related to each other. The test group can be executed as a whole to - * verify the correctness of a group of functionalities. - * - * @param name The name of the test group. - */ -#ifdef __cplusplus -#define _FOSSIL_TEST_GROUP(name) \ - extern "C" void name##_test_group(fossil_test_env_t *_env) -#else -#define _FOSSIL_TEST_GROUP(name) \ - void name##_test_group(fossil_test_env_t *_env) -#endif - -/** - * @brief Macro to export a test group. - * - * This macro is used to export a test group from a test file. The test group - * will be available to other test files that import it. - * - * @param name The name of the test group to export. - */ -#define _FOSSIL_TEST_EXPORT(name) \ - void name##_test_group(fossil_test_env_t *_env) - -/** - * @brief Macro to import a test group. - * - * This macro is used to import a test group into the test runner. The test group - * will be executed when the test runner is run. - * - * @param name The name of the test group to import. - */ -#define _FOSSIL_TEST_IMPORT(name) \ - name##_test_group(&_env) - -// Main runner management macros - -/** - * @brief Macro to start the test runner. - * - * This macro is used to start the test runner, which will initialize the test - * environment and set up the necessary structures for running the test cases. - */ -#define _FOSSIL_TEST_START(argc, argv) \ - fossil_test_env_t _env; \ - fossil_test_init(&_env, argc, argv) - -/** - * @brief Macro to run all test cases in the test suite. - * - * This macro is used to run all test cases in the test suite. The test cases - * will be executed in the order they were added to the suite. - */ -#define _FOSSIL_TEST_RUN() \ - fossil_test_run_all(&_env) - -/** - * @brief Macro to print the test summary. - * - * This macro is used to print the test summary, which includes the number of - * tests that passed, failed, and were skipped. - */ -#define _FOSSIL_TEST_SUMMARY() \ - fossil_test_summary(&_env) - -/** - * @brief Macro to end the test runner. - * - * This macro is used to end the test runner, which will clean up the test - * framework and return the appropriate exit code based on the test results. - */ -#define _FOSSIL_TEST_END() \ - int fail_count = _env.fail_count; \ - return fail_count > 0 ? EXIT_FAILURE : EXIT_SUCCESS - -// Behavior-driven development macros for Given, When, Then structure - -/** - * @brief Macro for defining a Given step in a behavior-driven development test. - * - * This macro is used to define a Given step in a behavior-driven development test. - * The Given step is used to specify the initial context of a test case. - * - * @param description The description of the Given step. - */ -#define _GIVEN(description) \ - if (0) { \ - printf(FOSSIL_TEST_COLOR_MAGENTA "Given %s\n" FOSSIL_TEST_COLOR_RESET, description); \ - } - -/** - * @brief Macro for defining a When step in a behavior-driven development test. - * - * This macro is used to define a When step in a behavior-driven development test. - * The When step is used to specify the action that is being tested. - * - * @param description The description of the When step. - */ -#define _WHEN(description) \ - if (0) { \ - printf(FOSSIL_TEST_COLOR_MAGENTA "When %s\n" FOSSIL_TEST_COLOR_RESET, description); \ - } - -/** - * @brief Macro for defining a Then step in a behavior-driven development test. - * - * This macro is used to define a Then step in a behavior-driven development test. - * The Then step is used to specify the expected outcome of a test case. - * - * @param description The description of the Then step. - */ -#define _THEN(description) \ - if (0) { \ - printf(FOSSIL_TEST_COLOR_MAGENTA "Then %s\n" FOSSIL_TEST_COLOR_RESET, description); \ - } - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/code/logic/internal.c b/code/logic/internal.c deleted file mode 100644 index 2dcec572..00000000 --- a/code/logic/internal.c +++ /dev/null @@ -1,172 +0,0 @@ -/* - * ----------------------------------------------------------------------------- - * Project: Fossil Logic - * - * This file is part of the Fossil Logic project, which aims to develop high- - * performance, cross-platform applications and libraries. The code contained - * herein is subject to the terms and conditions defined in the project license. - * - * Author: Michael Gene Brockus (Dreamer) - * Date: 07/01/2024 - * - * Copyright (C) 2024 Fossil Logic. All rights reserved. - * ----------------------------------------------------------------------------- - */ -#include "fossil/test/internal.h" - -#define FOSSIL_TEST_BUFFER_SIZE 1000 - -static bool internal_test_color_enabled = true; - -// Function to apply color -void internal_test_apply_color(const char *color) { - if (strcmp(color, "red") == 0) { - printf(FOSSIL_TEST_COLOR_RED); - } else if (strcmp(color, "green") == 0) { - printf(FOSSIL_TEST_COLOR_GREEN); - } else if (strcmp(color, "yellow") == 0) { - printf(FOSSIL_TEST_COLOR_YELLOW); - } else if (strcmp(color, "blue") == 0) { - printf(FOSSIL_TEST_COLOR_BLUE); - } else if (strcmp(color, "magenta") == 0) { - printf(FOSSIL_TEST_COLOR_MAGENTA); - } else if (strcmp(color, "cyan") == 0) { - printf(FOSSIL_TEST_COLOR_CYAN); - } else if (strcmp(color, "white") == 0) { - printf(FOSSIL_TEST_COLOR_WHITE); - } -} - -// Function to apply text attributes (e.g., bold, underline) -void internal_test_apply_attribute(const char *attribute) { - if (strcmp(attribute, "bold") == 0) { - printf(FOSSIL_TEST_ATTR_BOLD); - } else if (strcmp(attribute, "underline") == 0) { - printf(FOSSIL_TEST_ATTR_UNDERLINE); - } else if (strcmp(attribute, "reset") == 0) { - printf(FOSSIL_TEST_COLOR_RESET); - } else if (strcmp(attribute, "normal") == 0) { - printf(FOSSIL_TEST_ATTR_NORMAL); - } else if (strcmp(attribute, "reversed") == 0) { - printf(FOSSIL_TEST_ATTR_REVERSED); - } else if (strcmp(attribute, "blink") == 0) { - printf(FOSSIL_TEST_ATTR_BLINK); - } else if (strcmp(attribute, "hidden") == 0) { - printf(FOSSIL_TEST_ATTR_HIDDEN); - } -} - -// Function to print text with attributes, colors, and format specifiers -void internal_test_print_with_attributes(const char *format, ...) { - va_list args; - va_start(args, format); - - char buffer[FOSSIL_TEST_BUFFER_SIZE]; - vsnprintf(buffer, sizeof(buffer), format, args); - - const char *current_pos = buffer; - const char *start = NULL; - const char *end = NULL; - - while ((start = strchr(current_pos, '{')) != NULL) { - printf("%.*s", (int)(start - current_pos), current_pos); - end = strchr(start, '}'); - if (end) { - size_t length = end - start - 1; - char attributes[length + 1]; - strncpy(attributes, start + 1, length); - attributes[length] = '\0'; - - char *color = NULL; - char *attribute = NULL; - char *comma_pos = strchr(attributes, ','); - if (comma_pos) { - *comma_pos = '\0'; - color = attributes; - attribute = comma_pos + 1; - } else { - color = attributes; - } - - if (color) internal_test_apply_color(color); - if (attribute) internal_test_apply_attribute(attribute); - - current_pos = end + 1; - } else { - break; - } - } - - printf("%s", current_pos); - va_end(args); -} - -// Setter to initialize color flag from options -void internal_test_set_color_output(bool enabled) { - internal_test_color_enabled = enabled; -} - -// Internal utility function for color printing -void internal_test_print_color(const char *color, const char *format, ...) { - va_list args; - va_start(args, format); - if (internal_test_color_enabled) { - printf("%s", color); - vprintf(format, args); - printf("%s", FOSSIL_TEST_COLOR_RESET); - } else { - vprintf(format, args); - } - va_end(args); -} - -// Function to print a sanitized string with attributes inside {} -void internal_test_puts(const char *str) { - if (str != NULL) { - char sanitized_str[FOSSIL_TEST_BUFFER_SIZE]; - strncpy(sanitized_str, str, sizeof(sanitized_str)); - sanitized_str[sizeof(sanitized_str) - 1] = '\0'; - internal_test_print_with_attributes(sanitized_str); - } else { - fputs("NULL\n", stderr); - } -} - -// Function to print a single character -void internal_test_putchar(char c) { - putchar(c); -} - -// Function to print a single character in color -void internal_test_putchar_color(char c, const char *color) { - if (internal_test_color_enabled) { - printf("%s%c%s", color, c, FOSSIL_TEST_COLOR_RESET); - } else { - putchar(c); - } -} - -// Function to print sanitized formatted output with attributes -void internal_test_printf(const char *format, ...) { - va_list args; - va_start(args, format); - char buffer[FOSSIL_TEST_BUFFER_SIZE]; - vsnprintf(buffer, sizeof(buffer), format, args); - - if (internal_test_color_enabled) { - internal_test_print_with_attributes(buffer); - } else { - // Strip color tags before printing - for (char *p = buffer; *p;) { - if (*p == '{') { - // Skip until closing '}' - while (*p && *p != '}') p++; - if (*p == '}') p++; - } else { - putchar(*p++); - } - } - } - - va_end(args); -} diff --git a/code/logic/marking.c b/code/logic/mark.c similarity index 62% rename from code/logic/marking.c rename to code/logic/mark.c index aca3d130..c385c2fe 100644 --- a/code/logic/marking.c +++ b/code/logic/mark.c @@ -1,62 +1,42 @@ /* * ----------------------------------------------------------------------------- - * File: framework.hpp * Project: Fossil Logic - * Description: This file implments the framework for benchmarking. - * + * * This file is part of the Fossil Logic project, which aims to develop high- * performance, cross-platform applications and libraries. The code contained * herein is subject to the terms and conditions defined in the project license. - * + * * Author: Michael Gene Brockus (Dreamer) - * Date: 07/01/2024 - * - * Copyright (C) 2024 Fossil Logic. All rights reserved. + * Date: 04/05/2014 + * + * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. * ----------------------------------------------------------------------------- */ -#include "fossil/test/marking.h" -#include -#include -#include -#include -#include -#include +#include "fossil/pizza/mark.h" +#include "fossil/pizza/common.h" #if defined(_WIN32) -#include static LARGE_INTEGER frequency; static LARGE_INTEGER start_time; #elif defined(__APPLE__) -#include clock_t start_time; #else -#include -#include clock_t start_time; #endif -#ifndef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 199309L +#ifndef CLOCK_MONOTONIC +#define CLOCK_MONOTONIC 1 #endif -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - - void fossil_test_start_benchmark(void) { #if defined(_WIN32) QueryPerformanceFrequency(&frequency); QueryPerformanceCounter(&start_time); #elif defined(__APPLE__) start_time = mach_absolute_time(); -#elif defined(_POSIX_VERSION) - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - start_time = ts.tv_sec * 1e9 + ts.tv_nsec; #else struct timeval tv; - gettimeofday(&tv, NULL); + gettimeofday(&tv, null); start_time = (uint64_t)tv.tv_sec * 1e6 + tv.tv_usec; #endif } @@ -71,13 +51,9 @@ uint64_t fossil_test_stop_benchmark(void) { mach_timebase_info_data_t timebase; mach_timebase_info(&timebase); return (end_time - start_time) * timebase.numer / timebase.denom; -#elif defined(_POSIX_VERSION) - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return (ts.tv_sec * 1e9 + ts.tv_nsec) - start_time; #else struct timeval tv; - gettimeofday(&tv, NULL); + gettimeofday(&tv, null); uint64_t end_time = (uint64_t)tv.tv_sec * 1e6 + tv.tv_usec; return (end_time - start_time) * 1e3; #endif @@ -88,36 +64,36 @@ void assume_duration(double expected, double actual, double unit) { double elapsed_seconds = elapsed_time / (1e9 / unit); // Convert to the desired time unit if (elapsed_seconds < expected) { - printf("Benchmark failed: expected %f, got %f\n", expected, actual); + pizza_io_printf("Benchmark failed: expected %f, got %f\n", expected, actual); } } // Marks a test case as timeout with a specified time and prints it to stderr. void fossil_test_benchmark(char* duration_type, double expected, double actual) { - if (duration_type == NULL) { - printf("Error: duration_type is NULL\n"); + if (duration_type == null) { + pizza_io_printf("Error: duration_type is null\n"); return; } - if (strcmp(duration_type, "minutes") == 0) { + if (pizza_io_cstr_compare(duration_type, "minutes") == 0) { assume_duration(expected, actual, 60.0); - } else if (strcmp(duration_type, "seconds") == 0) { + } else if (pizza_io_cstr_compare(duration_type, "seconds") == 0) { assume_duration(expected, actual, 1.0); - } else if (strcmp(duration_type, "milliseconds") == 0) { + } else if (pizza_io_cstr_compare(duration_type, "milliseconds") == 0) { assume_duration(expected, actual, 0.001); - } else if (strcmp(duration_type, "microseconds") == 0) { + } else if (pizza_io_cstr_compare(duration_type, "microseconds") == 0) { assume_duration(expected, actual, 1e-6); - } else if (strcmp(duration_type, "nanoseconds") == 0) { + } else if (pizza_io_cstr_compare(duration_type, "nanoseconds") == 0) { assume_duration(expected, actual, 1e-9); - } else if (strcmp(duration_type, "picoseconds") == 0) { + } else if (pizza_io_cstr_compare(duration_type, "picoseconds") == 0) { assume_duration(expected, actual, 1e-12); - } else if (strcmp(duration_type, "femtoseconds") == 0) { + } else if (pizza_io_cstr_compare(duration_type, "femtoseconds") == 0) { assume_duration(expected, actual, 1e-15); - } else if (strcmp(duration_type, "attoseconds") == 0) { + } else if (pizza_io_cstr_compare(duration_type, "attoseconds") == 0) { assume_duration(expected, actual, 1e-18); - } else if (strcmp(duration_type, "zeptoseconds") == 0) { + } else if (pizza_io_cstr_compare(duration_type, "zeptoseconds") == 0) { assume_duration(expected, actual, 1e-21); - } else if (strcmp(duration_type, "yoctoseconds") == 0) { + } else if (pizza_io_cstr_compare(duration_type, "yoctoseconds") == 0) { assume_duration(expected, actual, 1e-24); } else { printf("Unknown option: %s\n", duration_type); @@ -125,13 +101,13 @@ void fossil_test_benchmark(char* duration_type, double expected, double actual) } // end of func void fossil_benchmark_init(fossil_mark_t* benchmark, const char* name) { - if (benchmark == NULL) { - printf("Error: benchmark is NULL\n"); + if (benchmark == null) { + pizza_io_printf("Error: benchmark is null\n"); return; } - if (name == NULL) { - printf("Error: name is NULL\n"); + if (name == null) { + pizza_io_printf("Error: name is null\n"); return; } @@ -144,8 +120,8 @@ void fossil_benchmark_init(fossil_mark_t* benchmark, const char* name) { } void fossil_benchmark_start(fossil_mark_t* benchmark) { - if (benchmark == NULL) { - printf("Error: benchmark is NULL\n"); + if (benchmark == null) { + pizza_io_printf("Error: benchmark is null\n"); return; } @@ -156,8 +132,8 @@ void fossil_benchmark_start(fossil_mark_t* benchmark) { } void fossil_benchmark_stop(fossil_mark_t* benchmark) { - if (benchmark == NULL) { - printf("Error: benchmark is NULL\n"); + if (benchmark == null) { + pizza_io_printf("Error: benchmark is null\n"); return; } @@ -177,40 +153,40 @@ void fossil_benchmark_stop(fossil_mark_t* benchmark) { } double fossil_benchmark_elapsed_seconds(const fossil_mark_t* benchmark) { - if (benchmark == NULL) { - printf("Error: benchmark is NULL\n"); + if (benchmark == null) { + pizza_io_printf("Error: benchmark is null\n"); return 0.0; } return benchmark->total_duration; } double fossil_benchmark_min_time(const fossil_mark_t* benchmark) { - if (benchmark == NULL) { - printf("Error: benchmark is NULL\n"); + if (benchmark == null) { + pizza_io_printf("Error: benchmark is null\n"); return 0.0; } return benchmark->min_duration; } double fossil_benchmark_max_time(const fossil_mark_t* benchmark) { - if (benchmark == NULL) { - printf("Error: benchmark is NULL\n"); + if (benchmark == null) { + pizza_io_printf("Error: benchmark is null\n"); return 0.0; } return benchmark->max_duration; } double fossil_benchmark_avg_time(const fossil_mark_t* benchmark) { - if (benchmark == NULL) { - printf("Error: benchmark is NULL\n"); + if (benchmark == null) { + pizza_io_printf("Error: benchmark is null\n"); return 0.0; } return benchmark->num_samples > 0 ? benchmark->total_duration / benchmark->num_samples : 0.0; } void fossil_benchmark_reset(fossil_mark_t* benchmark) { - if (benchmark == NULL) { - printf("Error: benchmark is NULL\n"); + if (benchmark == null) { + pizza_io_printf("Error: benchmark is null\n"); return; } benchmark->num_samples = 0; @@ -220,25 +196,25 @@ void fossil_benchmark_reset(fossil_mark_t* benchmark) { } void fossil_benchmark_report(const fossil_mark_t* benchmark) { - if (benchmark == NULL) { - printf("Error: benchmark is NULL\n"); + if (benchmark == null) { + pizza_io_printf("Error: benchmark is null\n"); return; } - printf("\033[1;36mBenchmark : %s\n", benchmark->name); - printf("\033[1;32mTotal Time: %.6f seconds\n", fossil_benchmark_elapsed_seconds(benchmark)); - printf("\033[1;32mMin Time : %.6f seconds\n", fossil_benchmark_min_time(benchmark)); - printf("\033[1;32mMax Time : %.6f seconds\n", fossil_benchmark_max_time(benchmark)); - printf("\033[1;32mAvg Time : %.6f seconds\n", fossil_benchmark_avg_time(benchmark)); + pizza_io_printf("\033[1;36mBenchmark : %s\n", benchmark->name); + pizza_io_printf("\033[1;32mTotal Time: %.6f seconds\n", fossil_benchmark_elapsed_seconds(benchmark)); + pizza_io_printf("\033[1;32mMin Time : %.6f seconds\n", fossil_benchmark_min_time(benchmark)); + pizza_io_printf("\033[1;32mMax Time : %.6f seconds\n", fossil_benchmark_max_time(benchmark)); + pizza_io_printf("\033[1;32mAvg Time : %.6f seconds\n", fossil_benchmark_avg_time(benchmark)); } void fossil_scoped_benchmark_init(fossil_scoped_mark_t* scoped_benchmark, fossil_mark_t* benchmark) { - if (scoped_benchmark == NULL) { - printf("Error: scoped_benchmark is NULL\n"); + if (scoped_benchmark == null) { + pizza_io_printf("Error: scoped_benchmark is null\n"); return; } - if (benchmark == NULL) { - printf("Error: benchmark is NULL\n"); + if (benchmark == null) { + pizza_io_printf("Error: benchmark is null\n"); return; } @@ -247,8 +223,8 @@ void fossil_scoped_benchmark_init(fossil_scoped_mark_t* scoped_benchmark, fossil } void fossil_scoped_benchmark_destroy(fossil_scoped_mark_t* scoped_benchmark) { - if (scoped_benchmark == NULL) { - printf("Error: scoped_benchmark is NULL\n"); + if (scoped_benchmark == null) { + pizza_io_printf("Error: scoped_benchmark is null\n"); return; } diff --git a/code/logic/meson.build b/code/logic/meson.build index 5aa736b0..5bfe6c35 100644 --- a/code/logic/meson.build +++ b/code/logic/meson.build @@ -1,11 +1,13 @@ dir = include_directories('.') +cc = meson.get_compiler('c') -test_code = ['mocking.c', 'testing.c', 'marking.c', 'internal.c'] +test_code = ['mock.c', 'test.c', 'mark.c', 'sanity.c', 'common.c'] -fossil_test_lib = library('fossil-test', +fossil_test_lib = library('pizza-test', test_code, install: true, - include_directories: dir) + include_directories: dir, + dependencies: [cc.find_library('m', required: false)]) fossil_test_dep = declare_dependency( link_with: fossil_test_lib, diff --git a/code/logic/mock.c b/code/logic/mock.c new file mode 100644 index 00000000..6eb5bc47 --- /dev/null +++ b/code/logic/mock.c @@ -0,0 +1,136 @@ +/* + * ----------------------------------------------------------------------------- + * Project: Fossil Logic + * + * This file is part of the Fossil Logic project, which aims to develop high- + * performance, cross-platform applications and libraries. The code contained + * herein is subject to the terms and conditions defined in the project license. + * + * Author: Michael Gene Brockus (Dreamer) + * Date: 04/05/2014 + * + * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. + * ----------------------------------------------------------------------------- + */ +#include "fossil/pizza/mock.h" +#include "fossil/pizza/common.h" + +// ***************************************************************************** +// Function declarations +// ***************************************************************************** + +void fossil_mock_init(fossil_mock_calllist_t *list) { + if (!list) { + return; + } + list->head = NULL; + list->tail = NULL; + list->size = 0; +} + +void fossil_mock_destroy(fossil_mock_calllist_t *list) { + if (!list) { + return; + } + + fossil_mock_call_t *current = list->head; + while (current) { + fossil_mock_call_t *next = current->next; + pizza_sys_memory_free(current->function_name); + for (int i = 0; i < current->num_args; ++i) { + pizza_sys_memory_free(current->arguments[i].value.data); + pizza_sys_memory_free(current->arguments[i].attribute.name); + pizza_sys_memory_free(current->arguments[i].attribute.description); + pizza_sys_memory_free(current->arguments[i].attribute.id); + } + pizza_sys_memory_free(current->arguments); + pizza_sys_memory_free(current); + current = next; + } + list->head = NULL; + list->tail = NULL; + list->size = 0; +} + +void fossil_mock_add_call(fossil_mock_calllist_t *list, const char *function_name, fossil_mock_pizza_t *arguments, int num_args) { + if (!list || !function_name || (!arguments && num_args > 0)) { + return; + } + + fossil_mock_call_t *call = (fossil_mock_call_t *)pizza_sys_memory_alloc(sizeof(fossil_mock_call_t)); + if (!call) { + return; + } + + call->function_name = pizza_io_cstr_dup(function_name); + if (!call->function_name) { + pizza_sys_memory_free(call); + return; + } + + if (num_args > 0) { + call->arguments = (fossil_mock_pizza_t *)pizza_sys_memory_alloc(num_args * sizeof(fossil_mock_pizza_t)); + if (!call->arguments) { + pizza_sys_memory_free(call->function_name); + pizza_sys_memory_free(call); + return; + } + + for (int i = 0; i < num_args; ++i) { + call->arguments[i].type = arguments[i].type; + call->arguments[i].value.data = pizza_io_cstr_dup(arguments[i].value.data); + call->arguments[i].value.mutable_flag = arguments[i].value.mutable_flag; + call->arguments[i].attribute.name = pizza_io_cstr_dup(arguments[i].attribute.name); + call->arguments[i].attribute.description = pizza_io_cstr_dup(arguments[i].attribute.description); + call->arguments[i].attribute.id = pizza_io_cstr_dup(arguments[i].attribute.id); + + if (!call->arguments[i].value.data || !call->arguments[i].attribute.name || + !call->arguments[i].attribute.description || !call->arguments[i].attribute.id) { + for (int j = 0; j <= i; ++j) { + pizza_sys_memory_free(call->arguments[j].value.data); + pizza_sys_memory_free(call->arguments[j].attribute.name); + pizza_sys_memory_free(call->arguments[j].attribute.description); + pizza_sys_memory_free(call->arguments[j].attribute.id); + } + pizza_sys_memory_free(call->arguments); + pizza_sys_memory_free(call->function_name); + pizza_sys_memory_free(call); + return; + } + } + } else { + call->arguments = NULL; + } + + call->num_args = num_args; + call->next = NULL; + + if (list->tail) { + list->tail->next = call; + } else { + list->head = call; + } + list->tail = call; + list->size++; +} + +void fossil_mock_print(fossil_mock_calllist_t *list) { + if (!list) { + return; + } + + fossil_mock_call_t *current = list->head; + while (current) { + pizza_io_printf("Function: %s\n", current->function_name); + pizza_io_printf("Arguments:\n"); + for (int i = 0; i < current->num_args; ++i) { + pizza_io_printf(" Type: %d\n", current->arguments[i].type); + pizza_io_printf(" Value: %s\n", current->arguments[i].value.data); + pizza_io_printf(" Mutable: %s\n", current->arguments[i].value.mutable_flag ? "true" : "false"); + pizza_io_printf(" Attribute Name: %s\n", current->arguments[i].attribute.name); + pizza_io_printf(" Attribute Description: %s\n", current->arguments[i].attribute.description); + pizza_io_printf(" Attribute ID: %s\n", current->arguments[i].attribute.id); + } + current = current->next; + } +} diff --git a/code/logic/mocking.c b/code/logic/mocking.c deleted file mode 100644 index abbe2fd8..00000000 --- a/code/logic/mocking.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * ----------------------------------------------------------------------------- - * File: framework.hpp - * Project: Fossil Logic - * Description: This file implments the framework for mockup testing. - * - * This file is part of the Fossil Logic project, which aims to develop high- - * performance, cross-platform applications and libraries. The code contained - * herein is subject to the terms and conditions defined in the project license. - * - * Author: Michael Gene Brockus (Dreamer) - * Date: 07/01/2024 - * - * Copyright (C) 2024 Fossil Logic. All rights reserved. - * ----------------------------------------------------------------------------- - */ -#include "fossil/test/mocking.h" - -extern char *_custom_fossil_test_strdup(const char *str); - -// ***************************************************************************** -// Function declarations -// ***************************************************************************** - -void fossil_mock_init(fossil_mock_calllist_t *list) { - if (!list) { - return; - } - list->head = NULL; - list->tail = NULL; - list->size = 0; -} - -void fossil_mock_destroy(fossil_mock_calllist_t *list) { - if (!list) { - return; - } - - fossil_mock_call_t *current = list->head; - while (current) { - fossil_mock_call_t *next = current->next; - free(current->function_name); - for (int i = 0; i < current->num_args; ++i) { - free(current->arguments[i]); - } - free(current->arguments); - free(current); - current = next; - } - list->head = NULL; - list->tail = NULL; - list->size = 0; -} - -void fossil_mock_add_call(fossil_mock_calllist_t *list, const char *function_name, char **arguments, int num_args) { - if (!list || !function_name || !arguments) { - return; - } - - fossil_mock_call_t *call = (fossil_mock_call_t *)malloc(sizeof(fossil_mock_call_t)); - if (!call) { - return; - } - - call->function_name = _custom_fossil_test_strdup(function_name); - if (!call->function_name) { - free(call); - return; - } - - call->arguments = (char **)malloc(num_args * sizeof(char *)); - if (!call->arguments) { - free(call->function_name); - free(call); - return; - } - - for (int i = 0; i < num_args; ++i) { - call->arguments[i] = _custom_fossil_test_strdup(arguments[i]); - if (!call->arguments[i]) { - for (int j = 0; j < i; ++j) { - free(call->arguments[j]); - } - free(call->arguments); - free(call->function_name); - free(call); - return; - } - } - - call->num_args = num_args; - call->next = NULL; - - if (list->tail) { - list->tail->next = call; - } else { - list->head = call; - } - list->tail = call; - list->size++; -} - -void fossil_mock_print(fossil_mock_calllist_t *list) { - if (!list) { - return; - } - - fossil_mock_call_t *current = list->head; - while (current) { - printf("Function: %s\n", current->function_name); - printf("Arguments: "); - for (int i = 0; i < current->num_args; ++i) { - printf("%s", current->arguments[i]); - if (i < current->num_args - 1) { - printf(", "); - } - } - printf("\n"); - current = current->next; - } -} diff --git a/code/logic/sanity.c b/code/logic/sanity.c new file mode 100644 index 00000000..24a227c1 --- /dev/null +++ b/code/logic/sanity.c @@ -0,0 +1,87 @@ +/* + * ----------------------------------------------------------------------------- + * Project: Fossil Logic + * + * This file is part of the Fossil Logic project, which aims to develop high- + * performance, cross-platform applications and libraries. The code contained + * herein is subject to the terms and conditions defined in the project license. + * + * Author: Michael Gene Brockus (Dreamer) + * Date: 04/05/2014 + * + * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. + * ----------------------------------------------------------------------------- + */ +#include "fossil/pizza/sanity.h" +#ifdef _WIN32 +#include +#else +#include +#include +#include +#include +#endif + +int pizza_sys_call_execute(const char *command) { +#ifdef _WIN32 + return system(command); // On Windows, use the system function to execute the command. +#else + return system(command); // On Unix-like systems, use the system function to execute the command. +#endif +} + +int pizza_sys_call_getpid(void) { +#ifdef _WIN32 + return GetCurrentProcessId(); // On Windows, use the GetCurrentProcessId function to get the process ID. +#else + return getpid(); // On Unix-like systems, use the getpid function to get the process ID. +#endif +} + +void pizza_sys_call_sleep(int milliseconds) { +#ifdef _WIN32 + Sleep(milliseconds); // On Windows, use the Sleep function to sleep for the specified number of milliseconds. +#else + sleep(milliseconds * 1000); // On Unix-like systems, use the usleep function to sleep for the specified number of microseconds. +#endif +} + +int pizza_sys_call_create_file(const char *filename) { +#ifdef _WIN32 + HANDLE hFile = CreateFileA(filename, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) return -1; // If the file handle is invalid, return an error code. + CloseHandle(hFile); // Close the file handle. + return 0; // Return success. +#else + int fd = open(filename, O_CREAT | O_WRONLY, 0644); // On Unix-like systems, use the open function to create the file. + if (fd == -1) return -1; // If the file descriptor is invalid, return an error code. + close(fd); // Close the file descriptor. + return 0; // Return success. +#endif +} + +int fossil_sanity_sys_execute(const char* command) { + return pizza_sys_call_execute(command); +} + +int fossil_sanity_sys_getpid(void) { + return pizza_sys_call_getpid(); +} + +void fossil_sanity_sys_sleep(int milliseconds) { + pizza_sys_call_sleep(milliseconds); +} + +int fossil_sanity_sys_create_file(const char* filename) { + return pizza_sys_call_create_file(filename); +} + +int fossil_sanity_sys_file_exists(const char* filename) { +#ifdef _WIN32 + struct _stat buffer; + return (_stat(filename, &buffer) == 0); // On Windows, use the _stat function to check if the file exists. +#else + struct stat buffer; + return (stat(filename, &buffer) == 0); // On Unix-like systems, use the stat function to check if the file exists. +#endif +} diff --git a/code/logic/test.c b/code/logic/test.c new file mode 100644 index 00000000..a6badeee --- /dev/null +++ b/code/logic/test.c @@ -0,0 +1,922 @@ +/* + * ----------------------------------------------------------------------------- + * Project: Fossil Logic + * + * This file is part of the Fossil Logic project, which aims to develop high- + * performance, cross-platform applications and libraries. The code contained + * herein is subject to the terms and conditions defined in the project license. + * + * Author: Michael Gene Brockus (Dreamer) + * Date: 04/05/2014 + * + * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. + * ----------------------------------------------------------------------------- + */ +#include "fossil/pizza/test.h" +#include +#include +#include +#include +#include +#include +#include + +jmp_buf test_jump_buffer; // This will hold the jump buffer for longjmp +static int _ASSERT_COUNT = 0; // Counter for the number of assertions + +// --- Internal helper for timing --- +static uint64_t fossil_pizza_now_ns(void) { + struct timeval tv; + gettimeofday(&tv, null); + return (uint64_t)tv.tv_sec * 1000000000ULL + (uint64_t)tv.tv_usec * 1000ULL; +} + +// --- Start --- +int fossil_pizza_start(fossil_pizza_engine_t* engine, int argc, char** argv) { + if (!engine || !argv) return FOSSIL_PIZZA_FAILURE; + + pizza_sys_memory_set(engine, 0, sizeof(*engine)); + engine->suites = null; + engine->count = 0; + engine->capacity = 0; + engine->score_total = 0; + engine->score_possible = 0; + engine->pallet = fossil_pizza_pallet_create(argc, argv); + pizza_sys_memory_set(&engine->score, 0, sizeof(engine->score)); + + return FOSSIL_PIZZA_SUCCESS; +} + +// --- Add Suite --- +int fossil_pizza_add_suite(fossil_pizza_engine_t* engine, fossil_pizza_suite_t suite) { + if (!engine) return FOSSIL_PIZZA_FAILURE; + if (engine->count >= engine->capacity) { + size_t new_cap = engine->capacity ? engine->capacity * 2 : 4; + fossil_pizza_suite_t* resized = pizza_sys_memory_realloc(engine->suites, new_cap * sizeof(*engine->suites)); + if (!resized) return FOSSIL_PIZZA_FAILURE; + engine->suites = resized; + engine->capacity = new_cap; + } + engine->suites[engine->count++] = suite; + return FOSSIL_PIZZA_SUCCESS; +} + +// --- Add Case --- +int fossil_pizza_add_case(fossil_pizza_suite_t* suite, fossil_pizza_case_t test_case) { + if (!suite) return FOSSIL_PIZZA_FAILURE; + if (suite->count >= suite->capacity) { + size_t new_cap = suite->capacity ? suite->capacity * 2 : 4; + fossil_pizza_case_t* resized = pizza_sys_memory_realloc(suite->cases, new_cap * sizeof(*suite->cases)); + if (!resized) return FOSSIL_PIZZA_FAILURE; + suite->cases = resized; + suite->capacity = new_cap; + } + suite->cases[suite->count++] = test_case; + return FOSSIL_PIZZA_SUCCESS; +} + +// --- Update Score --- +void fossil_pizza_update_score(fossil_pizza_case_t* test_case, fossil_pizza_suite_t* suite) { + switch (test_case->result) { + case FOSSIL_PIZZA_CASE_PASS: + suite->score.passed++; + suite->total_score++; + break; + case FOSSIL_PIZZA_CASE_FAIL: + suite->score.failed++; + break; + case FOSSIL_PIZZA_CASE_TIMEOUT: + suite->score.timeout++; + break; + case FOSSIL_PIZZA_CASE_SKIPPED: + suite->score.skipped++; + break; + case FOSSIL_PIZZA_CASE_UNEXPECTED: + suite->score.unexpected++; + break; + case FOSSIL_PIZZA_CASE_EMPTY: + default: + suite->score.empty++; + break; + } + suite->total_possible++; +} + +// --- Run One Test --- +void fossil_pizza_test_output(const fossil_pizza_case_t* test_case) { + if (!test_case) return; + + const char* result_str = + (test_case->result == FOSSIL_PIZZA_CASE_PASS) ? "PASS" : + (test_case->result == FOSSIL_PIZZA_CASE_FAIL) ? "FAIL" : + (test_case->result == FOSSIL_PIZZA_CASE_EMPTY) ? "EMPTY" : "UNKNOWN"; + + switch (G_PIZZA_THEME) { + case PIZZA_THEME_FOSSIL: + if (G_PIZZA_VERBOSE == PIZZA_VERBOSE_PLAIN) { + pizza_io_printf("{blue}Test Case:{reset} %s | {green}Result:{reset} %s | {yellow}Time:{reset} %llu ns\n", + test_case->name, result_str, test_case->elapsed_ns); + } else if (G_PIZZA_VERBOSE == PIZZA_VERBOSE_DOGE) { + pizza_io_printf("{blue}========================================{reset}\n"); + pizza_io_printf("{cyan}Test Case:{reset} %s\n", test_case->name); + pizza_io_printf("{cyan}Given Result:{reset} %s\n", result_str); + pizza_io_printf("{cyan}With Timestamp:{reset} %llu ns\n", test_case->elapsed_ns); + pizza_io_printf("{blue}========================================{reset}\n"); + } + break; + + case PIZZA_THEME_CATCH: + case PIZZA_THEME_DOCTEST: + if (G_PIZZA_VERBOSE == PIZZA_VERBOSE_PLAIN) { + pizza_io_printf("{cyan}Test Case:{reset} %s | {green}Result:{reset} %s | {yellow}Time:{reset} %llu ns\n", + test_case->name, result_str, test_case->elapsed_ns); + } else if (G_PIZZA_VERBOSE == PIZZA_VERBOSE_DOGE) { + pizza_io_printf("{cyan}========================================{reset}\n"); + pizza_io_printf("{cyan}Test Case:{reset} %s\n", test_case->name); + pizza_io_printf("{cyan}Given Result:{reset} %s\n", result_str); + pizza_io_printf("{cyan}With Timestamp:{reset} %llu ns\n", test_case->elapsed_ns); + pizza_io_printf("{cyan}========================================{reset}\n"); + } + break; + + case PIZZA_THEME_CPPUTEST: + if (G_PIZZA_VERBOSE == PIZZA_VERBOSE_PLAIN) { + pizza_io_printf("{blue}[TEST CASE]{reset} %s | {green}[RESULT]{reset} %s | {yellow}[TIME]{reset} %llu ns\n", + test_case->name, result_str, test_case->elapsed_ns); + } else if (G_PIZZA_VERBOSE == PIZZA_VERBOSE_DOGE) { + pizza_io_printf("{blue}[========================================]{reset}\n"); + pizza_io_printf("{blue}[Test Case]:{reset} %s\n", test_case->name); + pizza_io_printf("{blue}[Given Result]:{reset} %s\n", result_str); + pizza_io_printf("{blue}[With Timestamp]:{reset} %llu ns\n", test_case->elapsed_ns); + pizza_io_printf("{blue}[========================================]{reset}\n"); + } + break; + + case PIZZA_THEME_TAP: + if (G_PIZZA_VERBOSE == PIZZA_VERBOSE_PLAIN) { + pizza_io_printf("{green}ok - Test Case:{reset} %s | {green}Result:{reset} %s | {yellow}Time:{reset} %llu ns\n", + test_case->name, result_str, test_case->elapsed_ns); + } else if (G_PIZZA_VERBOSE == PIZZA_VERBOSE_DOGE) { + pizza_io_printf("{green}# ========================================{reset}\n"); + pizza_io_printf("{green}# Test Case:{reset} %s\n", test_case->name); + pizza_io_printf("{green}# Given Result:{reset} %s\n", result_str); + pizza_io_printf("{green}# With Timestamp:{reset} %llu ns\n", test_case->elapsed_ns); + pizza_io_printf("{green}# ========================================{reset}\n"); + } + break; + + case PIZZA_THEME_GOOGLETEST: + if (G_PIZZA_VERBOSE == PIZZA_VERBOSE_PLAIN) { + pizza_io_printf("{blue}[ RUN ]{reset} %s\n", test_case->name); + pizza_io_printf("{green}[ %s ]{reset} %s ({yellow}%llu ns{reset})\n", result_str, test_case->name, test_case->elapsed_ns); + } else if (G_PIZZA_VERBOSE == PIZZA_VERBOSE_DOGE) { + pizza_io_printf("{blue}[========================================]{reset}\n"); + pizza_io_printf("{blue}[Test Case]:{reset} %s\n", test_case->name); + pizza_io_printf("{blue}[Given Result]:{reset} %s\n", result_str); + pizza_io_printf("{blue}[With Timestamp]:{reset} %llu ns\n", test_case->elapsed_ns); + pizza_io_printf("{blue}[========================================]{reset}\n"); + } + break; + + case PIZZA_THEME_UNITY: + if (G_PIZZA_VERBOSE == PIZZA_VERBOSE_PLAIN) { + pizza_io_printf("{magenta}Unity Test Case:{reset} %s | {green}Result:{reset} %s | {yellow}Time:{reset} %llu ns\n", + test_case->name, result_str, test_case->elapsed_ns); + } else if (G_PIZZA_VERBOSE == PIZZA_VERBOSE_DOGE) { + pizza_io_printf("{magenta}========================================{reset}\n"); + pizza_io_printf("{magenta}Test Case:{reset} %s\n", test_case->name); + pizza_io_printf("{magenta}Given Result:{reset} %s\n", result_str); + pizza_io_printf("{magenta}With Timestamp:{reset} %llu ns\n", test_case->elapsed_ns); + pizza_io_printf("{magenta}========================================{reset}\n"); + } + break; + + default: + if (G_PIZZA_VERBOSE == PIZZA_VERBOSE_PLAIN) { + pizza_io_printf("{cyan}Test Case:{reset} %s | {green}Result:{reset} %s | {yellow}Time:{reset} %llu ns\n", + test_case->name, result_str, test_case->elapsed_ns); + } else if (G_PIZZA_VERBOSE == PIZZA_VERBOSE_DOGE) { + pizza_io_printf("{cyan}========================================{reset}\n"); + pizza_io_printf("{cyan}Test Case:{reset} %s\n", test_case->name); + pizza_io_printf("{cyan}Given Result:{reset} %s\n", result_str); + pizza_io_printf("{cyan}With Timestamp:{reset} %llu ns\n", test_case->elapsed_ns); + pizza_io_printf("{cyan}========================================{reset}\n"); + } + break; + } +} + +void fossil_pizza_run_test(const fossil_pizza_engine_t* engine, fossil_pizza_case_t* test_case, fossil_pizza_suite_t* suite) { + if (!test_case || !suite) return; + + if (test_case->result != FOSSIL_PIZZA_CASE_SKIPPED) { + // Check if the test case name matches the --only filter + if (engine->pallet.run.only && pizza_io_cstr_compare(engine->pallet.run.only, test_case->name) != 0) { + return; + } + + for (int i = 0; i < (engine->pallet.run.repeat ? engine->pallet.run.repeat : 1); ++i) { + if (test_case->setup) test_case->setup(); + + uint64_t start_time = fossil_pizza_now_ns(); + + if (test_case->run) { + if (setjmp(test_jump_buffer) == 0) { + test_case->run(); + uint64_t elapsed_time = fossil_pizza_now_ns() - start_time; + + if (1000000ULL > 0 && elapsed_time > 1000000ULL) { + test_case->result = FOSSIL_PIZZA_CASE_TIMEOUT; + } else { + test_case->result = FOSSIL_PIZZA_CASE_PASS; + } + } else { + test_case->result = FOSSIL_PIZZA_CASE_FAIL; + if (engine->pallet.run.fail_fast) { + fossil_pizza_test_output(test_case); + return; // Exit immediately if --fail-fast is enabled + } + } + } else { + test_case->result = FOSSIL_PIZZA_CASE_EMPTY; + } + test_case->elapsed_ns = fossil_pizza_now_ns() - start_time; + + if (test_case->teardown) test_case->teardown(); + } + + // Output test case result + fossil_pizza_test_output(test_case); + } else { + // Handle unexpected cases + test_case->result = FOSSIL_PIZZA_CASE_UNEXPECTED; + fossil_pizza_test_output(test_case); + } + + // Update scores based on result + fossil_pizza_update_score(test_case, suite); + _ASSERT_COUNT = 0; // Reset the assertion count for the next test case +} + +// --- Algorithmic modifications --- + +// --- Sorting Test Cases --- +static int compare_name_asc(const void* a, const void* b) { + return pizza_io_cstr_compare(((fossil_pizza_case_t*)a)->name, ((fossil_pizza_case_t*)b)->name); +} + +static int compare_name_desc(const void* a, const void* b) { + return pizza_io_cstr_compare(((fossil_pizza_case_t*)b)->name, ((fossil_pizza_case_t*)a)->name); +} + +static int compare_result_asc(const void* a, const void* b) { + return ((fossil_pizza_case_t*)a)->result - ((fossil_pizza_case_t*)b)->result; +} + +static int compare_result_desc(const void* a, const void* b) { + return ((fossil_pizza_case_t*)b)->result - ((fossil_pizza_case_t*)a)->result; +} + +static int compare_time_asc(const void* a, const void* b) { + return ((fossil_pizza_case_t*)a)->elapsed_ns - ((fossil_pizza_case_t*)b)->elapsed_ns; +} + +static int compare_time_desc(const void* a, const void* b) { + return ((fossil_pizza_case_t*)b)->elapsed_ns - ((fossil_pizza_case_t*)a)->elapsed_ns; +} + +void fossil_pizza_sort_cases(fossil_pizza_suite_t* suite, const fossil_pizza_engine_t* engine) { + if (!suite || !suite->cases || suite->count <= 1 || !engine) return; + + int (*compare)(const void*, const void*) = null; + + if (engine->pallet.sort.by) { + if (pizza_io_cstr_compare(engine->pallet.sort.by, "name") == 0) { + compare = (pizza_io_cstr_compare(engine->pallet.sort.order, "desc") == 0) ? compare_name_desc : compare_name_asc; + } else if (pizza_io_cstr_compare(engine->pallet.sort.by, "result") == 0) { + compare = (pizza_io_cstr_compare(engine->pallet.sort.order, "desc") == 0) ? compare_result_desc : compare_result_asc; + } else if (pizza_io_cstr_compare(engine->pallet.sort.by, "time") == 0) { + compare = (pizza_io_cstr_compare(engine->pallet.sort.order, "desc") == 0) ? compare_time_desc : compare_time_asc; + } else { + // Invalid sort criteria + return; + } + } + + if (engine->pallet.sort.order) { + if (pizza_io_cstr_compare(engine->pallet.sort.order, "desc") == 0) { + compare = (compare == compare_name_asc) ? compare_name_desc : compare; + } else if (pizza_io_cstr_compare(engine->pallet.sort.order, "asc") == 0) { + compare = (compare == compare_name_desc) ? compare_name_asc : compare; + } + } + + if (compare) { + qsort(suite->cases, suite->count, sizeof(fossil_pizza_case_t), compare); + } +} + +// --- Filtering Test Cases --- +size_t fossil_pizza_filter_cases(fossil_pizza_suite_t* suite, const fossil_pizza_engine_t* engine, fossil_pizza_case_t** filtered_cases) { + if (!suite || !suite->cases || !filtered_cases || !engine) return 0; + + size_t count = 0; + for (size_t i = 0; i < suite->count; ++i) { + fossil_pizza_case_t* test_case = &suite->cases[i]; + + // Apply filters based on engine->pallet.filter + if (engine->pallet.filter.test_name && pizza_io_cstr_compare(test_case->name, engine->pallet.filter.test_name) != 0) { + continue; + } + if (engine->pallet.filter.suite_name && pizza_io_cstr_compare(suite->suite_name, engine->pallet.filter.suite_name) != 0) { + continue; + } + if (engine->pallet.filter.tag && (!test_case->tags || !strstr(test_case->tags, engine->pallet.filter.tag))) { + continue; + } + + filtered_cases[count++] = test_case; + } + return count; +} + +// --- Shuffling Test Cases --- +void fossil_pizza_shuffle_cases(fossil_pizza_suite_t* suite, const fossil_pizza_engine_t* engine) { + if (!suite || !suite->cases || suite->count < 2) return; + + unsigned int seed = (engine && engine->pallet.shuffle.seed) + ? (unsigned int)atoi(engine->pallet.shuffle.seed) + : (unsigned int)time(null); + srand(seed); + + for (size_t i = suite->count - 1; i > 0; --i) { + size_t j = rand() % (i + 1); + fossil_pizza_case_t temp = suite->cases[i]; + suite->cases[i] = suite->cases[j]; + suite->cases[j] = temp; + } + + if (engine && engine->pallet.shuffle.by) { + if (pizza_io_cstr_compare(engine->pallet.shuffle.by, "name") == 0) { + qsort(suite->cases, suite->count, sizeof(fossil_pizza_case_t), + (int (*)(const void*, const void*))pizza_io_cstr_compare); + } else if (pizza_io_cstr_compare(engine->pallet.shuffle.by, "result") == 0) { + qsort(suite->cases, suite->count, sizeof(fossil_pizza_case_t), compare_result_asc); + } else if (pizza_io_cstr_compare(engine->pallet.shuffle.by, "time") == 0) { + qsort(suite->cases, suite->count, sizeof(fossil_pizza_case_t), compare_time_asc); + } + } +} + +// --- Run One Suite --- +int fossil_pizza_run_suite(const fossil_pizza_engine_t* engine, fossil_pizza_suite_t* suite) { + if (!suite) return FOSSIL_PIZZA_FAILURE; + if (suite->setup) suite->setup(); + + suite->time_elapsed_ns = fossil_pizza_now_ns(); + suite->total_score = 0; + suite->total_possible = 0; + pizza_sys_memory_set(&suite->score, 0, sizeof(suite->score)); + + if (!suite->cases) return FOSSIL_PIZZA_FAILURE; + + // Apply filtering, sorting, and shuffling + fossil_pizza_case_t* filtered_cases[suite->count]; + size_t filtered_count = fossil_pizza_filter_cases(suite, engine, filtered_cases); + + if (filtered_count > 0) { + fossil_pizza_sort_cases(suite, engine); + fossil_pizza_shuffle_cases(suite, engine); + + for (size_t i = 0; i < filtered_count; ++i) { + fossil_pizza_case_t* test_case = filtered_cases[i]; + fossil_pizza_run_test(engine, test_case, suite); + } + } + + suite->time_elapsed_ns = fossil_pizza_now_ns() - suite->time_elapsed_ns; + + if (suite->teardown) suite->teardown(); + return FOSSIL_PIZZA_SUCCESS; +} + +// --- Run All Suites --- +int fossil_pizza_run_all(fossil_pizza_engine_t* engine) { + if (!engine) return FOSSIL_PIZZA_FAILURE; + pizza_sys_memory_set(&engine->score, 0, sizeof(engine->score)); + engine->score_total = 0; + engine->score_possible = 0; + + for (size_t i = 0; i < engine->count; ++i) { + fossil_pizza_run_suite(engine, &engine->suites[i]); + + engine->score_total += engine->suites[i].total_score; + engine->score_possible += engine->suites[i].total_possible; + + // aggregate suite scores + fossil_pizza_score_t* src = &engine->suites[i].score; + engine->score.passed += src->passed; + engine->score.failed += src->failed; + engine->score.skipped += src->skipped; + engine->score.timeout += src->timeout; + engine->score.unexpected += src->unexpected; + engine->score.empty += src->empty; + } + return FOSSIL_PIZZA_SUCCESS; +} + +// --- Summary Report --- +void fossil_pizza_summary_timestamp(const fossil_pizza_engine_t* engine) { + if (!engine) return; + + uint64_t total_elapsed_ns = 0; + for (size_t i = 0; i < engine->count; ++i) { + total_elapsed_ns += engine->suites[i].time_elapsed_ns; + } + + uint64_t total_elapsed_us = total_elapsed_ns / 1000; + uint64_t total_elapsed_ms = total_elapsed_us / 1000; + uint64_t total_elapsed_s = total_elapsed_ms / 1000; + uint64_t minutes = total_elapsed_s / 60; + uint64_t seconds = total_elapsed_s % 60; + uint64_t microseconds = total_elapsed_us % 1000; + uint64_t nanoseconds = total_elapsed_ns % 1000; + + switch (engine->pallet.theme) { + case PIZZA_THEME_FOSSIL: + pizza_io_printf("{blue,bold}\n========================================================================={reset}\n"); + pizza_io_printf("{blue,bold}Elapsed Time:{white} %llu minutes, %llu seconds, %llu microseconds, %llu nanoseconds\n{reset}", + minutes, seconds, microseconds, nanoseconds); + pizza_io_printf("{blue,bold}========================================================================={reset}\n"); + break; + + case PIZZA_THEME_CATCH: + case PIZZA_THEME_DOCTEST: + pizza_io_printf("\n=========================================================================\n"); + pizza_io_printf("Elapsed Time: %llu minutes, %llu seconds, %llu microseconds, %llu nanoseconds\n", + minutes, seconds, microseconds, nanoseconds); + pizza_io_printf("=========================================================================\n"); + break; + + case PIZZA_THEME_CPPUTEST: + pizza_io_printf("\n=========================================================================\n"); + pizza_io_printf("[Elapsed Time]: %llu minutes, %llu seconds, %llu microseconds, %llu nanoseconds\n", + minutes, seconds, microseconds, nanoseconds); + pizza_io_printf("=========================================================================\n"); + break; + + case PIZZA_THEME_TAP: + pizza_io_printf("\n# Elapsed Time: %llu minutes, %llu seconds, %llu microseconds, %llu nanoseconds\n", + minutes, seconds, microseconds, nanoseconds); + break; + + case PIZZA_THEME_GOOGLETEST: + pizza_io_printf("[==========] Elapsed Time: %llu minutes, %llu seconds, %llu microseconds, %llu nanoseconds\n", + minutes, seconds, microseconds, nanoseconds); + break; + + case PIZZA_THEME_UNITY: + pizza_io_printf("Unity Test Elapsed Time\n"); + pizza_io_printf("Minutes: %llu, Seconds: %llu, Microseconds: %llu, Nanoseconds: %llu\n", + minutes, seconds, microseconds, nanoseconds); + break; + + default: + pizza_io_printf("Unknown theme. Unable to display elapsed time.\n"); + break; + } + + // Calculate averages + double avg_time_per_suite_ns = (engine->count > 0) ? (double)total_elapsed_ns / engine->count : 0; + double avg_time_per_test_ns = (engine->score_possible > 0) ? (double)total_elapsed_ns / engine->score_possible : 0; + + switch (engine->pallet.theme) { + case PIZZA_THEME_FOSSIL: + pizza_io_printf("{blue,bold}Average Time per Suite:{white} %.2f nanoseconds\n{reset}", avg_time_per_suite_ns); + pizza_io_printf("{blue,bold}Average Time per Test :{white} %.2f nanoseconds\n{reset}", avg_time_per_test_ns); + pizza_io_printf("{blue,bold}========================================================================={reset}\n"); + break; + + case PIZZA_THEME_CATCH: + case PIZZA_THEME_DOCTEST: + pizza_io_printf("Average Time per Suite: %.2f nanoseconds\n", avg_time_per_suite_ns); + pizza_io_printf("Average Time per Test : %.2f nanoseconds\n", avg_time_per_test_ns); + pizza_io_printf("=========================================================================\n"); + break; + + case PIZZA_THEME_CPPUTEST: + pizza_io_printf("[Average Time per Suite]: %.2f nanoseconds\n", avg_time_per_suite_ns); + pizza_io_printf("[Average Time per Test ]: %.2f nanoseconds\n", avg_time_per_test_ns); + pizza_io_printf("=========================================================================\n"); + break; + + case PIZZA_THEME_TAP: + pizza_io_printf("# Average Time per Suite: %.2f nanoseconds\n", avg_time_per_suite_ns); + pizza_io_printf("# Average Time per Test : %.2f nanoseconds\n", avg_time_per_test_ns); + break; + + case PIZZA_THEME_GOOGLETEST: + pizza_io_printf("[----------] Average Time per Suite: %.2f nanoseconds\n", avg_time_per_suite_ns); + pizza_io_printf("[----------] Average Time per Test : %.2f nanoseconds\n", avg_time_per_test_ns); + break; + + case PIZZA_THEME_UNITY: + pizza_io_printf("Average Time per Suite: %.2f nanoseconds\n", avg_time_per_suite_ns); + pizza_io_printf("Average Time per Test : %.2f nanoseconds\n", avg_time_per_test_ns); + break; + + default: + pizza_io_printf("Unknown theme. Unable to display average times.\n"); + break; + } +} + +void fossil_pizza_summary_scoreboard(const fossil_pizza_engine_t* engine) { + if (!engine) return; + + double success_rate = (engine->score_possible > 0) + ? ((double)engine->score_total / engine->score_possible) * 100 + : 0; + + switch (engine->pallet.theme) { + case PIZZA_THEME_FOSSIL: + pizza_io_printf("{blue,bold}Suites run:{cyan} %4zu, {blue}Test run:{cyan} %4d, {blue}Score:{cyan} %d/%d\n{reset}", + engine->count, engine->score_possible, engine->score_total, engine->score_possible); + pizza_io_printf("{blue}Passed :{cyan} %4d\n{reset}", engine->score.passed); + pizza_io_printf("{blue}Failed :{cyan} %4d\n{reset}", engine->score.failed); + pizza_io_printf("{blue}Skipped :{cyan} %4d\n{reset}", engine->score.skipped); + pizza_io_printf("{blue}Timeouts :{cyan} %4d\n{reset}", engine->score.timeout); + pizza_io_printf("{blue}Unexpected:{cyan} %4d\n{reset}", engine->score.unexpected); + pizza_io_printf("{blue}Empty :{cyan} %4d\n{reset}", engine->score.empty); + pizza_io_printf("{blue}Success Rate:{cyan} %.2f%%\n{reset}", success_rate); + break; + + case PIZZA_THEME_CATCH: + case PIZZA_THEME_DOCTEST: + pizza_io_printf("Suites run: %zu, Test run: %d, Score: %d/%d\n", + engine->count, engine->score_possible, engine->score_total, engine->score_possible); + pizza_io_printf("Passed : %d\n", engine->score.passed); + pizza_io_printf("Failed : %d\n", engine->score.failed); + pizza_io_printf("Skipped : %d\n", engine->score.skipped); + pizza_io_printf("Timeouts : %d\n", engine->score.timeout); + pizza_io_printf("Unexpected: %d\n", engine->score.unexpected); + pizza_io_printf("Empty : %d\n", engine->score.empty); + pizza_io_printf("Success Rate: %.2f%%\n", success_rate); + break; + + case PIZZA_THEME_CPPUTEST: + pizza_io_printf("TEST SUMMARY:\n"); + pizza_io_printf("Suites run: %zu\n", engine->count); + pizza_io_printf("Tests run : %d\n", engine->score_possible); + pizza_io_printf("Score : %d/%d\n", engine->score_total, engine->score_possible); + pizza_io_printf("Passed : %d\n", engine->score.passed); + pizza_io_printf("Failed : %d\n", engine->score.failed); + pizza_io_printf("Skipped : %d\n", engine->score.skipped); + pizza_io_printf("Timeouts : %d\n", engine->score.timeout); + pizza_io_printf("Unexpected: %d\n", engine->score.unexpected); + pizza_io_printf("Empty : %d\n", engine->score.empty); + pizza_io_printf("Success Rate: %.2f%%\n", success_rate); + break; + + case PIZZA_THEME_TAP: + pizza_io_printf("TAP version 13\n"); + pizza_io_printf("# Suites run: %zu\n", engine->count); + pizza_io_printf("# Tests run : %d\n", engine->score_possible); + pizza_io_printf("# Score : %d/%d\n", engine->score_total, engine->score_possible); + pizza_io_printf("# Passed : %d\n", engine->score.passed); + pizza_io_printf("# Failed : %d\n", engine->score.failed); + pizza_io_printf("# Skipped : %d\n", engine->score.skipped); + pizza_io_printf("# Timeouts : %d\n", engine->score.timeout); + pizza_io_printf("# Unexpected: %d\n", engine->score.unexpected); + pizza_io_printf("# Empty : %d\n", engine->score.empty); + pizza_io_printf("# Success Rate: %.2f%%\n", success_rate); + break; + + case PIZZA_THEME_GOOGLETEST: + pizza_io_printf("[==========] Suites run: %zu\n", engine->count); + pizza_io_printf("[----------] Tests run : %d\n", engine->score_possible); + pizza_io_printf("[==========] Score : %d/%d\n", engine->score_total, engine->score_possible); + pizza_io_printf("[ PASSED ] %d tests.\n", engine->score.passed); + pizza_io_printf("[ FAILED ] %d tests.\n", engine->score.failed); + pizza_io_printf("[ SKIPPED ] %d tests.\n", engine->score.skipped); + pizza_io_printf("[ TIMEOUTS ] %d tests.\n", engine->score.timeout); + pizza_io_printf("[UNEXPECTED] %d tests.\n", engine->score.unexpected); + pizza_io_printf("[ EMPTY ] %d tests.\n", engine->score.empty); + pizza_io_printf("[ SUCCESS ] %.2f%%\n", success_rate); + break; + + case PIZZA_THEME_UNITY: + pizza_io_printf("Unity Test Summary\n"); + pizza_io_printf("Suites run: %zu\n", engine->count); + pizza_io_printf("Tests run : %d\n", engine->score_possible); + pizza_io_printf("Score : %d/%d\n", engine->score_total, engine->score_possible); + pizza_io_printf("Passed : %d\n", engine->score.passed); + pizza_io_printf("Failed : %d\n", engine->score.failed); + pizza_io_printf("Skipped : %d\n", engine->score.skipped); + pizza_io_printf("Timeouts : %d\n", engine->score.timeout); + pizza_io_printf("Unexpected: %d\n", engine->score.unexpected); + pizza_io_printf("Empty : %d\n", engine->score.empty); + pizza_io_printf("Success Rate: %.2f%%\n", success_rate); + break; + + default: + pizza_io_printf("Unknown theme. Unable to display scoreboard.\n"); + break; + } +} + +void fossil_pizza_summary_heading(const fossil_pizza_engine_t* engine) { + pizza_sys_hostinfo_system_t system_info; + pizza_sys_hostinfo_endianness_t endianness_info; + + pizza_sys_hostinfo_get_system(&system_info); + pizza_sys_hostinfo_get_endianness(&endianness_info); + + switch (engine->pallet.theme) { + case PIZZA_THEME_FOSSIL: + pizza_io_printf("{blue,bold}========================================================================={reset}\n"); + pizza_io_printf("{blue}==={cyan} Fossil Pizza Summary {blue}===: os{magenta} %s {blue}endianess:{magenta} %s {reset}\n", + system_info.os_name, endianness_info.is_little_endian ? "Little-endian" : "Big-endian"); + pizza_io_printf("{blue,bold}========================================================================={reset}\n"); + break; + + case PIZZA_THEME_CATCH: + case PIZZA_THEME_DOCTEST: + pizza_io_printf("=========================================================================\n"); + pizza_io_printf("=== Fossil Pizza Summary ===: os %s endianess: %s\n", + system_info.os_name, endianness_info.is_little_endian ? "Little-endian" : "Big-endian"); + pizza_io_printf("=========================================================================\n"); + break; + + case PIZZA_THEME_CPPUTEST: + pizza_io_printf("=========================================================================\n"); + pizza_io_printf("[Fossil Pizza Summary]: os %s endianess: %s\n", + system_info.os_name, endianness_info.is_little_endian ? "Little-endian" : "Big-endian"); + pizza_io_printf("=========================================================================\n"); + break; + + case PIZZA_THEME_TAP: + pizza_io_printf("TAP version 13\n"); + pizza_io_printf("# Fossil Pizza Summary: os %s endianess: %s\n", + system_info.os_name, endianness_info.is_little_endian ? "Little-endian" : "Big-endian"); + break; + + case PIZZA_THEME_GOOGLETEST: + pizza_io_printf("[==========] {red}F{yellow}o{yellow}s{green}s{blue}i{red}l {yellow}P{green}i{blue}z{red}z{yellow}a {green}S{blue}u{red}m{yellow}m{green}a{blue}r{red}y{reset}\n"); + pizza_io_printf("[----------] OS: %s, Endianess: %s\n", + system_info.os_name, endianness_info.is_little_endian ? "Little-endian" : "Big-endian"); + break; + + case PIZZA_THEME_UNITY: + pizza_io_printf("Unity Test Summary\n"); + pizza_io_printf("OS: %s, Endianess: %s\n", + system_info.os_name, endianness_info.is_little_endian ? "Little-endian" : "Big-endian"); + break; + + default: + pizza_io_printf("Unknown theme. Unable to display heading.\n"); + break; + } +} + +void fossil_pizza_summary(const fossil_pizza_engine_t* engine) { + if (!engine) return; + + fossil_pizza_summary_heading(engine); + fossil_pizza_summary_scoreboard(engine); + fossil_pizza_summary_timestamp(engine); +} + +// --- End / Cleanup --- +int32_t fossil_pizza_end(fossil_pizza_engine_t* engine) { + if (!engine) return FOSSIL_PIZZA_FAILURE; + for (size_t i = 0; i < engine->count; ++i) { + fossil_pizza_suite_t* suite = &engine->suites[i]; + if (suite->cases) { + for (size_t j = 0; j < suite->count; ++j) { + fossil_pizza_case_t* test_case = &suite->cases[j]; + if (test_case->teardown) { + test_case->teardown(); + } + } + pizza_sys_memory_free(suite->cases); + } + } + pizza_sys_memory_free(engine->suites); + return FOSSIL_PIZZA_SUCCESS; +} + +// -- Assume -- + +void pizza_test_assert_internal_output(const char *message, const char *file, int line, const char *func, int anomaly_count) { + // Output assertion failure based on theme + switch (G_PIZZA_THEME) { + case PIZZA_THEME_FOSSIL: + pizza_io_printf("{red,bold}Assertion failed:{reset} {yellow}%s{reset} {blue}(%s:%d in %s){reset}\n", + message, file, line, func); + if (anomaly_count > 0) { + pizza_io_printf("{yellow}Duplicate or similar assertion detected [Anomaly Count: %d]{reset}\n", anomaly_count); + } + break; + + case PIZZA_THEME_CATCH: + case PIZZA_THEME_DOCTEST: + pizza_io_printf("Assertion failed: %s (%s:%d in %s)\n", message, file, line, func); + if (anomaly_count > 0) { + pizza_io_printf("Duplicate or similar assertion detected [Anomaly Count: %d]\n", anomaly_count); + } + break; + + case PIZZA_THEME_CPPUTEST: + pizza_io_printf("[ASSERTION FAILED] %s (%s:%d in %s)\n", message, file, line, func); + if (anomaly_count > 0) { + pizza_io_printf("[DUPLICATE ASSERTION] Anomaly Count: %d\n", anomaly_count); + } + break; + + case PIZZA_THEME_TAP: + pizza_io_printf("not ok - Assertion failed: %s (%s:%d in %s)\n", message, file, line, func); + if (anomaly_count > 0) { + pizza_io_printf("# Duplicate or similar assertion detected [Anomaly Count: %d]\n", anomaly_count); + } + break; + + case PIZZA_THEME_GOOGLETEST: + pizza_io_printf("[ FAILED ] Assertion failed: %s (%s:%d in %s)\n", message, file, line, func); + if (anomaly_count > 0) { + pizza_io_printf("[ WARNING ] Duplicate or similar assertion detected [Anomaly Count: %d]\n", anomaly_count); + } + break; + + case PIZZA_THEME_UNITY: + pizza_io_printf("Unity Assertion Failed: %s (%s:%d in %s)\n", message, file, line, func); + if (anomaly_count > 0) { + pizza_io_printf("Unity Duplicate Assertion Detected [Anomaly Count: %d]\n", anomaly_count); + } + break; + + default: + pizza_io_printf("Assertion failed: %s (%s:%d in %s)\n", message, file, line, func); + if (anomaly_count > 0) { + pizza_io_printf("Duplicate or similar assertion detected [Anomaly Count: %d]\n", anomaly_count); + } + break; + } +} + +static int pizza_test_assert_internal_detect(const char *message, const char *file, int line, const char *func) { + static const char *last_message = null; // Store the last assertion message + static const char *last_file = null; // Store the last file name + static int last_line = 0; // Store the last line number + static const char *last_func = null; // Store the last function name + static int anomaly_count = 0; // Counter for anomaly detection + + // Check if the current assertion is the same or similar to the last one + if (last_message && strstr(message, last_message) != null && + last_file && pizza_io_cstr_compare(last_file, file) == 0 && + last_line == line && + last_func && pizza_io_cstr_compare(last_func, func) == 0) { + anomaly_count++; + } else { + anomaly_count = 0; // Reset anomaly count for new assertion + last_message = message; + last_file = file; + last_line = line; + last_func = func; + } + + return anomaly_count; +} + +void pizza_test_assert_internal(bool condition, const char *message, const char *file, int line, const char *func) { + _ASSERT_COUNT++; // Increment the assertion count + + if (!condition) { + int anomaly_count = pizza_test_assert_internal_detect(message, file, line, func); + + // Output assertion failure + pizza_test_assert_internal_output(message, file, line, func, anomaly_count); + + longjmp(test_jump_buffer, 1); // Jump back to test case failure handler + } +} + +// ********************************************************************************************* +// internal messages +// ********************************************************************************************* + +void _given(const char *description) { + if (description) { + switch (G_PIZZA_THEME) { + case PIZZA_THEME_FOSSIL: + pizza_io_printf("{blue}Given %s{reset}\n", description); + break; + case PIZZA_THEME_CATCH: + case PIZZA_THEME_DOCTEST: + pizza_io_printf("Given: %s\n", description); + break; + case PIZZA_THEME_CPPUTEST: + pizza_io_printf("[GIVEN] %s\n", description); + break; + case PIZZA_THEME_TAP: + pizza_io_printf("# Given: %s\n", description); + break; + case PIZZA_THEME_GOOGLETEST: + pizza_io_printf("[----------] Given: %s\n", description); + break; + case PIZZA_THEME_UNITY: + pizza_io_printf("Unity Given: %s\n", description); + break; + default: + pizza_io_printf("Given: %s\n", description); + break; + } + } +} + +void _when(const char *description) { + if (description) { + switch (G_PIZZA_THEME) { + case PIZZA_THEME_FOSSIL: + pizza_io_printf("{blue}When %s{reset}\n", description); + break; + case PIZZA_THEME_CATCH: + case PIZZA_THEME_DOCTEST: + pizza_io_printf("When: %s\n", description); + break; + case PIZZA_THEME_CPPUTEST: + pizza_io_printf("[WHEN] %s\n", description); + break; + case PIZZA_THEME_TAP: + pizza_io_printf("# When: %s\n", description); + break; + case PIZZA_THEME_GOOGLETEST: + pizza_io_printf("[----------] When: %s\n", description); + break; + case PIZZA_THEME_UNITY: + pizza_io_printf("Unity When: %s\n", description); + break; + default: + pizza_io_printf("When: %s\n", description); + break; + } + } +} + +void _then(const char *description) { + if (description) { + switch (G_PIZZA_THEME) { + case PIZZA_THEME_FOSSIL: + pizza_io_printf("{blue}Then %s{reset}\n", description); + break; + case PIZZA_THEME_CATCH: + case PIZZA_THEME_DOCTEST: + pizza_io_printf("Then: %s\n", description); + break; + case PIZZA_THEME_CPPUTEST: + pizza_io_printf("[THEN] %s\n", description); + break; + case PIZZA_THEME_TAP: + pizza_io_printf("# Then: %s\n", description); + break; + case PIZZA_THEME_GOOGLETEST: + pizza_io_printf("[----------] Then: %s\n", description); + break; + case PIZZA_THEME_UNITY: + pizza_io_printf("Unity Then: %s\n", description); + break; + default: + pizza_io_printf("Then: %s\n", description); + break; + } + } +} + +void _on_skip(const char *description) { + if (description) { + switch (G_PIZZA_THEME) { + case PIZZA_THEME_FOSSIL: + pizza_io_printf("{yellow}On Skip %s{reset}\n", description); + break; + case PIZZA_THEME_CATCH: + case PIZZA_THEME_DOCTEST: + pizza_io_printf("On Skip: %s\n", description); + break; + case PIZZA_THEME_CPPUTEST: + pizza_io_printf("[SKIP] %s\n", description); + break; + case PIZZA_THEME_TAP: + pizza_io_printf("# On Skip: %s\n", description); + break; + case PIZZA_THEME_GOOGLETEST: + pizza_io_printf("[ SKIPPED ] %s\n", description); + break; + case PIZZA_THEME_UNITY: + pizza_io_printf("Unity On Skip: %s\n", description); + break; + default: + pizza_io_printf("On Skip: %s\n", description); + break; + } + } +} diff --git a/code/logic/testing.c b/code/logic/testing.c deleted file mode 100644 index 9ff5c4b7..00000000 --- a/code/logic/testing.c +++ /dev/null @@ -1,1369 +0,0 @@ -/* - * ----------------------------------------------------------------------------- - * Project: Fossil Logic - * - * This file is part of the Fossil Logic project, which aims to develop high- - * performance, cross-platform applications and libraries. The code contained - * herein is subject to the terms and conditions defined in the project license. - * - * Author: Michael Gene Brockus (Dreamer) - * Date: 07/01/2024 - * - * Copyright (C) 2024 Fossil Logic. All rights reserved. - * ----------------------------------------------------------------------------- - */ -#include "fossil/test/testing.h" - -// Array of messages for each category -const char *sarcastic_messages[] = { - "Wow, no tests were run! What a productive day!", - "No tests to run, guess we're all just too good at writing code.", - "Congratulations, you've done absolutely nothing today.", - "Oh, look! No tests were executed. What an achievement!", - "Not a single test run, but hey, that's one way to keep things perfect!", - "All set for a day of zero productivity? Nice!", - "The test suite is empty, but hey, at least the code didn't break!", - "Zero tests executed. Clearly, you've mastered the art of doing nothing.", - "Great! We've made it through an entire test run without running a single test.", - "Isn't it great when there's nothing to test?", - "No tests today, must be a holiday!", - "Looks like the tests took a day off.", - "No tests? Guess it's time for a coffee break.", - "No tests executed. Time to celebrate doing nothing!", - "No tests? Must be a sign of perfection.", - "No tests run. Maybe tomorrow.", - "No tests? Guess we're just that good.", - "No tests today. Enjoy the free time!", - "No tests? Must be a coding miracle.", - "No tests executed. Time to relax.", - "No tests? Guess it's a lazy day.", - "No tests run. Must be a record.", - "No tests? Time to enjoy the silence.", - "No tests executed. Time to chill.", - "No tests? Must be a coding vacation.", - "No tests run. Time to take it easy.", - "No tests? Guess we're flawless.", - "No tests executed. Time to unwind.", - "No tests? Must be a coding break.", - "No tests run. Time to kick back.", - "No tests executed. What a surprise!", - "No tests? Guess we're all perfect coders.", - "No tests run. Time to take a nap.", - "No tests? Must be a coding dream.", - "No tests executed. Time to party!", - "No tests? Guess we're all geniuses.", - "No tests run. Time to celebrate!", - "No tests? Must be a coding legend.", - "No tests executed. Time to rejoice.", - "No tests? Guess we're all experts.", - "No tests run. Time to relax and enjoy.", - "No tests? Must be a coding utopia.", - "No tests executed. Time to have fun.", - "No tests? Guess we're all masters.", - "No tests run. Time to take a break.", - "No tests? Must be a coding paradise.", - "No tests executed. Time to chill out.", - "No tests? Guess we're all wizards.", - "No tests run. Time to unwind and relax.", - "No tests? Must be a coding heaven." -}; - -const char *humorous_messages[] = { - "Well, that was an epic failure! Better luck next time!", - "Whoops! Someone's definitely gonna need to debug that.", - "Looks like someone forgot to write the test cases.", - "Your tests failed, but at least you got closer to perfection... not.", - "Not all heroes wear capes. Some of them fail tests.", - "Don't worry, failure is just success in disguise. Or maybe not.", - "Well, that was awkward. Let's try again, shall we?", - "Looks like we've encountered a bug! Hope you've got a magnifying glass.", - "Your tests are taking a nap, but don't worry, we'll wake them up!", - "It's not a failure, it's just a learning experience! A very *expensive* one.", - "Oops! That didn't go as planned.", - "Well, that was a disaster. Time to debug!", - "Looks like we hit a snag. Time to fix it!", - "Your tests failed spectacularly. Congrats!", - "Well, that was a mess. Let's clean it up.", - "Oops! Something went wrong. Time to investigate.", - "Well, that was a flop. Better luck next time.", - "Looks like we have some bugs to squash.", - "Your tests failed. Time to roll up your sleeves.", - "Oops! That didn't work. Time to try again.", - "Well, that was a fail. Let's fix it.", - "Looks like we have some issues to resolve.", - "Your tests failed. Time to get to work.", - "Oops! That was a fail. Time to debug.", - "Well, that was a bust. Let's fix it.", - "Looks like we have some problems to solve.", - "Your tests failed. Time to figure it out.", - "Oops! That didn't go well. Time to debug.", - "Well, that was a fail. Let's try again.", - "Looks like we have some errors to fix.", - "Your tests failed. Time to troubleshoot.", - "Oops! That was unexpected. Time to investigate.", - "Well, that was a blunder. Let's correct it.", - "Looks like we have some debugging to do.", - "Your tests failed. Time to analyze the issue.", - "Oops! That didn't go as expected. Time to debug.", - "Well, that was a setback. Let's resolve it.", - "Looks like we have some fixing to do.", - "Your tests failed. Time to diagnose the problem.", - "Oops! That was a misstep. Time to debug.", - "Well, that was a glitch. Let's fix it.", - "Looks like we have some troubleshooting to do.", - "Your tests failed. Time to rectify the issue.", - "Oops! That was a hiccup. Time to debug.", - "Well, that was a malfunction. Let's repair it.", - "Looks like we have some correcting to do.", - "Your tests failed. Time to address the problem.", - "Oops! That was a slip-up. Time to debug.", - "Well, that was a snag. Let's fix it." -}; - -const char *great_news_messages[] = { - "Great news! All tests passed. You're a testing genius!", - "Success! Everything works as expected. Go celebrate!", - "You did it! All tests passed. You're officially a rock star!", - "Congrats, all tests passed! You've earned a gold star!", - "Woohoo! All tests passed with flying colors!", - "No bugs found today, you're on fire!", - "Amazing! You've got a perfect test suite. Keep it up!", - "Nice job! All tests passed. I think we've found the next coding superhero!", - "Fantastic! No issues at all, just pure success!", - "All tests passed, everything's awesome.", - "Great job! All tests passed with no issues.", - "Congratulations! All tests passed successfully.", - "Well done! All tests passed with flying colors.", - "Awesome! All tests passed without a hitch.", - "Fantastic! All tests passed with no problems.", - "Excellent! All tests passed with ease.", - "Superb! All tests passed without any issues.", - "Outstanding! All tests passed with no errors.", - "Brilliant! All tests passed with no failures.", - "Impressive! All tests passed with no bugs.", - "Wonderful! All tests passed with no glitches.", - "Terrific! All tests passed with no hiccups.", - "Marvelous! All tests passed with no snags.", - "Spectacular! All tests passed with no setbacks.", - "Phenomenal! All tests passed with no troubles.", - "Remarkable! All tests passed with no obstacles.", - "Incredible! All tests passed with no difficulties.", - "Stellar! All tests passed with no challenges.", - "Magnificent! All tests passed with no hurdles.", - "Fabulous! All tests passed with no impediments.", - "Perfect! All tests passed without any issues.", - "Great! All tests passed with no errors.", - "Wonderful! All tests passed with no problems.", - "Amazing! All tests passed with no failures.", - "Fantastic! All tests passed with no bugs.", - "Excellent! All tests passed with no glitches.", - "Superb! All tests passed with no hiccups.", - "Outstanding! All tests passed with no snags.", - "Brilliant! All tests passed with no setbacks.", - "Impressive! All tests passed with no troubles.", - "Wonderful! All tests passed with no obstacles.", - "Terrific! All tests passed with no difficulties.", - "Marvelous! All tests passed with no challenges.", - "Spectacular! All tests passed with no hurdles.", - "Phenomenal! All tests passed with no impediments.", - "Remarkable! All tests passed with no issues.", - "Incredible! All tests passed with no errors.", - "Stellar! All tests passed with no problems.", - "Magnificent! All tests passed with no failures.", - "Fabulous! All tests passed with no bugs." -}; - -const char *timeout_messages[] = { - "Some tests timed out. Please check the test cases.", - "Looks like some tests took too long to complete. Time to debug!", - "Uh-oh! Some tests took too long to run. Time to investigate!", - "Looks like some tests are still running. Did you forget to add a timeout?", - "Some tests are still in progress. Time to grab a coffee and debug!", - "Timeout alert! Some tests didn't finish in time.", - "Tests are taking longer than expected. Time to check for infinite loops!", - "Some tests exceeded the time limit. Let's optimize!", - "Tests are running slow. Time to profile and improve performance!", - "Timeout detected! Some tests need a closer look.", - "Tests are taking forever. Time to investigate!", - "Some tests are stuck. Time to debug!", - "Tests are running longer than expected. Time to check for bottlenecks!", - "Timeout warning! Some tests didn't complete in time.", - "Tests are still running. Time to check for performance issues!", - "Some tests are taking too long. Time to optimize!", - "Tests are timing out. Time to investigate the cause!", - "Timeout issue detected. Some tests need attention!", - "Tests are running slow. Time to improve efficiency!", - "Timeout alert! Some tests are not finishing in time.", - "Tests are taking too long. Time to debug and optimize!", - "Timeout detected. Some tests are running indefinitely!", - "Tests are exceeding the time limit. Time to investigate!", - "Timeout issue. Some tests are not completing in time!", - "Tests are running longer than expected. Time to debug!", - "Timeout warning. Some tests are taking too long!", - "Tests are still running. Time to check for issues!", - "Timeout detected. Some tests need optimization!", - "Tests are taking forever. Time to investigate the cause!", - "Timeout alert. Some tests are not finishing in time!", - "Tests are taking too long. Time to find the bottleneck!", - "Timeout detected. Some tests need a performance review!", - "Tests are running indefinitely. Time to debug!", - "Timeout warning. Some tests are stuck!", - "Tests are taking too long. Time to optimize performance!", - "Timeout issue detected. Some tests need a closer look!", - "Tests are running slow. Time to investigate!", - "Timeout alert. Some tests are not completing!", - "Tests are taking too long. Time to debug!", - "Timeout detected. Some tests are running too long!", - "Tests are exceeding the time limit. Time to optimize!", - "Timeout issue. Some tests are not finishing!", - "Tests are running longer than expected. Time to debug!", - "Timeout warning. Some tests are taking too long!", - "Tests are still running. Time to check for issues!", - "Timeout detected. Some tests need optimization!", - "Tests are taking forever. Time to investigate the cause!", - "Timeout alert. Some tests are not finishing in time!", - "Tests are taking too long. Time to find the bottleneck!" -}; - -// Suggestions arrays based on different outcomes -const char *empty_suite_suggestions[] = { - "Check if your test suite has defined tests.", - "Make sure your test cases are properly configured.", - "Review the test configuration to ensure it’s correct.", - "Verify that your test suite is correctly initialized.", - "Check if test scripts are properly linked.", - "Ensure your testing framework is correctly set up.", - "Double-check if your test directories and files are valid.", - "Ensure the tests are compiled correctly.", - "Make sure tests are registered with the test runner.", - "Verify that all necessary modules are loaded for testing.", - "Ensure no tests are excluded or skipped by mistake.", - "Check if you’ve specified the correct test entry points.", - "Review whether your test framework supports the required features.", - "Confirm that all test cases are included in the build process.", - "Verify that no test dependencies are missing.", - "Ensure that all required test resources are available.", - "Check if any testing tools need configuration.", - "Make sure your test suite is executed with the right parameters.", - "Confirm the right environment variables are set for tests.", - "Ensure that tests are properly isolated from the system environment.", - "Check if the tests are being executed in the expected environment.", - "Verify that your test suite hasn’t been inadvertently disabled.", - "Check if your test suite has been corrupted or altered.", - "Make sure your testing environment is up-to-date.", - "Ensure that the test results are being captured correctly.", - "Verify that your testing platform supports your test types.", - "Review if your test framework is up-to-date and compatible.", - "Ensure tests are not being skipped due to unsupported features.", - "Check if you have proper logging or error handling in place.", - "Verify the test output directory is correctly specified.", - "Check if the test suite outputs meaningful logs or results.", - "Ensure that all test dependencies are installed and available.", - "Confirm that your build system includes the necessary test files.", - "Check for any overlooked configuration settings in the test suite.", - "Ensure there are no naming conflicts in the test directory structure.", - "Review your test suite’s memory and resource usage.", - "Verify that tests are not blocked due to missing permissions.", - "Check if tests are running with the required privileges.", - "Make sure your test runner is executing tests in the right order.", - "Ensure the tests don’t require external resources unavailable in the environment.", - "Verify that no steps in your test suite are being skipped inadvertently.", - "Check if there’s a version mismatch between your test framework and environment.", - "Ensure that you have enough logging output to debug your tests.", - "Make sure your test environment matches the production environment closely." -}; - -const char *failure_suggestions[] = { - "Look into the test failures and their root causes.", - "Check for bugs, missing dependencies, or misconfigured tests.", - "Examine the test environment for potential issues.", - "Review the test case logic and expected behavior.", - "Consider adding more edge cases to capture hidden bugs.", - "Check if the failed test cases have valid input data.", - "Verify that the expected output is correct for failed tests.", - "Ensure there are no race conditions in your tests.", - "Review if there are any memory or resource leaks.", - "Double-check the test setup and teardown procedures.", - "Look for dependency issues or configuration mismatches.", - "Verify the test environment matches the expected conditions.", - "Make sure the test dependencies are properly installed and up to date.", - "Check if recent changes have affected the test behavior.", - "Consider increasing the verbosity of your test logs for debugging.", - "Ensure tests are not dependent on unpredictable external factors.", - "Review the error messages and stack traces for more details.", - "Consider running the failed tests in isolation to identify causes.", - "Ensure you’re not testing with outdated or incorrect input data.", - "Check if the failure is due to a logic error in the application.", - "Test the system under a variety of conditions to expose hidden bugs.", - "Try different configurations or platforms to identify the issue.", - "Check the system’s resource usage to ensure it isn’t causing the failure.", - "Verify that the failure doesn’t result from a system or platform limitation.", - "Review recent updates in the codebase that might impact test behavior.", - "Test the specific test case individually and ensure correct behavior.", - "Check for inconsistencies between the test case and actual implementation.", - "Ensure that third-party dependencies are compatible with your tests.", - "Re-examine the test case for edge cases that were missed.", - "Look into whether any time-dependent issues are causing failures.", - "Check if there are any environment-specific issues affecting tests.", - "Test the system with smaller, isolated inputs to identify bugs.", - "Investigate any external resources required for tests to work properly.", - "Try running the test suite on a different platform or environment.", - "Look into possible memory corruption or incorrect memory handling.", - "Check if the test’s timing constraints or limits are too tight.", - "Review error-handling mechanisms to ensure proper feedback.", - "Consider testing with more realistic inputs to simulate real-world behavior.", - "Verify if test failures are isolated or part of a larger systemic issue.", - "Make sure test cases correctly handle different user scenarios.", - "Check if failures are related to thread safety or parallel execution.", - "Consider running tests under various loads to stress the system.", - "Try testing with different versions of dependencies to rule out compatibility issues." -}; - -const char *success_suggestions[] = { - "Great job! Keep adding tests to cover edge cases.", - "Fantastic! Consider adding performance and stress tests.", - "Success! Now, look at adding additional tests for edge cases.", - "Well done! You’re on the right track, keep it up.", - "Good job! Now, consider reviewing code for possible optimizations.", - "Excellent work! Think about testing with larger input datasets.", - "Well done! Consider testing under load to simulate real-world conditions.", - "Great! Time to refine the code and improve error handling.", - "Nice work! Next, consider checking for potential memory leaks.", - "Awesome! Try adding tests for concurrency and thread safety.", - "Great success! Start testing for edge cases and unexpected inputs.", - "Well done! Ensure tests are comprehensive and cover every possible scenario.", - "Excellent! Look into integrating continuous integration for automated tests.", - "Fantastic job! Start testing the performance of the system under stress.", - "Great work! Keep iterating to improve coverage and test completeness.", - "Awesome! Make sure to run tests in various environments to ensure compatibility.", - "Well done! Try testing with more complex data structures and inputs.", - "Excellent! Don’t forget to add edge cases and potential failure modes.", - "Nice! Keep up the great work and start testing corner cases.", - "Great! Keep building up your test coverage to ensure robust software.", - "Nice! Ensure that your tests cover all relevant code paths.", - "Fantastic! Consider using mock objects to improve test isolation.", - "Awesome! Now look into running automated tests on multiple platforms.", - "Excellent! Test your code under different load conditions for scalability.", - "Great! Review your test output to ensure all cases are covered.", - "Well done! Think about testing for long-term stability and memory usage.", - "Fantastic! Time to start profiling the application’s performance.", - "Nice work! Think about optimizing the system after completing testing.", - "Awesome! Ensure your tests reflect real-world usage patterns.", - "Great! Start testing the system with different network configurations.", - "Nice! Keep up the good work by testing boundary cases for reliability.", - "Excellent! Begin testing different configurations for system flexibility.", - "Fantastic! Add integration tests to ensure different components work together.", - "Great! Consider using fuzz testing to uncover hidden bugs.", - "Well done! Add regression tests to catch issues with new changes.", - "Awesome! Time to start integrating tests with your build pipeline.", - "Excellent! Keep improving your test cases to cover all edge conditions.", - "Nice! Think about automating test execution for faster feedback.", - "Fantastic! Keep testing under high load to ensure stability and performance.", - "Awesome! Consider testing the system's scalability under high traffic.", - "Great! Don’t forget to validate that the system handles all error conditions.", - "Nice! Keep testing in various environments for cross-platform compatibility.", - "Well done! Think about adding security tests for potential vulnerabilities." -}; - -const char *timeout_suggestions[] = { - "Check resource usage and adjust timeout values.", - "Investigate slow-running tests and optimize them.", - "Consider breaking large tests into smaller ones to avoid timeouts.", - "Check for any environmental factors affecting test performance.", - "Consider adjusting the test parameters to reduce execution time.", - "Examine the test case to ensure there are no infinite loops or deadlocks.", - "Look into possible network issues if tests depend on network resources.", - "Consider profiling the test to identify performance bottlenecks.", - "Check if the system is running out of memory or resources during tests.", - "Try running tests with different configurations to pinpoint the cause.", - "Investigate test dependencies to ensure they don’t block execution.", - "Ensure your system is not overloaded with other processes during tests.", - "Look into optimizing algorithms to reduce test execution time.", - "Consider running tests in parallel to speed up overall execution.", - "Check if the system has enough hardware resources to handle tests.", - "Test with smaller data sets to see if the timeout issue persists.", - "Look into reducing unnecessary waits or sleep intervals in the test cases.", - "Check if the timeout values are set appropriately for different test cases.", - "Investigate if any external services are slowing down the tests.", - "Ensure tests are isolated and not dependent on external factors.", - "Consider adjusting the execution environment to improve performance.", - "Make sure tests are not waiting on unnecessary synchronization.", - "Try optimizing your code to avoid long-running operations in tests.", - "Check if there are any operations that can be optimized for speed.", - "Investigate if the timeout is caused by network latency or delays.", - "Ensure that timeout values reflect the actual test execution times.", - "Break tests into smaller chunks to better manage timeouts.", - "Check the system logs for errors or warnings during test execution.", - "Look into improving the algorithmic complexity of long-running tests.", - "Consider adding more logging to track where the test is stalling.", - "Ensure the environment is clean and not affecting test performance.", - "Try testing on a different platform to compare execution times.", - "Look into system-wide performance issues affecting the tests.", - "Test the system under a reduced load to identify performance bottlenecks.", - "Consider adding more granular timeout checks to diagnose issues.", - "Investigate whether background processes are affecting test performance.", - "Test in a more controlled environment to eliminate external influences.", - "Consider using profiling tools to identify long-running sections in the tests.", - "Check if running tests in isolation resolves the timeout issues.", - "Look into optimizing data access patterns in long-running tests.", - "Consider breaking tests into independent smaller tests to improve performance.", - "Investigate if there are unnecessary dependencies in the test cases.", - "Consider using mock data to speed up test execution and avoid delays.", - "Check if large input sizes are contributing to the timeout.", - "Investigate if your timeout limits need adjustment for specific tests." -}; - -enum { - _FOSSIL_TEST_RESPONSE_LENGTH = 50 -}; - -static const char *FOSSIL_TEST_OPTIONS[] = { - "--version - Displays the current version of Fossil Test\n", - "--help - Shows help message with usage\n", - "--info - Displays detailed information about the test run\n" -}; - -static const char *FOSSIL_TEST_COMMANDS[] = { - "reverse [enable|disable] - Enables or disables reverse order of test execution\n", - "repeat [count] - Repeats the test suite a specified number of times\n", - "shuffle [enable|disable] - Enables or disables shuffling of test execution order\n", - "dry-run [enable|disable] - Enables or disables dry-run mode\n", - "summary [option name ] - Sets the summary mode (plain, ci, jellyfish)\n", - "color [enable|disable] - Enables or disables colored output\n", - "format [option name ] - Output format options (plain, ci, jellyfish)\n" -}; - -static const char *FOSSIL_TEST_VERSION = "1.2.0"; // Version of Fossil Test -static const char *FOSSIL_TEST_AUTHOR = "Michael Gene Brockus (Dreamer)"; // Author of Fossil Test -static const char *FOSSIL_TEST_LICENSE = "Mozilla Public License 2.0"; // License of Fossil Test - -jmp_buf test_jump_buffer; // This will hold the jump buffer for longjmp -static int _ASSERT_COUNT = 0; // Counter for the number of assertions - -// ***************************************************************************** -// Helper function declarations -// ***************************************************************************** - -// Custom implementation of strdup to avoid warnings on some platforms -char *_custom_fossil_test_strdup(const char *str) { - size_t len = strlen(str) + 1; - char *new_str = (char *)malloc(len); - if (new_str == NULL) { - return NULL; - } - memcpy(new_str, str, len); - return new_str; -} - -// ***************************************************************************** -// Function declarations -// ***************************************************************************** - -// Initialize the options structure -fossil_test_options_t fossil_test_init_options(void) { - fossil_test_options_t options; - options.show_version = false; - options.show_help = false; - options.show_info = false; - options.reverse = false; - options.repeat_enabled = false; - options.repeat_count = 1; - options.shuffle_enabled = false; - options.dry_run = false; - options.summary = FOSSIL_TEST_SUMMARY_PLAIN; - options.color_output = true; // default to true for better UX - options.format = FOSSIL_TEST_FORMAT_PLAIN; // Default format is plain - return options; -} - -void usage_info(void) { - internal_test_puts("{cyan,bold}==================================================\n"); - internal_test_puts("{white,bold}\tUsage: fossil [options] [command]\n"); - internal_test_puts("{cyan,bold}==================================================\n\n"); - - internal_test_puts("{yellow,bold}Options:\n"); - for (size_t i = 0; i < sizeof(FOSSIL_TEST_OPTIONS) / sizeof(FOSSIL_TEST_OPTIONS[0]); i++) { - internal_test_printf(" {green}> %s\n", FOSSIL_TEST_OPTIONS[i]); - } - - internal_test_puts("\n{yellow,bold}Commands:\n"); - for (size_t i = 0; i < sizeof(FOSSIL_TEST_COMMANDS) / sizeof(FOSSIL_TEST_COMMANDS[0]); i++) { - internal_test_printf(" {green}> %s\n", FOSSIL_TEST_COMMANDS[i]); - } - - internal_test_puts("\n{cyan,bold}==================================================\n"); -} - -void version_info(void) { - internal_test_puts("{magenta,bold}==================================================\n"); - internal_test_puts("{white,bold}\tFossil Logic Test Framework\n"); - internal_test_puts("{magenta,bold}==================================================\n"); - - internal_test_printf(" {cyan}Version: %s\n", FOSSIL_TEST_VERSION); - internal_test_printf(" {cyan}Author : %s\n", FOSSIL_TEST_AUTHOR); - internal_test_printf(" {cyan}License: %s\n", FOSSIL_TEST_LICENSE); - - internal_test_puts("{magenta,bold}==================================================\n"); -} - -// Command pallet parser -fossil_test_options_t fossil_options_parse(int argc, char **argv) { - fossil_test_options_t options = fossil_test_init_options(); - - for (int i = 1; i < argc; i++) { - if (strcmp(argv[i], "--version") == 0) { - options.show_version = true; - } else if (strcmp(argv[i], "--help") == 0) { - options.show_help = true; - } else if (strcmp(argv[i], "--info") == 0) { - options.show_info = true; - } else if (strcmp(argv[i], "reverse") == 0) { - if (i + 1 < argc && strcmp(argv[i + 1], "enable") == 0) { - options.reverse = true; - i++; - } else if (i + 1 < argc && strcmp(argv[i + 1], "disable") == 0) { - options.reverse = false; - i++; - } - } else if (strcmp(argv[i], "repeat") == 0) { - options.repeat_enabled = true; - if (i + 1 < argc && argv[i + 1][0] != '-') { - options.repeat_count = atoi(argv[i + 1]); - i++; - } - } else if (strcmp(argv[i], "shuffle") == 0) { - if (i + 1 < argc && strcmp(argv[i + 1], "enable") == 0) { - options.shuffle_enabled = true; - i++; - } else if (i + 1 < argc && strcmp(argv[i + 1], "disable") == 0) { - options.shuffle_enabled = false; - i++; - } - } else if (strcmp(argv[i], "dry-run") == 0) { - if (i + 1 < argc && strcmp(argv[i + 1], "enable") == 0) { - options.dry_run = true; - i++; - } else if (i + 1 < argc && strcmp(argv[i + 1], "disable") == 0) { - options.dry_run = false; - i++; - } - } else if (strcmp(argv[i], "summary") == 0) { - if (i + 1 < argc) { - if (strcmp(argv[i + 1], "plain") == 0) { - options.summary = FOSSIL_TEST_SUMMARY_PLAIN; - } else if (strcmp(argv[i + 1], "ci") == 0) { - options.summary = FOSSIL_TEST_SUMMARY_CI; - } else if (strcmp(argv[i + 1], "jellyfish") == 0) { - options.summary = FOSSIL_TEST_SUMMARY_JELLYFISH; - } - i++; - } - } else if (strcmp(argv[i], "color") == 0) { - if (i + 1 < argc && strcmp(argv[i + 1], "enable") == 0) { - options.color_output = true; - internal_test_set_color_output(options.color_output); - i++; - } else if (i + 1 < argc && strcmp(argv[i + 1], "disable") == 0) { - options.color_output = false; - internal_test_set_color_output(options.color_output); - i++; - } - } else if (strcmp(argv[i], "format") == 0) { - if (i + 1 < argc) { - if (strcmp(argv[i + 1], "plain") == 0) { - options.format = FOSSIL_TEST_FORMAT_PLAIN; - } else if (strcmp(argv[i + 1], "ci") == 0) { - options.format = FOSSIL_TEST_FORMAT_CI; - } else if (strcmp(argv[i + 1], "jellyfish") == 0) { - options.format = FOSSIL_TEST_FORMAT_JELLYFISH; - } - i++; - } - } - } - - return options; -} - -// Function to reverse the order of test cases in the linked list -void reverse_test_cases(fossil_test_case_t **test_cases) { - if (!test_cases || !*test_cases) { - return; // No test cases to reverse - } - - fossil_test_case_t *prev = NULL; - fossil_test_case_t *current = *test_cases; - fossil_test_case_t *next = NULL; - - // Traverse the linked list and reverse the 'next' pointers - while (current) { - next = current->next; // Store the next node - current->next = prev; // Reverse the current node's pointer - prev = current; // Move prev and current one step forward - current = next; - } - - // After the loop, prev will point to the new head of the list - *test_cases = prev; -} - -// Function to shuffle test cases using the Fisher-Yates algorithm -void shuffle_test_cases(fossil_test_case_t **test_cases) { - if (!test_cases || !*test_cases) { - return; // No test cases to shuffle - } - - // Calculate the length of the linked list (number of test cases) - int n = 0; - fossil_test_case_t *current = *test_cases; - while (current) { - n++; - current = current->next; - } - - // If there is only one or zero test cases, no need to shuffle - if (n <= 1) { - return; - } - - // Create an array to hold the test cases - fossil_test_case_t **array = malloc(sizeof(fossil_test_case_t *) * n); - if (!array) { - return; // Memory allocation failed - } - - // Fill the array with the test case pointers - current = *test_cases; - for (int i = 0; i < n; i++) { - array[i] = current; - current = current->next; - } - - // Shuffle the array using the Fisher-Yates algorithm - srand(time(NULL)); // Seed the random number generator - - for (int i = n - 1; i > 0; i--) { - // Generate a random index between 0 and i - int j = rand() % (i + 1); - - // Swap the elements - fossil_test_case_t *temp = array[i]; - array[i] = array[j]; - array[j] = temp; - } - - // Rebuild the linked list from the shuffled array - for (int i = 0; i < n - 1; i++) { - array[i]->next = array[i + 1]; - } - array[n - 1]->next = NULL; // Last test case points to NULL - - // Update the original test cases pointer - *test_cases = array[0]; - - // Free the array after use - free(array); -} - -// Creates and returns a new test suite -fossil_test_suite_t* fossil_test_create_suite(const char *name) { - if (!name) { - return NULL; - } - - fossil_test_suite_t *suite = (fossil_test_suite_t*)malloc(sizeof(fossil_test_suite_t)); - if (!suite) { - return NULL; - } - - suite->name = name; - suite->suite_setup_func = NULL; - suite->suite_teardown_func = NULL; - suite->tests = NULL; - suite->next = NULL; - return suite; -} - -// Registers a test suite in the environment -void fossil_test_register_suite(fossil_test_env_t *env, fossil_test_suite_t *suite) { - if (!env || !suite) { - return; - } - - suite->next = env->test_suites; - env->test_suites = suite; - if (env->options.show_info) { - internal_test_printf("{blue,bold}Registered test suite: %s\n{reset}", suite->name); - } -} - -// Adds a test case to a suite -void fossil_test_add_case(fossil_test_suite_t *suite, fossil_test_case_t *test_case) { - if (!suite || !test_case) { - return; - } - - test_case->next = suite->tests; - suite->tests = test_case; -} - -// Removes and frees a test case from a suite -void fossil_test_remove_case(fossil_test_suite_t *suite, fossil_test_case_t *test_case) { - if (!suite || !test_case) { - return; - } - - fossil_test_case_t *prev = NULL; - fossil_test_case_t *curr = suite->tests; - - while (curr) { - if (curr == test_case) { - if (prev) { - prev->next = curr->next; - } else { - suite->tests = curr->next; - } - free(curr); - return; - } - prev = curr; - curr = curr->next; - } -} - -// Setup for individual test case -void fossil_test_case_setup(fossil_test_case_t *test_case) { - if (test_case && test_case->setup_func) { - test_case->setup_func(); - } -} - -// Teardown for individual test case -void fossil_fossil_test_case_teardown(fossil_test_case_t *test_case) { - if (test_case && test_case->teardown_func) { - test_case->teardown_func(); - } -} - -// Runs a test suite -// Runs a test suite -void fossil_test_run_suite(fossil_test_suite_t *suite, fossil_test_env_t *env) { - if (!suite || !env) { - return; - } - - if (env->options.show_info) { - switch (env->options.format) { - case FOSSIL_TEST_FORMAT_CI: - internal_test_printf("[SUITE] %s\n", suite->name); - break; - case FOSSIL_TEST_FORMAT_JELLYFISH: - internal_test_printf("{brightblue}[Suite: %s]{reset}\n", suite->name); - break; - case FOSSIL_TEST_FORMAT_PLAIN: - default: - internal_test_printf("{blue,bold}Running suite: %s{reset}\n", suite->name); - break; - } - } - - if (env->options.shuffle_enabled) { - shuffle_test_cases(&suite->tests); - } - - if (env->options.reverse) { - reverse_test_cases(&suite->tests); - } - - if (suite->suite_setup_func) { - suite->suite_setup_func(); - } - - clock_t suite_start = clock(); - - fossil_test_case_t *current_test = suite->tests; - while (current_test) { - fossil_test_run_case(current_test, env); - current_test = current_test->next; - } - - clock_t suite_end = clock(); - double total_execution_time = (double)(suite_end - suite_start) / CLOCKS_PER_SEC; - - if (suite->suite_teardown_func) { - suite->suite_teardown_func(); - } - - if (env->options.show_info) { - switch (env->options.format) { - case FOSSIL_TEST_FORMAT_CI: - internal_test_printf("[SUITE DONE] %s (%.3fs)\n", suite->name, total_execution_time); - break; - case FOSSIL_TEST_FORMAT_JELLYFISH: - internal_test_printf("{cyan}⏱ Suite %s completed in %.3f seconds{reset}\n", suite->name, total_execution_time); - break; - case FOSSIL_TEST_FORMAT_PLAIN: - default: - internal_test_printf("{cyan}Total execution time for suite %s: %.3f seconds{reset}\n", suite->name, total_execution_time); - break; - } - } -} - -// Internal function to handle assertions with anomaly detection -void fossil_test_assert_internal(bool condition, const char *message, const char *file, int line, const char *func) { - static const char *last_message = NULL; // Store the last assertion message - static const char *last_file = NULL; // Store the last file name - static int last_line = 0; // Store the last line number - static const char *last_func = NULL; // Store the last function name - static int anomaly_count = 0; // Counter for anomaly detection - - _ASSERT_COUNT++; // Increment the assertion count - - if (!condition) { - // Check if the current assertion is the same or similar to the last one - if (last_message && strstr(message, last_message) != NULL && - last_file && strcmp(last_file, file) == 0 && - last_line == line && - last_func && strcmp(last_func, func) == 0) { - anomaly_count++; - internal_test_printf("{yellow}Duplicate or similar assertion detected: %s (%s:%d in %s) [Anomaly Count: %d]{reset}\n", - message, file, line, func, anomaly_count); - } else { - anomaly_count = 0; // Reset anomaly count for new assertion - last_message = message; - last_file = file; - last_line = line; - last_func = func; - internal_test_printf("{red}Assertion failed: %s (%s:%d in %s){reset}\n", message, file, line, func); - } - - longjmp(test_jump_buffer, 1); // Jump back to test case failure handler - } -} - -// Runs a test case -void fossil_test_run_case(fossil_test_case_t *test_case, fossil_test_env_t *env) { - if (!test_case || !env) return; - - if (env->options.dry_run) { - internal_test_printf("{purple}Dry run mode enabled. No tests will be executed.{reset}\n"); - return; - } - - test_case->status = TEST_STATUS_PASS; - fossil_test_case_setup(test_case); - - _ASSERT_COUNT = 0; - clock_t start_iter = clock(); - double timeout_seconds = 180.0; - - if (setjmp(env->env) == 0) { - for (int i = 0; i < env->options.repeat_count; i++) { - test_case->test_func(); - - clock_t now = clock(); - double elapsed_seconds = (double)(now - start_iter) / CLOCKS_PER_SEC; - if (elapsed_seconds > timeout_seconds) { - test_case->status = TEST_STATUS_TTIMEOUT; - break; - } - } - } else { - test_case->status = TEST_STATUS_FAIL; - } - - clock_t end_iter = clock(); - test_case->execution_time = (double)(end_iter - start_iter) / CLOCKS_PER_SEC; - fossil_fossil_test_case_teardown(test_case); - - // Output based on format - switch (test_case->status) { - case TEST_STATUS_PASS: - if (env->options.show_info) { - switch (env->options.format) { - case FOSSIL_TEST_FORMAT_PLAIN: - internal_test_printf("{green}PASSED: %s (%.3fs){reset}\n", test_case->name, test_case->execution_time); - break; - case FOSSIL_TEST_FORMAT_CI: - internal_test_printf("::notice file=%s,line=0::PASSED: %s (%.3fs)\n", - test_case->name, test_case->name, test_case->execution_time); - break; - case FOSSIL_TEST_FORMAT_JELLYFISH: - internal_test_printf("{green}✔ PASS: {blue}%s {dim}(%.3fs){reset}\n", - test_case->name, test_case->execution_time); - break; - } - } - env->pass_count++; - break; - - case TEST_STATUS_FAIL: - env->fail_count++; - switch (env->options.format) { - case FOSSIL_TEST_FORMAT_PLAIN: - internal_test_printf("{red}FAILED: %s{reset}\n", test_case->name); - break; - case FOSSIL_TEST_FORMAT_CI: - internal_test_printf("{red}::error file=%s,line=0::FAILED: %s{reset}\n", - test_case->name, test_case->name); - break; - case FOSSIL_TEST_FORMAT_JELLYFISH: - internal_test_printf("{red}✖ FAIL: {blue}%s{reset}\n", test_case->name); - if (test_case->failure_message) { - internal_test_printf("{red} → Reason: %s{reset}\n", test_case->failure_message); - } - break; - } - break; - - case TEST_STATUS_SKIP: - env->skip_count++; - switch (env->options.format) { - case FOSSIL_TEST_FORMAT_PLAIN: - internal_test_printf("{yellow}SKIPPED: %s{reset}\n", test_case->name); - break; - case FOSSIL_TEST_FORMAT_CI: - internal_test_printf("{yellow}::warning file=%s,line=0::SKIPPED: %s{reset}\n", - test_case->name, test_case->name); - break; - case FOSSIL_TEST_FORMAT_JELLYFISH: - internal_test_printf("{yellow}⚠ SKIP: {blue}%s{reset}\n", test_case->name); - break; - } - break; - - case TEST_STATUS_TTIMEOUT: - env->timeout_count++; - switch (env->options.format) { - case FOSSIL_TEST_FORMAT_PLAIN: - internal_test_printf("{orange}TIMEOUT: %s\n{reset}", test_case->name); - break; - case FOSSIL_TEST_FORMAT_CI: - internal_test_printf("{orange}::error file=%s,line=0::TIMEOUT: %s{reset}\n", - test_case->name, test_case->name); - break; - case FOSSIL_TEST_FORMAT_JELLYFISH: - internal_test_printf("{orange}⏳ TIMEOUT: {blue}%s{reset}\n", test_case->name); - break; - } - break; - - default: - env->unexpected_count++; - internal_test_printf("{magenta}Unexpected test result for: %s{reset}\n", test_case->name); - break; - } -} - -// Runs all the test suites -void fossil_test_run_all(fossil_test_env_t *env) { - if (!env) { - return; - } - - if (env->options.dry_run) { - internal_test_printf("{purple}Dry run mode enabled. No tests will be executed.{reset}\n"); - return; - } - - fossil_test_suite_t *current_suite = env->test_suites; - - while (current_suite) { - fossil_test_run_suite(current_suite, env); - current_suite = current_suite->next; - } -} - -// Initializes the test environment -void fossil_test_init(fossil_test_env_t *env, int argc, char **argv) { - if (!env) { - return; - } - - env->options = fossil_options_parse(argc, argv); - if (env->options.show_version) { - version_info(); - exit(EXIT_SUCCESS); - } - - if (env->options.show_help) { - usage_info(); - exit(EXIT_SUCCESS); - } - - env->pass_count = 0; - env->fail_count = 0; - env->skip_count = 0; - env->empty_count = 0; - env->total_tests = 0; - env->timeout_count = 0; - env->start_execution_time = clock(); - env->end_execution_time = 0.0; - env->unexpected_count = 0; - env->test_suites = NULL; - - if (env->options.dry_run) { - internal_test_printf("{purple}Dry run mode enabled. No tests will be executed or evaluated.{reset}\n"); - return; - } -} - -void fossil_test_comment(fossil_test_env_t *env) { - if (!env) { - return; - } - - // Dynamic comment based on test result - switch (env->options.summary) { - case FOSSIL_TEST_SUMMARY_PLAIN: - // Plain Mode: Simple comments without extra formatting - if (env->pass_count > 0 && env->fail_count == 0 && env->timeout_count == 0) { - internal_test_printf("{cyan}Comment: %s{reset}\n", great_news_messages[rand() % (sizeof(great_news_messages) / sizeof(great_news_messages[0]))]); - } else if (env->fail_count > 0) { - internal_test_printf("{red}Comment: %s{reset}\n", humorous_messages[rand() % (sizeof(humorous_messages) / sizeof(humorous_messages[0]))]); - } else if (env->timeout_count > 0) { - internal_test_printf("{orange}Comment: %s{reset}\n", timeout_messages[rand() % (sizeof(timeout_messages) / sizeof(timeout_messages[0]))]); - } else if (env->pass_count == 0 && env->fail_count == 0 && env->timeout_count == 0) { - internal_test_printf("{cyan}Comment: %s{reset}\n", sarcastic_messages[rand() % (sizeof(sarcastic_messages) / sizeof(sarcastic_messages[0]))]); - } else { - internal_test_printf("{cyan}Comment: The test results are mixed. Consider analyzing individual test cases to uncover underlying issues.{reset}\n"); - } - break; - - case FOSSIL_TEST_SUMMARY_CI: - // CI Mode: CI-friendly comments, typically no extra decoration, focused on results - if (env->pass_count > 0 && env->fail_count == 0 && env->timeout_count == 0) { - internal_test_printf("COMMENT=Success! %s\n", great_news_messages[rand() % (sizeof(great_news_messages) / sizeof(great_news_messages[0]))]); - } else if (env->fail_count > 0) { - internal_test_printf("COMMENT=Oops, there were failures: %s\n", humorous_messages[rand() % (sizeof(humorous_messages) / sizeof(humorous_messages[0]))]); - } else if (env->timeout_count > 0) { - internal_test_printf("COMMENT=Timeout encountered: %s\n", timeout_messages[rand() % (sizeof(timeout_messages) / sizeof(timeout_messages[0]))]); - } else if (env->pass_count == 0 && env->fail_count == 0 && env->timeout_count == 0) { - internal_test_printf("COMMENT=No results! %s\n", sarcastic_messages[rand() % (sizeof(sarcastic_messages) / sizeof(sarcastic_messages[0]))]); - } else { - internal_test_printf("COMMENT=Mixed results: Consider reviewing individual test cases.\n"); - } - break; - - case FOSSIL_TEST_SUMMARY_JELLYFISH: - // Jellyfish Mode: Playful, humorous, or insightful with additional context - if (env->pass_count > 0 && env->fail_count == 0 && env->timeout_count == 0) { - internal_test_printf("{blue}{bold}Great News!{reset} {cyan}%s{reset}\n", great_news_messages[rand() % (sizeof(great_news_messages) / sizeof(great_news_messages[0]))]); - } else if (env->fail_count > 0) { - internal_test_printf("{red}{bold}Oops, Something Went Wrong!{reset} {orange}%s{reset}\n", humorous_messages[rand() % (sizeof(humorous_messages) / sizeof(humorous_messages[0]))]); - internal_test_printf("{red}Failure detected, please review test logs for more information.{reset}\n"); - } else if (env->timeout_count > 0) { - internal_test_printf("{orange}{bold}Timeouts Happened!{reset} {yellow}%s{reset}\n", timeout_messages[rand() % (sizeof(timeout_messages) / sizeof(timeout_messages[0]))]); - internal_test_printf("{yellow}It might be worth investigating system performance or external factors.{reset}\n"); - } else if (env->pass_count == 0 && env->fail_count == 0 && env->timeout_count == 0) { - internal_test_printf("{cyan}{bold}No Results?{reset} {magenta}%s{reset}\n", sarcastic_messages[rand() % (sizeof(sarcastic_messages) / sizeof(sarcastic_messages[0]))]); - internal_test_printf("{magenta}Perhaps there were no tests run or something went awry!{reset}\n"); - } else { - internal_test_printf("{blue}{italic}Mixed Results Detected!{reset} {green}A closer look at individual test cases might reveal more details.{reset}\n"); - } - break; - - default: - internal_test_printf("{red}Unknown summary mode.{reset}\n"); - break; - } -} - -void fossil_test_analyze(fossil_test_env_t *env) { - if (!env) { - return; - } - - fossil_test_suite_t *suite = env->test_suites; - while (suite) { - fossil_test_case_t *test = suite->tests; - while (test) { - if (test->status == TEST_STATUS_SKIP) { - env->skip_count++; // Increment skipped count directly from env - } - test = test->next; - } - suite = suite->next; - } - - // Total tests count - int total_tests = env->pass_count + env->fail_count + env->skip_count + env->timeout_count; - - // Calculate success rate and other statistics - double success_rate = (double)env->pass_count / (double)total_tests * 100; - double failure_rate = (double)env->fail_count / (double)total_tests * 100; - double skip_rate = (double)env->skip_count / (double)total_tests * 100; - double timeout_rate = (double)env->timeout_count / (double)total_tests * 100; - - // Calculate probability (success probability) - double probability_of_success = (double)env->pass_count / total_tests; - - // Calculate average (mean of success, failure, skip, timeout rates) - double average_rate = (success_rate + failure_rate + skip_rate + timeout_rate) / 4.0; - - // Prediction (can be based on past success rate or other methods) - double prediction = success_rate; // For simplicity, using the past success rate as prediction - - // Switch for summary modes - switch (env->options.summary) { - case FOSSIL_TEST_SUMMARY_PLAIN: - // Plain Mode: Basic output without extra formatting - if (env->fail_count > 0) { - internal_test_printf("{cyan}Failure rate: %.2f%%{reset}\n", failure_rate); - } - if (env->timeout_count > 0) { - internal_test_printf("{cyan}Timeout tests: %.2f%%{reset}\n", timeout_rate); - } - if (env->skip_count > 0) { - internal_test_printf("{cyan}Skipped tests: %.2f%% (%d tests){reset}\n", skip_rate, env->skip_count); - } - if (env->pass_count > 0) { - internal_test_printf("{cyan}Success rate: %.2f%%{reset}\n", success_rate); - } - - internal_test_printf("{cyan}Probability of success: %.2f{reset}\n", probability_of_success); - internal_test_printf("{cyan}Average test rate: %.2f%%{reset}\n", average_rate); - internal_test_printf("{cyan}Prediction (Future Success Rate): %.2f%%{reset}\n", prediction); - if (env->skip_count > 0) { - internal_test_printf("{yellow}Note: There were %d skipped tests. Please check the conditions or requirements for those tests.{reset}\n", env->skip_count); - } - break; - - case FOSSIL_TEST_SUMMARY_CI: - // CI Mode: CI-friendly output for automated systems - if (env->fail_count > 0) { - internal_test_printf("::group::{bold}Test Failure Analysis (CI Mode){reset}\n"); - internal_test_printf("FAILURE_RATE=%.2f%%\n", failure_rate); - } - if (env->timeout_count > 0) { - internal_test_printf("::group::{bold}Test Timeout Analysis (CI Mode){reset}\n"); - internal_test_printf("TIMEOUT_RATE=%.2f%%\n", timeout_rate); - } - if (env->skip_count > 0) { - internal_test_printf("::group::{bold}Test Skipped Analysis (CI Mode){reset}\n"); - internal_test_printf("SKIP_RATE=%.2f%%\n", skip_rate); - internal_test_printf("SKIPPED_TESTS=%d\n", env->skip_count); - } - if (env->pass_count > 0) { - internal_test_printf("::group::{bold}Test Success Analysis (CI Mode){reset}\n"); - internal_test_printf("SUCCESS_RATE=%.2f%%\n", success_rate); - } - - internal_test_printf("SUCCESS_PROBABILITY=%.2f\n", probability_of_success); - internal_test_printf("AVERAGE_TEST_RATE=%.2f%%\n", average_rate); - internal_test_printf("PREDICTION=%.2f%%\n", prediction); - internal_test_printf("::endgroup::\n"); - - if (env->skip_count > 0) { - internal_test_printf("::group::{bold}Skipped Tests Analysis (CI Mode){reset}\n"); - internal_test_printf("SKIPPED_TESTS=There were %d skipped tests. Please verify conditions or requirements for those tests.\n", env->skip_count); - internal_test_printf("::endgroup::\n"); - } - break; - - case FOSSIL_TEST_SUMMARY_JELLYFISH: - // Jellyfish Mode: Detailed output with insights and suggestions - if (env->fail_count > 0) { - internal_test_printf("Failure Rate: %.2f%%\n", failure_rate); - internal_test_printf("Consider reviewing the failed tests for possible issues with the environment, misconfigurations, or test logic.\n"); - } - if (env->timeout_count > 0) { - internal_test_printf("Timeout Rate: %.2f%%\n", timeout_rate); - internal_test_printf("Investigate system resources, network conditions, or tests prone to timeouts.\n"); - } - if (env->skip_count > 0) { - internal_test_printf("Skipped Tests: %.2f%% (%d tests)\n", skip_rate, env->skip_count); - internal_test_printf("Ensure tests are not being skipped due to unmet conditions. Review the prerequisites or intentional exclusions.\n"); - } - if (env->pass_count > 0) { - internal_test_printf("Success Rate: %.2f%%\n", success_rate); - internal_test_printf("Great job! Consider reviewing the successful tests for potential optimizations or edge cases.\n"); - } - - internal_test_printf("Probability of Success: %.2f\n", probability_of_success); - internal_test_printf("Average Test Rate: %.2f%%\n", average_rate); - internal_test_printf("Prediction (Future Success Rate): %.2f%%\n", prediction); - - if (env->skip_count > 0) { - internal_test_printf("{yellow}Note: There were %d skipped tests. Please check the conditions or requirements for those tests.{reset}\n", env->skip_count); - } - break; - - default: - internal_test_printf("{red}Unknown summary mode.{reset}\n"); - break; - } -} - -void fossil_test_suggest(fossil_test_env_t *env) { - if (!env) { - return; - } - - // Switch for summary modes - switch (env->options.summary) { - case FOSSIL_TEST_SUMMARY_PLAIN: - case FOSSIL_TEST_SUMMARY_JELLYFISH: - // Plain Mode: Display basic suggestions - if (env->pass_count == 0 && env->fail_count == 0 && env->skip_count == 0 && env->timeout_count == 0 && env->empty_count > 0) { - internal_test_printf("{cyan}{italic}Suggestion: %s{reset}\n", empty_suite_suggestions[rand() % 50]); - } else if (env->fail_count > 0) { - internal_test_printf("{cyan}{italic}Suggestion: %s{reset}\n", failure_suggestions[rand() % 50]); - } else if (env->pass_count > 0) { - internal_test_printf("{cyan}{italic}Suggestion: %s{reset}\n", success_suggestions[rand() % 50]); - } else if (env->timeout_count > 0) { - internal_test_printf("{cyan}{italic}Suggestion: %s{reset}\n", timeout_suggestions[rand() % 50]); - } else if (env->skip_count > 0) { - internal_test_printf("{cyan}{italic}Suggestion: Review skipped tests for prerequisites or intentional exclusions. Ensure tests are not being skipped due to unmet conditions.{reset}\n"); - } - break; - - case FOSSIL_TEST_SUMMARY_CI: - // CI Mode: CI-friendly suggestion format - if (env->fail_count > 0) { - internal_test_printf("::group::{bold}Test Failure Suggestion (CI Mode){reset}\n"); - internal_test_printf("FAILURE_SUGGESTION=%s\n", failure_suggestions[rand() % 50]); - internal_test_printf("::endgroup::\n"); - } else if (env->pass_count > 0) { - internal_test_printf("::group::{bold}Test Success Suggestion (CI Mode){reset}\n"); - internal_test_printf("SUCCESS_SUGGESTION=%s\n", success_suggestions[rand() % 50]); - internal_test_printf("::endgroup::\n"); - } else if (env->timeout_count > 0) { - internal_test_printf("::group::{bold}Test Timeout Suggestion (CI Mode){reset}\n"); - internal_test_printf("TIMEOUT_SUGGESTION=%s\n", timeout_suggestions[rand() % 50]); - internal_test_printf("::endgroup::\n"); - } else if (env->skip_count > 0) { - internal_test_printf("::group::{bold}Test Skipped Suggestion (CI Mode){reset}\n"); - internal_test_printf("SKIPPED_SUGGESTION=Review skipped tests for prerequisites or intentional exclusions.\n"); - internal_test_printf("Ensure tests are not being skipped due to unmet conditions.\n"); - internal_test_printf("::endgroup::\n"); - } - break; - - default: - internal_test_printf("{red}Unknown summary mode.{reset}\n"); - break; - } -} - -void fossil_test_execution_time(fossil_test_env_t *env) { - if (!env) { - return; - } - - // Calculate total execution time in seconds - double total_execution_time = (double)(env->end_execution_time - env->start_execution_time) / CLOCKS_PER_SEC; - - // Breakdown into smaller units - int32_t seconds = (int32_t)total_execution_time; - int32_t milliseconds = (int32_t)((total_execution_time - seconds) * 1000); - int32_t microseconds = (int32_t)((total_execution_time - seconds - milliseconds / 1000.0) * 1000000); - int32_t nanoseconds = (int32_t)((total_execution_time - seconds - milliseconds / 1000.0 - microseconds / 1000000.0) * 1000000000); - - // Switch for summary modes - switch (env->options.summary) { - case FOSSIL_TEST_SUMMARY_PLAIN: - case FOSSIL_TEST_SUMMARY_JELLYFISH: - // Plain Mode: Display basic execution time without additional analysis - internal_test_printf("{blue}{bold}=================================================================================={reset}\n"); - internal_test_printf("{cyan}{italic}\tExecution Time Analysis (Plain Mode):{reset}\n"); - internal_test_printf("{cyan}{italic}|\tExecution time: %02d sec, %03d ms, %06d us, %09d ns{reset}\n", - seconds, milliseconds, microseconds, nanoseconds); - internal_test_printf("{blue}{bold}=================================================================================={reset}\n"); - break; - - case FOSSIL_TEST_SUMMARY_CI: - // CI Mode: Simple summary in CI-friendly format - internal_test_printf("::group::{bold}Execution Time (CI Mode){reset}\n"); - internal_test_printf("EXECUTION_TIME=%02d sec %03d ms %06d us %09d ns\n", - seconds, milliseconds, microseconds, nanoseconds); - internal_test_printf("::endgroup::\n"); - break; - - default: - internal_test_printf("{red}Unknown summary mode.{reset}\n"); - break; - } -} - -void fossil_test_summary(fossil_test_env_t *env) { - if (!env) { - return; - } - - if (env->options.dry_run) { - internal_test_printf("{purple}Dry run mode enabled. No tests were executed or evaluated.{reset}\n"); - return; - } - - fossil_test_suite_t *suite = env->test_suites; - while (suite != NULL) { - fossil_test_case_t *test = suite->tests; - while (test != NULL) { - if (test->status == TEST_STATUS_PASS) { - env->pass_count++; - } else if (test->status == TEST_STATUS_FAIL) { - env->fail_count++; - if (test->failure_message && env->options.summary != FOSSIL_TEST_SUMMARY_CI) { - internal_test_printf("{red}Test '%s' failed: %s{reset}\n", test->name, test->failure_message); - } - } else if (test->status == TEST_STATUS_SKIP) { - env->skip_count++; - } else if (test->status == TEST_STATUS_TTIMEOUT) { - env->timeout_count++; - } else { - env->unexpected_count++; - } - - test = test->next; - } - suite = suite->next; - } - - env->end_execution_time = clock(); - - internal_test_printf("{blue}{bold}=================================================================================={reset}\n"); - internal_test_printf("{cyan}{italic}\tFossil Smart Test {reset}\n"); - internal_test_printf("{blue}{bold}=================================================================================={reset}\n"); - - switch (env->options.summary) { - case FOSSIL_TEST_SUMMARY_PLAIN: - internal_test_printf("{blue}{bold}Fossil Test Summary:{reset}\n"); - internal_test_printf("{cyan}{italic}Passed: {green}%d{reset}\n", env->pass_count); - internal_test_printf("{cyan}{italic}Failed: {red}%d{reset}\n", env->fail_count); - internal_test_printf("{cyan}{italic}Skipped: {yellow}%d{reset}\n", env->skip_count); - internal_test_printf("{cyan}{italic}Timeouts: {magenta}%d{reset}\n", env->timeout_count); - internal_test_printf("{blue}{bold}Other: {blue}%d{reset}\n", env->unexpected_count); - fossil_test_comment(env); // AI-style comments - fossil_test_suggest(env); // Suggestions for test coverage or structure - fossil_test_execution_time(env); - break; - - case FOSSIL_TEST_SUMMARY_CI: - internal_test_printf("::group::{bold}Fossil Test Summary{reset}\n"); - internal_test_printf("PASS=%d\n", env->pass_count); - internal_test_printf("FAIL=%d\n", env->fail_count); - internal_test_printf("SKIP=%d\n", env->skip_count); - internal_test_printf("TIMEOUT=%d\n", env->timeout_count); - internal_test_printf("OTHER=%d\n", env->unexpected_count); - internal_test_printf("::endgroup::\n"); - fossil_test_comment(env); // AI-style comments - fossil_test_suggest(env); // Suggestions for test coverage or structure - fossil_test_execution_time(env); - break; - - case FOSSIL_TEST_SUMMARY_JELLYFISH: - fossil_test_analyze(env); // Deep insights - fossil_test_comment(env); // AI-style comments - fossil_test_suggest(env); // Suggestions for test coverage or structure - fossil_test_execution_time(env); - break; - - default: - internal_test_printf("{red}Unknown summary mode.{reset}\n"); - break; - - internal_test_printf("{blue}{bold}=================================================================================={reset}\n"); - } -} diff --git a/code/tests/cases/test_bdd.c b/code/tests/cases/test_bdd.c index 4cb60fa7..2b092aca 100644 --- a/code/tests/cases/test_bdd.c +++ b/code/tests/cases/test_bdd.c @@ -7,12 +7,12 @@ * herein is subject to the terms and conditions defined in the project license. * * Author: Michael Gene Brockus (Dreamer) - * Date: 07/01/2024 + * Date: 04/05/2014 * - * Copyright (C) 2024 Fossil Logic. All rights reserved. + * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. * ----------------------------------------------------------------------------- */ -#include +#include // * * * * * * * * * * * * * * * * * * * * * * * * // * Fossil Logic Test Utilites @@ -22,7 +22,7 @@ // * * * * * * * * * * * * * * * * * * * * * * * * // Define the test suite and add test cases -FOSSIL_TEST_SUITE(bdd_suite); +FOSSIL_SUITE(bdd_suite); // Setup function for the test suite FOSSIL_SETUP(bdd_suite) { @@ -42,7 +42,7 @@ FOSSIL_TEARDOWN(bdd_suite) { // as samples for library usage. // * * * * * * * * * * * * * * * * * * * * * * * * -FOSSIL_TEST_CASE(xbdd_logic_test) { +FOSSIL_TEST(xbdd_logic_test) { GIVEN("a valid statement is passed") { // Set up the context bool givenExecuted = true; @@ -63,7 +63,7 @@ FOSSIL_TEST_CASE(xbdd_logic_test) { } } // end of case -FOSSIL_TEST_CASE(xbdd_user_account) { +FOSSIL_TEST(xbdd_user_account) { GIVEN("a user's account with sufficient balance") { // Set up the context float accountBalance = 500.0; @@ -85,7 +85,7 @@ FOSSIL_TEST_CASE(xbdd_user_account) { } } // end of case -FOSSIL_TEST_CASE(xbdd_empty_cart) { +FOSSIL_TEST(xbdd_empty_cart) { GIVEN("a user with an empty shopping cart") { // Set up the context int cartItemCount = 0; @@ -103,7 +103,7 @@ FOSSIL_TEST_CASE(xbdd_empty_cart) { } } // end of case -FOSSIL_TEST_CASE(xbdd_valid_login) { +FOSSIL_TEST(xbdd_valid_login) { GIVEN("a registered user with valid credentials") { // Set up the context const char* validUsername = "user123"; @@ -137,7 +137,7 @@ FOSSIL_TEST_CASE(xbdd_valid_login) { } } // end of case -FOSSIL_TEST_CASE(xbdd_invalid_login) { +FOSSIL_TEST(xbdd_invalid_login) { GIVEN("a registered user with valid credentials") { // Set up the context const char* validUsername = "user123"; @@ -158,7 +158,7 @@ FOSSIL_TEST_CASE(xbdd_invalid_login) { } } // end of case -FOSSIL_TEST_CASE(xbdd_insufficient_balance) { +FOSSIL_TEST(xbdd_insufficient_balance) { GIVEN("a user's account with insufficient balance") { // Set up the context float accountBalance = 100.0; @@ -181,7 +181,7 @@ FOSSIL_TEST_CASE(xbdd_insufficient_balance) { } } // end of case -FOSSIL_TEST_CASE(xbdd_add_multiple_items_to_cart) { +FOSSIL_TEST(xbdd_add_multiple_items_to_cart) { GIVEN("a user with an empty shopping cart") { // Set up the context int cartItemCount = 0; @@ -198,7 +198,7 @@ FOSSIL_TEST_CASE(xbdd_add_multiple_items_to_cart) { } } // end of case -FOSSIL_TEST_CASE(xbdd_remove_item_from_cart) { +FOSSIL_TEST(xbdd_remove_item_from_cart) { GIVEN("a user with a shopping cart containing 2 items") { // Set up the context int cartItemCount = 2; diff --git a/code/tests/cases/test_bdd.cpp b/code/tests/cases/test_bdd.cpp index dfb49322..02bc445d 100644 --- a/code/tests/cases/test_bdd.cpp +++ b/code/tests/cases/test_bdd.cpp @@ -7,12 +7,12 @@ * herein is subject to the terms and conditions defined in the project license. * * Author: Michael Gene Brockus (Dreamer) - * Date: 07/01/2024 + * Date: 04/05/2014 * - * Copyright (C) 2024 Fossil Logic. All rights reserved. + * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. * ----------------------------------------------------------------------------- */ -#include +#include #include // * * * * * * * * * * * * * * * * * * * * * * * * @@ -23,7 +23,7 @@ // * * * * * * * * * * * * * * * * * * * * * * * * // Define the test suite and add test cases -FOSSIL_TEST_SUITE(cpp_bdd_suite); +FOSSIL_SUITE(cpp_bdd_suite); // Setup function for the test suite FOSSIL_SETUP(cpp_bdd_suite) { @@ -43,7 +43,7 @@ FOSSIL_TEARDOWN(cpp_bdd_suite) { // as samples for library usage. // * * * * * * * * * * * * * * * * * * * * * * * * -FOSSIL_TEST_CASE(cpp_xbdd_logic_test) { +FOSSIL_TEST(cpp_xbdd_logic_test) { GIVEN("a valid statement is passed") { // Set up the context bool givenExecuted = true; @@ -64,7 +64,7 @@ FOSSIL_TEST_CASE(cpp_xbdd_logic_test) { } } // end of case -FOSSIL_TEST_CASE(cpp_xbdd_user_account) { +FOSSIL_TEST(cpp_xbdd_user_account) { GIVEN("a user's account with sufficient balance") { // Set up the context float accountBalance = 500.0; @@ -86,7 +86,7 @@ FOSSIL_TEST_CASE(cpp_xbdd_user_account) { } } // end of case -FOSSIL_TEST_CASE(cpp_xbdd_empty_cart) { +FOSSIL_TEST(cpp_xbdd_empty_cart) { GIVEN("a user with an empty shopping cart") { // Set up the context int cartItemCount = 0; @@ -104,7 +104,7 @@ FOSSIL_TEST_CASE(cpp_xbdd_empty_cart) { } } // end of case -FOSSIL_TEST_CASE(cpp_xbdd_valid_login) { +FOSSIL_TEST(cpp_xbdd_valid_login) { GIVEN("a registered user with valid credentials") { // Set up the context std::string validUsername = "user123"; @@ -138,7 +138,7 @@ FOSSIL_TEST_CASE(cpp_xbdd_valid_login) { } } // end of case -FOSSIL_TEST_CASE(cpp_xbdd_invalid_login) { +FOSSIL_TEST(cpp_xbdd_invalid_login) { GIVEN("a registered user with valid credentials") { // Set up the context std::string validUsername = "user123"; @@ -159,7 +159,7 @@ FOSSIL_TEST_CASE(cpp_xbdd_invalid_login) { } } // end of case -FOSSIL_TEST_CASE(cpp_xbdd_insufficient_balance) { +FOSSIL_TEST(cpp_xbdd_insufficient_balance) { GIVEN("a user's account with insufficient balance") { // Set up the context float accountBalance = 100.0; @@ -182,7 +182,7 @@ FOSSIL_TEST_CASE(cpp_xbdd_insufficient_balance) { } } // end of case -FOSSIL_TEST_CASE(cpp_xbdd_add_multiple_items_to_cart) { +FOSSIL_TEST(cpp_xbdd_add_multiple_items_to_cart) { GIVEN("a user with an empty shopping cart") { // Set up the context int cartItemCount = 0; @@ -199,7 +199,7 @@ FOSSIL_TEST_CASE(cpp_xbdd_add_multiple_items_to_cart) { } } // end of case -FOSSIL_TEST_CASE(cpp_xbdd_remove_item_from_cart) { +FOSSIL_TEST(cpp_xbdd_remove_item_from_cart) { GIVEN("a user with a shopping cart containing 2 items") { // Set up the context int cartItemCount = 2; diff --git a/code/tests/cases/test_ddd.c b/code/tests/cases/test_ddd.c index 32654c2c..7c1e4c09 100644 --- a/code/tests/cases/test_ddd.c +++ b/code/tests/cases/test_ddd.c @@ -7,12 +7,12 @@ * herein is subject to the terms and conditions defined in the project license. * * Author: Michael Gene Brockus (Dreamer) - * Date: 07/01/2024 + * Date: 04/05/2014 * - * Copyright (C) 2024 Fossil Logic. All rights reserved. + * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. * ----------------------------------------------------------------------------- */ -#include +#include #include // Define the necessary types and functions for the test cases @@ -120,7 +120,7 @@ void service_process(Service *service, Entity *entity) { // * * * * * * * * * * * * * * * * * * * * * * * * // Define the test suite and add test cases -FOSSIL_TEST_SUITE(c_ddd_suite); +FOSSIL_SUITE(c_ddd_suite); // Setup function for the test suite FOSSIL_SETUP(c_ddd_suite) { @@ -139,7 +139,7 @@ FOSSIL_TEARDOWN(c_ddd_suite) { // Domain-Driven Design (DDD) usage in the Fossil Logic project. // * * * * * * * * * * * * * * * * * * * * * * * * -FOSSIL_TEST_CASE(c_ddd_entity_creation) { +FOSSIL_TEST(c_ddd_entity_creation) { // Example of creating an entity Entity entity = create_entity(42, "Sample Entity"); @@ -148,7 +148,7 @@ FOSSIL_TEST_CASE(c_ddd_entity_creation) { FOSSIL_TEST_ASSUME(strcmp(entity.name, "Sample Entity") == 0, "Entity name should be 'Sample Entity'"); } // end case -FOSSIL_TEST_CASE(c_ddd_value_object_equality) { +FOSSIL_TEST(c_ddd_value_object_equality) { // Example of value object equality ValueObject vo1 = create_value_object(10, 20); ValueObject vo2 = create_value_object(10, 20); @@ -157,7 +157,7 @@ FOSSIL_TEST_CASE(c_ddd_value_object_equality) { FOSSIL_TEST_ASSUME(value_object_equals(vo1, vo2), "Value objects should be equal"); } // end case -FOSSIL_TEST_CASE(c_ddd_aggregate_root_behavior) { +FOSSIL_TEST(c_ddd_aggregate_root_behavior) { // Example of aggregate root behavior AggregateRoot ar = create_aggregate_root(1); add_child_entity(&ar, create_entity(2, "Child Entity")); @@ -167,7 +167,7 @@ FOSSIL_TEST_CASE(c_ddd_aggregate_root_behavior) { FOSSIL_TEST_ASSUME(ar.children[0].id == 2, "Child entity ID should be 2"); } // end case -FOSSIL_TEST_CASE(c_ddd_repository_usage) { +FOSSIL_TEST(c_ddd_repository_usage) { // Example of repository usage Repository repo = create_repository(); Entity entity = create_entity(1, "Repo Entity"); @@ -178,7 +178,7 @@ FOSSIL_TEST_CASE(c_ddd_repository_usage) { FOSSIL_TEST_ASSUME(repository_get(&repo, 1).id == 1, "Retrieved entity ID should be 1"); } // end case -FOSSIL_TEST_CASE(c_ddd_service_layer) { +FOSSIL_TEST(c_ddd_service_layer) { // Example of service layer usage Service service = create_service(); Entity entity = create_entity(1, "Service Entity"); diff --git a/code/tests/cases/test_ddd.cpp b/code/tests/cases/test_ddd.cpp index 6fec934d..62c69e98 100644 --- a/code/tests/cases/test_ddd.cpp +++ b/code/tests/cases/test_ddd.cpp @@ -7,12 +7,12 @@ * herein is subject to the terms and conditions defined in the project license. * * Author: Michael Gene Brockus (Dreamer) - * Date: 07/01/2024 + * Date: 04/05/2014 * - * Copyright (C) 2024 Fossil Logic. All rights reserved. + * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. * ----------------------------------------------------------------------------- */ -#include +#include #include #include @@ -90,7 +90,7 @@ class Service { // * * * * * * * * * * * * * * * * * * * * * * * * // Define the test suite and add test cases -FOSSIL_TEST_SUITE(cpp_ddd_suite); +FOSSIL_SUITE(cpp_ddd_suite); // Setup function for the test suite FOSSIL_SETUP(cpp_ddd_suite) { @@ -109,7 +109,7 @@ FOSSIL_TEARDOWN(cpp_ddd_suite) { // Domain-Driven Design (DDD) usage in the Fossil Logic project. // * * * * * * * * * * * * * * * * * * * * * * * * -FOSSIL_TEST_CASE(cpp_ddd_entity_creation) { +FOSSIL_TEST(cpp_ddd_entity_creation) { // Example of creating an entity Entity entity(42, "Sample Entity"); @@ -118,7 +118,7 @@ FOSSIL_TEST_CASE(cpp_ddd_entity_creation) { FOSSIL_TEST_ASSUME(entity.name == "Sample Entity", "Entity name should be 'Sample Entity'"); } // end case -FOSSIL_TEST_CASE(cpp_ddd_value_object_equality) { +FOSSIL_TEST(cpp_ddd_value_object_equality) { // Example of value object equality ValueObject vo1(10, 20); ValueObject vo2(10, 20); @@ -127,7 +127,7 @@ FOSSIL_TEST_CASE(cpp_ddd_value_object_equality) { FOSSIL_TEST_ASSUME(vo1 == vo2, "Value objects should be equal"); } // end case -FOSSIL_TEST_CASE(cpp_ddd_aggregate_root_behavior) { +FOSSIL_TEST(cpp_ddd_aggregate_root_behavior) { // Example of aggregate root behavior AggregateRoot ar(1); ar.addChild(Entity(2, "Child Entity")); @@ -137,7 +137,7 @@ FOSSIL_TEST_CASE(cpp_ddd_aggregate_root_behavior) { FOSSIL_TEST_ASSUME(ar.children[0].id == 2, "Child entity ID should be 2"); } // end case -FOSSIL_TEST_CASE(cpp_ddd_repository_usage) { +FOSSIL_TEST(cpp_ddd_repository_usage) { // Example of repository usage Repository repo; Entity entity(1, "Repo Entity"); @@ -148,7 +148,7 @@ FOSSIL_TEST_CASE(cpp_ddd_repository_usage) { FOSSIL_TEST_ASSUME(repo.get(1).id == 1, "Retrieved entity ID should be 1"); } // end case -FOSSIL_TEST_CASE(cpp_ddd_service_layer) { +FOSSIL_TEST(cpp_ddd_service_layer) { // Example of service layer usage Service service; Entity entity(1, "Service Entity"); diff --git a/code/tests/cases/test_mark.c b/code/tests/cases/test_mark.c index 02add5d9..5191c359 100644 --- a/code/tests/cases/test_mark.c +++ b/code/tests/cases/test_mark.c @@ -7,13 +7,13 @@ * herein is subject to the terms and conditions defined in the project license. * * Author: Michael Gene Brockus (Dreamer) - * Date: 07/01/2024 + * Date: 04/05/2014 * - * Copyright (C) 2024 Fossil Logic. All rights reserved. + * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. * ----------------------------------------------------------------------------- */ -#include "fossil/test/framework.h" +#include "fossil/pizza/framework.h" // * * * * * * * * * * * * * * * * * * * * * * * * // * Fossil Logic Test Utilites @@ -23,7 +23,7 @@ // * * * * * * * * * * * * * * * * * * * * * * * * // Define the test suite and add test cases -FOSSIL_TEST_SUITE(c_mark_suite); +FOSSIL_SUITE(c_mark_suite); // Setup function for the test suite FOSSIL_SETUP(c_mark_suite) { @@ -44,7 +44,7 @@ FOSSIL_TEARDOWN(c_mark_suite) { // * * * * * * * * * * * * * * * * * * * * * * * * // A test case to check if the benchmark stop works correctly -FOSSIL_TEST_CASE(c_mark_start_and_stop) { +FOSSIL_TEST(c_mark_start_and_stop) { MARK_BENCHMARK(stop_test); MARK_START(stop_test); MARK_STOP(stop_test); diff --git a/code/tests/cases/test_mark.cpp b/code/tests/cases/test_mark.cpp index 941fd41c..e729210f 100644 --- a/code/tests/cases/test_mark.cpp +++ b/code/tests/cases/test_mark.cpp @@ -7,13 +7,13 @@ * herein is subject to the terms and conditions defined in the project license. * * Author: Michael Gene Brockus (Dreamer) - * Date: 07/01/2024 + * Date: 04/05/2014 * - * Copyright (C) 2024 Fossil Logic. All rights reserved. + * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. * ----------------------------------------------------------------------------- */ -#include "fossil/test/framework.h" +#include "fossil/pizza/framework.h" #include // * * * * * * * * * * * * * * * * * * * * * * * * @@ -24,7 +24,7 @@ // * * * * * * * * * * * * * * * * * * * * * * * * // Define the test suite and add test cases -FOSSIL_TEST_SUITE(cpp_mark_suite); +FOSSIL_SUITE(cpp_mark_suite); // Setup function for the test suite FOSSIL_SETUP(cpp_mark_suite) { @@ -45,7 +45,7 @@ FOSSIL_TEARDOWN(cpp_mark_suite) { // * * * * * * * * * * * * * * * * * * * * * * * * // A test case to check if the benchmark stop works correctly -FOSSIL_TEST_CASE(cpp_mark_start_and_stop) { +FOSSIL_TEST(cpp_mark_start_and_stop) { std::string benchmark_stop_test_name = "stop_test"; MARK_BENCHMARK(stop_test); MARK_START(stop_test); diff --git a/code/tests/cases/test_mock.c b/code/tests/cases/test_mock.c index c15504a6..3a3b0bc7 100644 --- a/code/tests/cases/test_mock.c +++ b/code/tests/cases/test_mock.c @@ -7,12 +7,12 @@ * herein is subject to the terms and conditions defined in the project license. * * Author: Michael Gene Brockus (Dreamer) - * Date: 07/01/2024 + * Date: 04/05/2014 * - * Copyright (C) 2024 Fossil Logic. All rights reserved. + * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. * ----------------------------------------------------------------------------- */ -#include +#include #include @@ -24,7 +24,7 @@ // * * * * * * * * * * * * * * * * * * * * * * * * // Define the test suite and add test cases -FOSSIL_TEST_SUITE(c_mock_suite); +FOSSIL_SUITE(c_mock_suite); // Setup function for the test suite FOSSIL_SETUP(c_mock_suite) { @@ -45,7 +45,7 @@ FOSSIL_MOCK_STRUCT(MockStruct) { } MockStruct; // Example of creating a mock function using the macro -FOSSIL_MOCK_FUNC(int, mock_function, int a, int b) { +FOSSIL_MOCK_FUNC(int, c_mock_function, int a, int b) { return a + b; } @@ -56,7 +56,7 @@ FOSSIL_MOCK_FUNC(int, mock_function, int a, int b) { // Domain-Driven Design (DDD) usage in the Fossil Logic project. // * * * * * * * * * * * * * * * * * * * * * * * * -FOSSIL_TEST_CASE(c_mock_call_list_initialization) { +FOSSIL_TEST(c_mock_call_list_initialization) { // Example of initializing a fossil_mock_calllist_t fossil_mock_calllist_t list; fossil_mock_init(&list); @@ -67,95 +67,260 @@ FOSSIL_TEST_CASE(c_mock_call_list_initialization) { FOSSIL_TEST_ASSUME(list.size == 0, "fossil_mock_calllist_t size should be 0 after initialization"); } // end case -FOSSIL_TEST_CASE(c_mock_call_list_addition) { +FOSSIL_TEST(c_mock_call_list_addition) { // Example of adding a fossil_mock_call_t to a fossil_mock_calllist_t fossil_mock_calllist_t list; fossil_mock_init(&list); - char *args[] = {"arg1", "arg2"}; + + // Create mock arguments + fossil_mock_pizza_t args[2]; + args[0].type = FOSSIL_MOCK_PIZZA_TYPE_CSTR; + args[0].value.data = pizza_io_cstr_dup("arg1"); + args[0].value.mutable_flag = false; + args[0].attribute.name = pizza_io_cstr_dup("arg1_name"); + args[0].attribute.description = pizza_io_cstr_dup("First argument"); + args[0].attribute.id = pizza_io_cstr_dup("1"); + + args[1].type = FOSSIL_MOCK_PIZZA_TYPE_CSTR; + args[1].value.data = pizza_io_cstr_dup("arg2"); + args[1].value.mutable_flag = false; + args[1].attribute.name = pizza_io_cstr_dup("arg2_name"); + args[1].attribute.description = pizza_io_cstr_dup("Second argument"); + args[1].attribute.id = pizza_io_cstr_dup("2"); + fossil_mock_add_call(&list, "test_function", args, 2); // Test cases FOSSIL_TEST_ASSUME(list.size == 1, "fossil_mock_calllist_t size should be 1 after adding a call"); FOSSIL_TEST_ASSUME(strcmp(list.head->function_name, "test_function") == 0, "Function name should be 'test_function'"); FOSSIL_TEST_ASSUME(list.head->num_args == 2, "Number of arguments should be 2"); - FOSSIL_TEST_ASSUME(strcmp(list.head->arguments[0], "arg1") == 0, "First argument should be 'arg1'"); - FOSSIL_TEST_ASSUME(strcmp(list.head->arguments[1], "arg2") == 0, "Second argument should be 'arg2'"); + FOSSIL_TEST_ASSUME(strcmp(list.head->arguments[0].value.data, "arg1") == 0, "First argument should be 'arg1'"); + FOSSIL_TEST_ASSUME(strcmp(list.head->arguments[1].value.data, "arg2") == 0, "Second argument should be 'arg2'"); } // end case -FOSSIL_TEST_CASE(c_mock_call_list_destruction) { +FOSSIL_TEST(c_mock_call_list_destruction) { // Example of destroying a fossil_mock_calllist_t fossil_mock_calllist_t list; fossil_mock_init(&list); - char *args[] = {"arg1", "arg2"}; + + // Create mock arguments + fossil_mock_pizza_t args[2]; + args[0].type = FOSSIL_MOCK_PIZZA_TYPE_CSTR; + args[0].value.data = pizza_io_cstr_dup("arg1"); + args[0].value.mutable_flag = false; + args[0].attribute.name = pizza_io_cstr_dup("arg1_name"); + args[0].attribute.description = pizza_io_cstr_dup("First argument"); + args[0].attribute.id = pizza_io_cstr_dup("1"); + + args[1].type = FOSSIL_MOCK_PIZZA_TYPE_CSTR; + args[1].value.data = pizza_io_cstr_dup("arg2"); + args[1].value.mutable_flag = false; + args[1].attribute.name = pizza_io_cstr_dup("arg2_name"); + args[1].attribute.description = pizza_io_cstr_dup("Second argument"); + args[1].attribute.id = pizza_io_cstr_dup("2"); + fossil_mock_add_call(&list, "test_function", args, 2); + FOSSIL_TEST_ASSUME(list.size == 1, "fossil_mock_calllist_t size should be 1 after adding a call"); FOSSIL_TEST_ASSUME(strcmp(list.head->function_name, "test_function") == 0, "Function name should be 'test_function'"); FOSSIL_TEST_ASSUME(list.head->num_args == 2, "Number of arguments should be 2"); - - fossil_mock_destroy(&list); // not allowed to access due to the object being freed + + fossil_mock_destroy(&list); } // end case -FOSSIL_TEST_CASE(c_mock_call_list_initialization_macro) { - // Example of initializing a fossil_mock_calllist_t using the macro - fossil_mock_calllist_t list; - MOCK_INIT(list); +FOSSIL_TEST(c_mock_function_creation) { + // Test cases + FOSSIL_TEST_ASSUME(fossil_mockup_c_mock_function(2, 3) == 5, "Mock function should return the sum of its arguments"); +} // end case + +FOSSIL_TEST(c_mock_alias_creation) { + // Example of creating a type alias using the macro // Test cases - FOSSIL_TEST_ASSUME(list.head == NULL, "fossil_mock_calllist_t head should be NULL after initialization"); - FOSSIL_TEST_ASSUME(list.tail == NULL, "fossil_mock_calllist_t tail should be NULL after initialization"); - FOSSIL_TEST_ASSUME(list.size == 0, "fossil_mock_calllist_t size should be 0 after initialization"); + MockInt x = 10; + FOSSIL_TEST_ASSUME(x == 10, "Mock alias should behave like the original type"); } // end case -FOSSIL_TEST_CASE(c_mock_call_list_addition_macro) { - // Example of adding a fossil_mock_call_t to a fossil_mock_calllist_t using the macro +FOSSIL_TEST(c_mock_struct_creation) { + // Test cases + MockStruct instance; + instance.a = 5; + instance.b = 'c'; + FOSSIL_TEST_ASSUME(instance.a == 5, "Mock struct member 'a' should be 5"); + FOSSIL_TEST_ASSUME(instance.b == 'c', "Mock struct member 'b' should be 'c'"); +} // end case + +FOSSIL_TEST(c_mock_call_list_type_handling) { + // Initialize the mock call list fossil_mock_calllist_t list; - MOCK_INIT(list); - char *args[] = {"arg1", "arg2"}; - MOCK_ADD_CALL(list, "test_function", args, 2); + fossil_mock_init(&list); + + // Create mock arguments with various types + fossil_mock_pizza_t args[3]; + args[0].type = FOSSIL_MOCK_PIZZA_TYPE_I32; + args[0].value.data = pizza_io_cstr_dup("42"); + args[0].value.mutable_flag = false; + args[0].attribute.name = pizza_io_cstr_dup("arg1"); + args[0].attribute.description = pizza_io_cstr_dup("Integer argument"); + args[0].attribute.id = pizza_io_cstr_dup("1"); + + args[1].type = FOSSIL_MOCK_PIZZA_TYPE_CSTR; + args[1].value.data = pizza_io_cstr_dup("Hello"); + args[1].value.mutable_flag = true; + args[1].attribute.name = pizza_io_cstr_dup("arg2"); + args[1].attribute.description = pizza_io_cstr_dup("String argument"); + args[1].attribute.id = pizza_io_cstr_dup("2"); + + args[2].type = FOSSIL_MOCK_PIZZA_TYPE_BOOL; + args[2].value.data = pizza_io_cstr_dup("true"); + args[2].value.mutable_flag = false; + args[2].attribute.name = pizza_io_cstr_dup("arg3"); + args[2].attribute.description = pizza_io_cstr_dup("Boolean argument"); + args[2].attribute.id = pizza_io_cstr_dup("3"); + + // Add a mock call with the arguments + fossil_mock_add_call(&list, "test_function", args, 3); // Test cases FOSSIL_TEST_ASSUME(list.size == 1, "fossil_mock_calllist_t size should be 1 after adding a call"); FOSSIL_TEST_ASSUME(strcmp(list.head->function_name, "test_function") == 0, "Function name should be 'test_function'"); - FOSSIL_TEST_ASSUME(list.head->num_args == 2, "Number of arguments should be 2"); - FOSSIL_TEST_ASSUME(strcmp(list.head->arguments[0], "arg1") == 0, "First argument should be 'arg1'"); - FOSSIL_TEST_ASSUME(strcmp(list.head->arguments[1], "arg2") == 0, "Second argument should be 'arg2'"); + FOSSIL_TEST_ASSUME(list.head->num_args == 3, "Number of arguments should be 3"); + + FOSSIL_TEST_ASSUME(list.head->arguments[0].type == FOSSIL_MOCK_PIZZA_TYPE_I32, "First argument type should be I32"); + FOSSIL_TEST_ASSUME(strcmp(list.head->arguments[0].value.data, "42") == 0, "First argument value should be '42'"); + FOSSIL_TEST_ASSUME(strcmp(list.head->arguments[0].attribute.name, "arg1") == 0, "First argument name should be 'arg1'"); + + FOSSIL_TEST_ASSUME(list.head->arguments[1].type == FOSSIL_MOCK_PIZZA_TYPE_CSTR, "Second argument type should be CSTR"); + FOSSIL_TEST_ASSUME(strcmp(list.head->arguments[1].value.data, "Hello") == 0, "Second argument value should be 'Hello'"); + FOSSIL_TEST_ASSUME(strcmp(list.head->arguments[1].attribute.name, "arg2") == 0, "Second argument name should be 'arg2'"); + + FOSSIL_TEST_ASSUME(list.head->arguments[2].type == FOSSIL_MOCK_PIZZA_TYPE_BOOL, "Third argument type should be BOOL"); + FOSSIL_TEST_ASSUME(strcmp(list.head->arguments[2].value.data, "true") == 0, "Third argument value should be 'true'"); + FOSSIL_TEST_ASSUME(strcmp(list.head->arguments[2].attribute.name, "arg3") == 0, "Third argument name should be 'arg3'"); + + // Clean up + fossil_mock_destroy(&list); } // end case -FOSSIL_TEST_CASE(c_mock_call_list_destruction_macro) { - // Example of destroying a fossil_mock_calllist_t using the macro +FOSSIL_TEST(c_mock_call_list_edge_cases) { + // Initialize the mock call list fossil_mock_calllist_t list; - MOCK_INIT(list); - char *args[] = {"arg1", "arg2"}; - MOCK_ADD_CALL(list, "test_function", args, 2); - - FOSSIL_TEST_ASSUME(list.size == 1, "fossil_mock_calllist_t size should be 1 after adding a call"); - FOSSIL_TEST_ASSUME(strcmp(list.head->function_name, "test_function") == 0, "Function name should be 'test_function'"); - FOSSIL_TEST_ASSUME(list.head->num_args == 2, "Number of arguments should be 2"); + fossil_mock_init(&list); + + // Add a call with no arguments + fossil_mock_add_call(&list, "no_args_function", NULL, 0); - MOCK_DESTROY(list); // not allowed to access due to the object being freed + // Test cases + FOSSIL_TEST_ASSUME(list.size == 1, "fossil_mock_calllist_t size should be 1 after adding a call with no arguments"); + FOSSIL_TEST_ASSUME(strcmp(list.head->function_name, "no_args_function") == 0, "Function name should be 'no_args_function'"); + FOSSIL_TEST_ASSUME(list.head->num_args == 0, "Number of arguments should be 0"); + + // Clean up + fossil_mock_destroy(&list); } // end case -FOSSIL_TEST_CASE(c_mock_function_creation) { +FOSSIL_TEST(c_mock_call_list_large_arguments) { + // Initialize the mock call list + fossil_mock_calllist_t list; + fossil_mock_init(&list); + + // Create a large number of mock arguments + const int num_args = 100; + fossil_mock_pizza_t args[num_args]; + for (int i = 0; i < num_args; ++i) { + args[i].type = FOSSIL_MOCK_PIZZA_TYPE_I32; + args[i].value.data = pizza_io_cstr_dup("42"); + args[i].value.mutable_flag = false; + args[i].attribute.name = pizza_io_cstr_dup("arg"); + args[i].attribute.description = pizza_io_cstr_dup("Large argument test"); + args[i].attribute.id = pizza_io_cstr_dup("id"); + } + + // Add a mock call with the large number of arguments + fossil_mock_add_call(&list, "large_args_function", args, num_args); + // Test cases - FOSSIL_TEST_ASSUME(fossil_mockup_mock_function(2, 3) == 5, "Mock function should return the sum of its arguments"); + FOSSIL_TEST_ASSUME(list.size == 1, "fossil_mock_calllist_t size should be 1 after adding a call with large arguments"); + FOSSIL_TEST_ASSUME(list.head->num_args == num_args, "Number of arguments should match the large number"); + + // Clean up + fossil_mock_destroy(&list); } // end case -FOSSIL_TEST_CASE(c_mock_alias_creation) { - // Example of creating a type alias using the macro - +FOSSIL_TEST(c_mock_macro_initialization) { + // Initialize the mock call list using the macro + fossil_mock_calllist_t list; + MOCK_INIT(list); // Test cases - MockInt x = 10; - FOSSIL_TEST_ASSUME(x == 10, "Mock alias should behave like the original type"); + FOSSIL_TEST_ASSUME(list.head == NULL, "fossil_mock_calllist_t head should be NULL after initialization using macro"); + FOSSIL_TEST_ASSUME(list.tail == NULL, "fossil_mock_calllist_t tail should be NULL after initialization using macro"); + FOSSIL_TEST_ASSUME(list.size == 0, "fossil_mock_calllist_t size should be 0 after initialization using macro"); +} // end case + +FOSSIL_TEST(c_mock_macro_addition) { + // Initialize the mock call list using the macro + fossil_mock_calllist_t list; + MOCK_INIT(list); + + // Create mock arguments + fossil_mock_pizza_t args[2]; + args[0].type = FOSSIL_MOCK_PIZZA_TYPE_CSTR; + args[0].value.data = pizza_io_cstr_dup("arg1"); + args[0].value.mutable_flag = false; + args[0].attribute.name = pizza_io_cstr_dup("arg1_name"); + args[0].attribute.description = pizza_io_cstr_dup("First argument"); + args[0].attribute.id = pizza_io_cstr_dup("1"); + + args[1].type = FOSSIL_MOCK_PIZZA_TYPE_CSTR; + args[1].value.data = pizza_io_cstr_dup("arg2"); + args[1].value.mutable_flag = false; + args[1].attribute.name = pizza_io_cstr_dup("arg2_name"); + args[1].attribute.description = pizza_io_cstr_dup("Second argument"); + args[1].attribute.id = pizza_io_cstr_dup("2"); + + // Add a mock call using the macro + MOCK_ADD_CALL(list, "test_function", args, 2); + + // Test cases + FOSSIL_TEST_ASSUME(list.size == 1, "fossil_mock_calllist_t size should be 1 after adding a call using macro"); + FOSSIL_TEST_ASSUME(strcmp(list.head->function_name, "test_function") == 0, "Function name should be 'test_function'"); + FOSSIL_TEST_ASSUME(list.head->num_args == 2, "Number of arguments should be 2"); + FOSSIL_TEST_ASSUME(strcmp(list.head->arguments[0].value.data, "arg1") == 0, "First argument should be 'arg1'"); + FOSSIL_TEST_ASSUME(strcmp(list.head->arguments[1].value.data, "arg2") == 0, "Second argument should be 'arg2'"); } // end case -FOSSIL_TEST_CASE(c_mock_struct_creation) { +FOSSIL_TEST(c_mock_macro_destruction) { + // Initialize the mock call list using the macro + fossil_mock_calllist_t list; + MOCK_INIT(list); + + // Create mock arguments + fossil_mock_pizza_t args[2]; + args[0].type = FOSSIL_MOCK_PIZZA_TYPE_CSTR; + args[0].value.data = pizza_io_cstr_dup("arg1"); + args[0].value.mutable_flag = false; + args[0].attribute.name = pizza_io_cstr_dup("arg1_name"); + args[0].attribute.description = pizza_io_cstr_dup("First argument"); + args[0].attribute.id = pizza_io_cstr_dup("1"); + + args[1].type = FOSSIL_MOCK_PIZZA_TYPE_CSTR; + args[1].value.data = pizza_io_cstr_dup("arg2"); + args[1].value.mutable_flag = false; + args[1].attribute.name = pizza_io_cstr_dup("arg2_name"); + args[1].attribute.description = pizza_io_cstr_dup("Second argument"); + args[1].attribute.id = pizza_io_cstr_dup("2"); + + // Add a mock call using the macro + MOCK_ADD_CALL(list, "test_function", args, 2); + + // Destroy the mock call list using the macro + MOCK_DESTROY(list); + // Test cases - MockStruct instance; - instance.a = 5; - instance.b = 'c'; - FOSSIL_TEST_ASSUME(instance.a == 5, "Mock struct member 'a' should be 5"); - FOSSIL_TEST_ASSUME(instance.b == 'c', "Mock struct member 'b' should be 'c'"); + FOSSIL_TEST_ASSUME(list.head == NULL, "fossil_mock_calllist_t head should be NULL after destruction using macro"); + FOSSIL_TEST_ASSUME(list.tail == NULL, "fossil_mock_calllist_t tail should be NULL after destruction using macro"); + FOSSIL_TEST_ASSUME(list.size == 0, "fossil_mock_calllist_t size should be 0 after destruction using macro"); } // end case // * * * * * * * * * * * * * * * * * * * * * * * * @@ -165,12 +330,16 @@ FOSSIL_TEST_GROUP(c_mock_test_cases) { FOSSIL_TEST_ADD(c_mock_suite, c_mock_call_list_initialization); FOSSIL_TEST_ADD(c_mock_suite, c_mock_call_list_addition); FOSSIL_TEST_ADD(c_mock_suite, c_mock_call_list_destruction); - FOSSIL_TEST_ADD(c_mock_suite, c_mock_call_list_initialization_macro); - FOSSIL_TEST_ADD(c_mock_suite, c_mock_call_list_addition_macro); - FOSSIL_TEST_ADD(c_mock_suite, c_mock_call_list_destruction_macro); FOSSIL_TEST_ADD(c_mock_suite, c_mock_function_creation); FOSSIL_TEST_ADD(c_mock_suite, c_mock_alias_creation); FOSSIL_TEST_ADD(c_mock_suite, c_mock_struct_creation); + FOSSIL_TEST_ADD(c_mock_suite, c_mock_call_list_type_handling); + FOSSIL_TEST_ADD(c_mock_suite, c_mock_call_list_edge_cases); + FOSSIL_TEST_ADD(c_mock_suite, c_mock_call_list_large_arguments); + + FOSSIL_TEST_ADD(c_mock_suite, c_mock_macro_initialization); + FOSSIL_TEST_ADD(c_mock_suite, c_mock_macro_addition); + FOSSIL_TEST_ADD(c_mock_suite, c_mock_macro_destruction); FOSSIL_TEST_REGISTER(c_mock_suite); } // end of group diff --git a/code/tests/cases/test_mock.cpp b/code/tests/cases/test_mock.cpp index baf8695c..23462907 100644 --- a/code/tests/cases/test_mock.cpp +++ b/code/tests/cases/test_mock.cpp @@ -7,14 +7,13 @@ * herein is subject to the terms and conditions defined in the project license. * * Author: Michael Gene Brockus (Dreamer) - * Date: 07/01/2024 + * Date: 04/05/2014 * - * Copyright (C) 2024 Fossil Logic. All rights reserved. + * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. * ----------------------------------------------------------------------------- */ -#include -#include -#include +#include +#include // * * * * * * * * * * * * * * * * * * * * * * * * @@ -25,7 +24,7 @@ // * * * * * * * * * * * * * * * * * * * * * * * * // Define the test suite and add test cases -FOSSIL_TEST_SUITE(cpp_mock_suite); +FOSSIL_SUITE(cpp_mock_suite); // Setup function for the test suite FOSSIL_SETUP(cpp_mock_suite) { @@ -46,7 +45,7 @@ FOSSIL_MOCK_STRUCT(MockStruct) { }; // Example of creating a mock function using the macro -FOSSIL_MOCK_FUNC(int, mock_function, int a, int b) { +FOSSIL_MOCK_FUNC(int, cpp_mock_function, int a, int b) { return a + b; } @@ -57,105 +56,271 @@ FOSSIL_MOCK_FUNC(int, mock_function, int a, int b) { // Domain-Driven Design (DDD) usage in the Fossil Logic project. // * * * * * * * * * * * * * * * * * * * * * * * * -FOSSIL_TEST_CASE(cpp_mock_call_list_initialization) { +FOSSIL_TEST(cpp_mock_call_list_initialization) { // Example of initializing a fossil_mock_calllist_t fossil_mock_calllist_t list; fossil_mock_init(&list); // Test cases - FOSSIL_TEST_ASSUME(list.head == NULL, "fossil_mock_calllist_t head should be NULL after initialization"); - FOSSIL_TEST_ASSUME(list.tail == NULL, "fossil_mock_calllist_t tail should be NULL after initialization"); + FOSSIL_TEST_ASSUME(list.head == nullptr, "fossil_mock_calllist_t head should be nullptr after initialization"); + FOSSIL_TEST_ASSUME(list.tail == nullptr, "fossil_mock_calllist_t tail should be nullptr after initialization"); FOSSIL_TEST_ASSUME(list.size == 0, "fossil_mock_calllist_t size should be 0 after initialization"); } // end case -FOSSIL_TEST_CASE(cpp_mock_call_list_addition) { +FOSSIL_TEST(cpp_mock_call_list_addition) { // Example of adding a fossil_mock_call_t to a fossil_mock_calllist_t fossil_mock_calllist_t list; fossil_mock_init(&list); - const char* args[] = {"arg1", "arg2"}; - fossil_mock_add_call(&list, "test_function", (char**)args, 2); + + // Create mock arguments + fossil_mock_pizza_t args[2]; + args[0].type = FOSSIL_MOCK_PIZZA_TYPE_CSTR; + args[0].value.data = pizza_io_cstr_dup("arg1"); + args[0].value.mutable_flag = false; + args[0].attribute.name = pizza_io_cstr_dup("arg1_name"); + args[0].attribute.description = pizza_io_cstr_dup("First argument"); + args[0].attribute.id = pizza_io_cstr_dup("1"); + + args[1].type = FOSSIL_MOCK_PIZZA_TYPE_CSTR; + args[1].value.data = pizza_io_cstr_dup("arg2"); + args[1].value.mutable_flag = false; + args[1].attribute.name = pizza_io_cstr_dup("arg2_name"); + args[1].attribute.description = pizza_io_cstr_dup("Second argument"); + args[1].attribute.id = pizza_io_cstr_dup("2"); + + fossil_mock_add_call(&list, "test_function", args, 2); // Test cases FOSSIL_TEST_ASSUME(list.size == 1, "fossil_mock_calllist_t size should be 1 after adding a call"); - //FOSSIL_TEST_ASSUME(list.head->function_name == "test_function", "Function name should be 'test_function'"); + FOSSIL_TEST_ASSUME(strcmp(list.head->function_name, "test_function") == 0, "Function name should be 'test_function'"); FOSSIL_TEST_ASSUME(list.head->num_args == 2, "Number of arguments should be 2"); - FOSSIL_TEST_ASSUME(std::strcmp(list.head->arguments[0], "arg1") == 0, "First argument should be 'arg1'"); - FOSSIL_TEST_ASSUME(std::strcmp(list.head->arguments[1], "arg2") == 0, "Second argument should be 'arg2'"); + FOSSIL_TEST_ASSUME(strcmp(list.head->arguments[0].value.data, "arg1") == 0, "First argument should be 'arg1'"); + FOSSIL_TEST_ASSUME(strcmp(list.head->arguments[1].value.data, "arg2") == 0, "Second argument should be 'arg2'"); } // end case -FOSSIL_TEST_CASE(cpp_mock_call_list_destruction) { +FOSSIL_TEST(cpp_mock_call_list_destruction) { // Example of destroying a fossil_mock_calllist_t fossil_mock_calllist_t list; fossil_mock_init(&list); - const char* args[] = {"arg1", "arg2"}; - fossil_mock_add_call(&list, "test_function", (char**)args, 2); + + // Create mock arguments + fossil_mock_pizza_t args[2]; + args[0].type = FOSSIL_MOCK_PIZZA_TYPE_CSTR; + args[0].value.data = pizza_io_cstr_dup("arg1"); + args[0].value.mutable_flag = false; + args[0].attribute.name = pizza_io_cstr_dup("arg1_name"); + args[0].attribute.description = pizza_io_cstr_dup("First argument"); + args[0].attribute.id = pizza_io_cstr_dup("1"); + + args[1].type = FOSSIL_MOCK_PIZZA_TYPE_CSTR; + args[1].value.data = pizza_io_cstr_dup("arg2"); + args[1].value.mutable_flag = false; + args[1].attribute.name = pizza_io_cstr_dup("arg2_name"); + args[1].attribute.description = pizza_io_cstr_dup("Second argument"); + args[1].attribute.id = pizza_io_cstr_dup("2"); + + fossil_mock_add_call(&list, "test_function", args, 2); + FOSSIL_TEST_ASSUME(list.size == 1, "fossil_mock_calllist_t size should be 1 after adding a call"); FOSSIL_TEST_ASSUME(strcmp(list.head->function_name, "test_function") == 0, "Function name should be 'test_function'"); FOSSIL_TEST_ASSUME(list.head->num_args == 2, "Number of arguments should be 2"); - - fossil_mock_destroy(&list); // not allowed to access due to the object being freed + + fossil_mock_destroy(&list); } // end case -FOSSIL_TEST_CASE(cpp_mock_call_list_initialization_macro) { - // Example of initializing a fossil_mock_calllist_t using the macro - fossil_mock_calllist_t list; - MOCK_INIT(list); +FOSSIL_TEST(cpp_mock_function_creation) { + // Test cases + FOSSIL_TEST_ASSUME(fossil_mockup_cpp_mock_function(2, 3) == 5, "Mock function should return the sum of its arguments"); +} // end case + +FOSSIL_TEST(cpp_mock_alias_creation) { + // Example of creating a type alias using the macro // Test cases - FOSSIL_TEST_ASSUME(list.head == NULL, "fossil_mock_calllist_t head should be NULL after initialization"); - FOSSIL_TEST_ASSUME(list.tail == NULL, "fossil_mock_calllist_t tail should be NULL after initialization"); - FOSSIL_TEST_ASSUME(list.size == 0, "fossil_mock_calllist_t size should be 0 after initialization"); + MockInt x = 10; + FOSSIL_TEST_ASSUME(x == 10, "Mock alias should behave like the original type"); } // end case -FOSSIL_TEST_CASE(cpp_mock_call_list_addition_macro) { - // Example of adding a fossil_mock_call_t to a fossil_mock_calllist_t using the macro +FOSSIL_TEST(cpp_mock_struct_creation) { + // Test cases + MockStruct instance; + instance.a = 5; + instance.b = 'c'; + FOSSIL_TEST_ASSUME(instance.a == 5, "Mock struct member 'a' should be 5"); + FOSSIL_TEST_ASSUME(instance.b == 'c', "Mock struct member 'b' should be 'c'"); +} // end case + +FOSSIL_TEST(cpp_mock_call_list_type_handling) { + // Initialize the mock call list fossil_mock_calllist_t list; - MOCK_INIT(list); - const char* args[] = {"arg1", "arg2"}; - MOCK_ADD_CALL(list, "test_function", (char**)args, 2); + fossil_mock_init(&list); + + // Create mock arguments with various types + fossil_mock_pizza_t args[3]; + args[0].type = FOSSIL_MOCK_PIZZA_TYPE_I32; + args[0].value.data = pizza_io_cstr_dup("42"); + args[0].value.mutable_flag = false; + args[0].attribute.name = pizza_io_cstr_dup("arg1"); + args[0].attribute.description = pizza_io_cstr_dup("Integer argument"); + args[0].attribute.id = pizza_io_cstr_dup("1"); + + args[1].type = FOSSIL_MOCK_PIZZA_TYPE_CSTR; + args[1].value.data = pizza_io_cstr_dup("Hello"); + args[1].value.mutable_flag = true; + args[1].attribute.name = pizza_io_cstr_dup("arg2"); + args[1].attribute.description = pizza_io_cstr_dup("String argument"); + args[1].attribute.id = pizza_io_cstr_dup("2"); + + args[2].type = FOSSIL_MOCK_PIZZA_TYPE_BOOL; + args[2].value.data = pizza_io_cstr_dup("true"); + args[2].value.mutable_flag = false; + args[2].attribute.name = pizza_io_cstr_dup("arg3"); + args[2].attribute.description = pizza_io_cstr_dup("Boolean argument"); + args[2].attribute.id = pizza_io_cstr_dup("3"); + + // Add a mock call with the arguments + fossil_mock_add_call(&list, "test_function", args, 3); // Test cases FOSSIL_TEST_ASSUME(list.size == 1, "fossil_mock_calllist_t size should be 1 after adding a call"); - //FOSSIL_TEST_ASSUME(list.head->function_name == "test_function", "Function name should be 'test_function'"); - FOSSIL_TEST_ASSUME(list.head->num_args == 2, "Number of arguments should be 2"); - FOSSIL_TEST_ASSUME(std::strcmp(list.head->arguments[0], "arg1") == 0, "First argument should be 'arg1'"); - FOSSIL_TEST_ASSUME(std::strcmp(list.head->arguments[1], "arg2") == 0, "Second argument should be 'arg2'"); + FOSSIL_TEST_ASSUME(strcmp(list.head->function_name, "test_function") == 0, "Function name should be 'test_function'"); + FOSSIL_TEST_ASSUME(list.head->num_args == 3, "Number of arguments should be 3"); + + FOSSIL_TEST_ASSUME(list.head->arguments[0].type == FOSSIL_MOCK_PIZZA_TYPE_I32, "First argument type should be I32"); + FOSSIL_TEST_ASSUME(strcmp(list.head->arguments[0].value.data, "42") == 0, "First argument value should be '42'"); + FOSSIL_TEST_ASSUME(strcmp(list.head->arguments[0].attribute.name, "arg1") == 0, "First argument name should be 'arg1'"); + + FOSSIL_TEST_ASSUME(list.head->arguments[1].type == FOSSIL_MOCK_PIZZA_TYPE_CSTR, "Second argument type should be CSTR"); + FOSSIL_TEST_ASSUME(strcmp(list.head->arguments[1].value.data, "Hello") == 0, "Second argument value should be 'Hello'"); + FOSSIL_TEST_ASSUME(strcmp(list.head->arguments[1].attribute.name, "arg2") == 0, "Second argument name should be 'arg2'"); + + FOSSIL_TEST_ASSUME(list.head->arguments[2].type == FOSSIL_MOCK_PIZZA_TYPE_BOOL, "Third argument type should be BOOL"); + FOSSIL_TEST_ASSUME(strcmp(list.head->arguments[2].value.data, "true") == 0, "Third argument value should be 'true'"); + FOSSIL_TEST_ASSUME(strcmp(list.head->arguments[2].attribute.name, "arg3") == 0, "Third argument name should be 'arg3'"); + + // Clean up + fossil_mock_destroy(&list); } // end case -FOSSIL_TEST_CASE(cpp_mock_call_list_destruction_macro) { - // Example of destroying a fossil_mock_calllist_t using the macro +FOSSIL_TEST(cpp_mock_call_list_edge_cases) { + // Initialize the mock call list fossil_mock_calllist_t list; - MOCK_INIT(list); - const char* args[] = {"arg1", "arg2"}; - MOCK_ADD_CALL(list, "test_function", (char**)args, 2); - FOSSIL_TEST_ASSUME(list.size == 1, "fossil_mock_calllist_t size should be 1 after adding a call"); - FOSSIL_TEST_ASSUME(strcmp(list.head->function_name, "test_function") == 0, "Function name should be 'test_function'"); - FOSSIL_TEST_ASSUME(list.head->num_args == 2, "Number of arguments should be 2"); + fossil_mock_init(&list); + + // Add a call with no arguments + fossil_mock_add_call(&list, "no_args_function", nullptr, 0); + + // Test cases + FOSSIL_TEST_ASSUME(list.size == 1, "fossil_mock_calllist_t size should be 1 after adding a call with no arguments"); + FOSSIL_TEST_ASSUME(strcmp(list.head->function_name, "no_args_function") == 0, "Function name should be 'no_args_function'"); + FOSSIL_TEST_ASSUME(list.head->num_args == 0, "Number of arguments should be 0"); - MOCK_DESTROY(list); // not allowed to access due to the object being freed + // Clean up + fossil_mock_destroy(&list); } // end case -FOSSIL_TEST_CASE(cpp_mock_function_creation) { +FOSSIL_TEST(cpp_mock_call_list_large_arguments) { + // Initialize the mock call list + fossil_mock_calllist_t list; + fossil_mock_init(&list); + + // Create a large number of mock arguments + const int num_args = 100; + fossil_mock_pizza_t args[num_args]; + for (int i = 0; i < num_args; ++i) { + args[i].type = FOSSIL_MOCK_PIZZA_TYPE_I32; + args[i].value.data = pizza_io_cstr_dup("42"); + args[i].value.mutable_flag = false; + args[i].attribute.name = pizza_io_cstr_dup("arg"); + args[i].attribute.description = pizza_io_cstr_dup("Large argument test"); + args[i].attribute.id = pizza_io_cstr_dup("id"); + } + + // Add a mock call with the large number of arguments + fossil_mock_add_call(&list, "large_args_function", args, num_args); + // Test cases - FOSSIL_TEST_ASSUME(fossil_mockup_mock_function(2, 3) == 5, "Mock function should return the sum of its arguments"); + FOSSIL_TEST_ASSUME(list.size == 1, "fossil_mock_calllist_t size should be 1 after adding a call with large arguments"); + FOSSIL_TEST_ASSUME(list.head->num_args == num_args, "Number of arguments should match the large number"); + + // Clean up + fossil_mock_destroy(&list); } // end case -FOSSIL_TEST_CASE(cpp_mock_alias_creation) { - // Example of creating a type alias using the macro - +FOSSIL_TEST(cpp_mock_macro_initialization) { + // Initialize the mock call list using the macro + fossil_mock_calllist_t list; + MOCK_INIT(list); // Test cases - MockInt x = 10; - FOSSIL_TEST_ASSUME(x == 10, "Mock alias should behave like the original type"); + FOSSIL_TEST_ASSUME(list.head == nullptr, "fossil_mock_calllist_t head should be nullptr after initialization using macro"); + FOSSIL_TEST_ASSUME(list.tail == nullptr, "fossil_mock_calllist_t tail should be nullptr after initialization using macro"); + FOSSIL_TEST_ASSUME(list.size == 0, "fossil_mock_calllist_t size should be 0 after initialization using macro"); +} // end case + +FOSSIL_TEST(cpp_mock_macro_addition) { + // Initialize the mock call list using the macro + fossil_mock_calllist_t list; + MOCK_INIT(list); + + // Create mock arguments + fossil_mock_pizza_t args[2]; + args[0].type = FOSSIL_MOCK_PIZZA_TYPE_CSTR; + args[0].value.data = pizza_io_cstr_dup("arg1"); + args[0].value.mutable_flag = false; + args[0].attribute.name = pizza_io_cstr_dup("arg1_name"); + args[0].attribute.description = pizza_io_cstr_dup("First argument"); + args[0].attribute.id = pizza_io_cstr_dup("1"); + + args[1].type = FOSSIL_MOCK_PIZZA_TYPE_CSTR; + args[1].value.data = pizza_io_cstr_dup("arg2"); + args[1].value.mutable_flag = false; + args[1].attribute.name = pizza_io_cstr_dup("arg2_name"); + args[1].attribute.description = pizza_io_cstr_dup("Second argument"); + args[1].attribute.id = pizza_io_cstr_dup("2"); + + // Add a mock call using the macro + MOCK_ADD_CALL(list, "test_function", args, 2); + + // Test cases + FOSSIL_TEST_ASSUME(list.size == 1, "fossil_mock_calllist_t size should be 1 after adding a call using macro"); + FOSSIL_TEST_ASSUME(strcmp(list.head->function_name, "test_function") == 0, "Function name should be 'test_function'"); + FOSSIL_TEST_ASSUME(list.head->num_args == 2, "Number of arguments should be 2"); + FOSSIL_TEST_ASSUME(strcmp(list.head->arguments[0].value.data, "arg1") == 0, "First argument should be 'arg1'"); + FOSSIL_TEST_ASSUME(strcmp(list.head->arguments[1].value.data, "arg2") == 0, "Second argument should be 'arg2'"); } // end case -FOSSIL_TEST_CASE(cpp_mock_struct_creation) { +FOSSIL_TEST(cpp_mock_macro_destruction) { + // Initialize the mock call list using the macro + fossil_mock_calllist_t list; + MOCK_INIT(list); + + // Create mock arguments + fossil_mock_pizza_t args[2]; + args[0].type = FOSSIL_MOCK_PIZZA_TYPE_CSTR; + args[0].value.data = pizza_io_cstr_dup("arg1"); + args[0].value.mutable_flag = false; + args[0].attribute.name = pizza_io_cstr_dup("arg1_name"); + args[0].attribute.description = pizza_io_cstr_dup("First argument"); + args[0].attribute.id = pizza_io_cstr_dup("1"); + + args[1].type = FOSSIL_MOCK_PIZZA_TYPE_CSTR; + args[1].value.data = pizza_io_cstr_dup("arg2"); + args[1].value.mutable_flag = false; + args[1].attribute.name = pizza_io_cstr_dup("arg2_name"); + args[1].attribute.description = pizza_io_cstr_dup("Second argument"); + args[1].attribute.id = pizza_io_cstr_dup("2"); + + // Add a mock call using the macro + MOCK_ADD_CALL(list, "test_function", args, 2); + + // Destroy the mock call list using the macro + MOCK_DESTROY(list); + // Test cases - MockStruct instance; - instance.a = 5; - instance.b = 'c'; - FOSSIL_TEST_ASSUME(instance.a == 5, "Mock struct member 'a' should be 5"); - FOSSIL_TEST_ASSUME(instance.b == 'c', "Mock struct member 'b' should be 'c'"); + FOSSIL_TEST_ASSUME(list.head == nullptr, "fossil_mock_calllist_t head should be nullptr after destruction using macro"); + FOSSIL_TEST_ASSUME(list.tail == nullptr, "fossil_mock_calllist_t tail should be nullptr after destruction using macro"); + FOSSIL_TEST_ASSUME(list.size == 0, "fossil_mock_calllist_t size should be 0 after destruction using macro"); } // end case // * * * * * * * * * * * * * * * * * * * * * * * * @@ -165,12 +330,16 @@ FOSSIL_TEST_GROUP(cpp_mock_test_cases) { FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_call_list_initialization); FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_call_list_addition); FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_call_list_destruction); - FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_call_list_initialization_macro); - FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_call_list_addition_macro); - FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_call_list_destruction_macro); FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_function_creation); FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_alias_creation); FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_struct_creation); + FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_call_list_type_handling); + FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_call_list_edge_cases); + FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_call_list_large_arguments); + + FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_macro_initialization); + FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_macro_addition); + FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_macro_destruction); FOSSIL_TEST_REGISTER(cpp_mock_suite); } // end of group diff --git a/code/tests/cases/test_sample.c b/code/tests/cases/test_sample.c index 5009d8cd..1e7802d2 100644 --- a/code/tests/cases/test_sample.c +++ b/code/tests/cases/test_sample.c @@ -7,13 +7,13 @@ * herein is subject to the terms and conditions defined in the project license. * * Author: Michael Gene Brockus (Dreamer) - * Date: 07/01/2024 + * Date: 04/05/2014 * - * Copyright (C) 2024 Fossil Logic. All rights reserved. + * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. * ----------------------------------------------------------------------------- */ -#include "fossil/test/framework.h" +#include "fossil/pizza/framework.h" // Test data structure for a sample test FOSSIL_MOCK_STRUCT(CSampleTestData) { @@ -32,10 +32,10 @@ FOSSIL_TEARDOWN(sample_suite) { } // Define the test suite and add test cases -FOSSIL_TEST_SUITE(sample_suite); +FOSSIL_SUITE(sample_suite); // A simple test case to check if input + 1 equals expected_output -FOSSIL_TEST_CASE(test_input_increment) { +FOSSIL_TEST(test_input_increment) { CSampleTestData data = { .input = 5, .expected_output = 6 }; int actual_output = data.input + 1; @@ -44,7 +44,7 @@ FOSSIL_TEST_CASE(test_input_increment) { } // A simple test case to check if input - 1 equals expected_output -FOSSIL_TEST_CASE(test_input_decrement) { +FOSSIL_TEST(test_input_decrement) { CSampleTestData data = { .input = 5, .expected_output = 4 }; int actual_output = data.input - 1; @@ -53,7 +53,7 @@ FOSSIL_TEST_CASE(test_input_decrement) { } // A simple test case to check if input * 2 equals expected_output -FOSSIL_TEST_CASE(test_input_double) { +FOSSIL_TEST(test_input_double) { CSampleTestData data = { .input = 5, .expected_output = 10 }; int actual_output = data.input * 2; @@ -62,7 +62,7 @@ FOSSIL_TEST_CASE(test_input_double) { } // A simple test case to check if input / 2 equals expected_output -FOSSIL_TEST_CASE(test_input_half) { +FOSSIL_TEST(test_input_half) { CSampleTestData data = { .input = 10, .expected_output = 5 }; int actual_output = data.input / 2; @@ -70,12 +70,8 @@ FOSSIL_TEST_CASE(test_input_half) { FOSSIL_TEST_ASSUME(actual_output == data.expected_output, "Half test failed"); } -FOSSIL_TEST_CASE(test_should_not_run) { - FOSSIL_TEST_ASSUME(1 == 0, "This test should not run"); -} - // A simple test case to check if input % 2 equals expected_output -FOSSIL_TEST_CASE(test_input_modulo) { +FOSSIL_TEST(test_input_modulo) { CSampleTestData data = { .input = 5, .expected_output = 1 }; int actual_output = data.input % 2; @@ -84,7 +80,7 @@ FOSSIL_TEST_CASE(test_input_modulo) { } // A simple test case to check if input squared equals expected_output -FOSSIL_TEST_CASE(test_input_square) { +FOSSIL_TEST(test_input_square) { CSampleTestData data = { .input = 3, .expected_output = 9 }; int actual_output = data.input * data.input; @@ -93,7 +89,7 @@ FOSSIL_TEST_CASE(test_input_square) { } // A simple test case to check if input is equal to expected_output -FOSSIL_TEST_CASE(test_input_equal) { +FOSSIL_TEST(test_input_equal) { CSampleTestData data = { .input = 7, .expected_output = 7 }; int actual_output = data.input; @@ -101,10 +97,6 @@ FOSSIL_TEST_CASE(test_input_equal) { FOSSIL_TEST_ASSUME(actual_output == data.expected_output, "Equality test failed"); } -FOSSIL_TEST_CASE(test_has_no_assertions) { - // This test has no assertions -} - FOSSIL_TEST_GROUP(c_sample_test_cases) { FOSSIL_TEST_ADD(sample_suite, test_input_increment); FOSSIL_TEST_ADD(sample_suite, test_input_decrement); @@ -113,9 +105,6 @@ FOSSIL_TEST_GROUP(c_sample_test_cases) { FOSSIL_TEST_ADD(sample_suite, test_input_modulo); FOSSIL_TEST_ADD(sample_suite, test_input_square); FOSSIL_TEST_ADD(sample_suite, test_input_equal); - FOSSIL_TEST_ADD(sample_suite, test_has_no_assertions); // Should be detected as empty - - FOSSIL_TEST_SKIP(test_should_not_run, "This test should not run"); FOSSIL_TEST_REGISTER(sample_suite); } diff --git a/code/tests/cases/test_sample.cpp b/code/tests/cases/test_sample.cpp index 8be91a48..fff05d6d 100644 --- a/code/tests/cases/test_sample.cpp +++ b/code/tests/cases/test_sample.cpp @@ -7,13 +7,13 @@ * herein is subject to the terms and conditions defined in the project license. * * Author: Michael Gene Brockus (Dreamer) - * Date: 07/01/2024 + * Date: 04/05/2014 * - * Copyright (C) 2024 Fossil Logic. All rights reserved. + * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. * ----------------------------------------------------------------------------- */ -#include "fossil/test/framework.h" +#include "fossil/pizza/framework.h" // Test data structure for a sample test FOSSIL_MOCK_STRUCT(CppSampleTestData) { @@ -32,10 +32,10 @@ FOSSIL_TEARDOWN(cpp_sample_suite) { } // Define the test suite and add test cases -FOSSIL_TEST_SUITE(cpp_sample_suite); +FOSSIL_SUITE(cpp_sample_suite); // A simple test case to check if input + 1 equals expected_output -FOSSIL_TEST_CASE(cpp_test_input_increment) { +FOSSIL_TEST(cpp_test_input_increment) { CppSampleTestData data = {5, 6}; // Simplified initialization int actual_output = data.input + 1; @@ -44,7 +44,7 @@ FOSSIL_TEST_CASE(cpp_test_input_increment) { } // A simple test case to check if input - 1 equals expected_output -FOSSIL_TEST_CASE(cpp_test_input_decrement) { +FOSSIL_TEST(cpp_test_input_decrement) { CppSampleTestData data = {5, 4}; // Simplified initialization int actual_output = data.input - 1; @@ -53,7 +53,7 @@ FOSSIL_TEST_CASE(cpp_test_input_decrement) { } // A simple test case to check if input * 2 equals expected_output -FOSSIL_TEST_CASE(cpp_test_input_double) { +FOSSIL_TEST(cpp_test_input_double) { CppSampleTestData data = {5, 10}; // Simplified initialization int actual_output = data.input * 2; @@ -62,7 +62,7 @@ FOSSIL_TEST_CASE(cpp_test_input_double) { } // A simple test case to check if input / 2 equals expected_output -FOSSIL_TEST_CASE(cpp_test_input_half) { +FOSSIL_TEST(cpp_test_input_half) { CppSampleTestData data = {10, 5}; // Simplified initialization int actual_output = data.input / 2; @@ -70,12 +70,8 @@ FOSSIL_TEST_CASE(cpp_test_input_half) { FOSSIL_TEST_ASSUME(actual_output == data.expected_output, "Half test failed"); } -FOSSIL_TEST_CASE(cpp_test_should_not_run) { - FOSSIL_TEST_ASSUME(1 == 0, "This test should not run"); -} - // A simple test case to check if input % 2 equals expected_output -FOSSIL_TEST_CASE(cpp_test_input_modulo) { +FOSSIL_TEST(cpp_test_input_modulo) { CppSampleTestData data = { 5, 1 }; int actual_output = data.input % 2; @@ -84,7 +80,7 @@ FOSSIL_TEST_CASE(cpp_test_input_modulo) { } // A simple test case to check if input squared equals expected_output -FOSSIL_TEST_CASE(cpp_test_input_square) { +FOSSIL_TEST(cpp_test_input_square) { CppSampleTestData data = { 3, 9 }; int actual_output = data.input * data.input; @@ -93,7 +89,7 @@ FOSSIL_TEST_CASE(cpp_test_input_square) { } // A simple test case to check if input is equal to expected_output -FOSSIL_TEST_CASE(cpp_test_input_equal) { +FOSSIL_TEST(cpp_test_input_equal) { CppSampleTestData data = { 7, 7 }; int actual_output = data.input; @@ -101,10 +97,6 @@ FOSSIL_TEST_CASE(cpp_test_input_equal) { FOSSIL_TEST_ASSUME(actual_output == data.expected_output, "Equality test failed"); } -FOSSIL_TEST_CASE(cpp_test_has_no_assertions) { - // This test has no assertions -} - FOSSIL_TEST_GROUP(cpp_sample_test_cases) { FOSSIL_TEST_ADD(cpp_sample_suite, cpp_test_input_increment); FOSSIL_TEST_ADD(cpp_sample_suite, cpp_test_input_decrement); @@ -113,9 +105,6 @@ FOSSIL_TEST_GROUP(cpp_sample_test_cases) { FOSSIL_TEST_ADD(cpp_sample_suite, cpp_test_input_modulo); FOSSIL_TEST_ADD(cpp_sample_suite, cpp_test_input_square); FOSSIL_TEST_ADD(cpp_sample_suite, cpp_test_input_equal); - FOSSIL_TEST_ADD(cpp_sample_suite, cpp_test_has_no_assertions); // Should be detected as empty - - FOSSIL_TEST_SKIP(cpp_test_should_not_run, "This test should not run"); FOSSIL_TEST_REGISTER(cpp_sample_suite); } diff --git a/code/tests/cases/test_sanity.c b/code/tests/cases/test_sanity.c new file mode 100644 index 00000000..be4749d5 --- /dev/null +++ b/code/tests/cases/test_sanity.c @@ -0,0 +1,102 @@ +/* + * ----------------------------------------------------------------------------- + * Project: Fossil Logic + * + * This file is part of the Fossil Logic project, which aims to develop high- + * performance, cross-platform applications and libraries. The code contained + * herein is subject to the terms and conditions defined in the project license. + * + * Author: Michael Gene Brockus (Dreamer) + * Date: 04/05/2014 + * + * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. + * ----------------------------------------------------------------------------- + */ +#include + +// * * * * * * * * * * * * * * * * * * * * * * * * +// * Fossil Logic Test Utilites +// * * * * * * * * * * * * * * * * * * * * * * * * +// Setup steps for things like test fixtures and +// mock objects are set here. +// * * * * * * * * * * * * * * * * * * * * * * * * + +// Define the test suite and add test cases +FOSSIL_SUITE(c_sanity_suite); + +// Setup function for the test suite +FOSSIL_SETUP(c_sanity_suite) { + // Setup code here +} + +// Teardown function for the test suite +FOSSIL_TEARDOWN(c_sanity_suite) { + // Teardown code here +} + +// * * * * * * * * * * * * * * * * * * * * * * * * +// * Fossil Logic Test Cases +// * * * * * * * * * * * * * * * * * * * * * * * * +// The test cases below are provided as samples, inspired +// by the Meson build system's approach of using test cases +// as samples for library usage. +// * * * * * * * * * * * * * * * * * * * * * * * * + +FOSSIL_TEST(c_sanity_sys_execute) { + const char *command = "echo Hello, Fossil!"; + int result = FOSSIL_SANITY_SYS_EXECUTE(command); + + // Test cases + ASSUME_ITS_EQUAL_I32(result, 0); // Assuming the command executes successfully +} // end case + +FOSSIL_TEST(c_sanity_sys_getpid) { + int pid = FOSSIL_SANITY_SYS_GETPID(); + + // Test cases + ASSUME_NOT_EQUAL_I32(pid, 0); // Process ID should not be 0 +} // end case + +FOSSIL_TEST(c_sanity_sys_create_file) { + const char *filename = "test_file.txt"; + int result = FOSSIL_SANITY_SYS_CREATE_FILE(filename); + + // Test cases + ASSUME_ITS_EQUAL_I32(result, 0); // File creation should succeed + ASSUME_ITS_EQUAL_I32(FOSSIL_SANITY_SYS_FILE_EXISTS(filename), 1); // File should exist + + // Cleanup + remove(filename); +} // end case + +FOSSIL_TEST(c_sanity_sys_file_exists) { + const char *filename = "test_file_exists.txt"; + + // Ensure file does not exist initially + ASSUME_ITS_EQUAL_I32(FOSSIL_SANITY_SYS_FILE_EXISTS(filename), 0); + + // Create the file + FILE *file = fopen(filename, "w"); + ASSUME_NOT_CNULL(file); + fclose(file); + + // Test cases + ASSUME_ITS_EQUAL_I32(FOSSIL_SANITY_SYS_FILE_EXISTS(filename), 1); + + // Cleanup + remove(filename); +} // end case + + + +// * * * * * * * * * * * * * * * * * * * * * * * * +// * Fossil Logic Test Pool +// * * * * * * * * * * * * * * * * * * * * * * * * +FOSSIL_TEST_GROUP(c_sanity_test_cases) { + FOSSIL_TEST_ADD(c_sanity_suite, c_sanity_sys_execute); + FOSSIL_TEST_ADD(c_sanity_suite, c_sanity_sys_getpid); + FOSSIL_TEST_ADD(c_sanity_suite, c_sanity_sys_create_file); + FOSSIL_TEST_ADD(c_sanity_suite, c_sanity_sys_file_exists); + + FOSSIL_TEST_REGISTER(c_sanity_suite); +} // end of group diff --git a/code/tests/cases/test_sanity.cpp b/code/tests/cases/test_sanity.cpp new file mode 100644 index 00000000..0a1d3d0e --- /dev/null +++ b/code/tests/cases/test_sanity.cpp @@ -0,0 +1,102 @@ +/* + * ----------------------------------------------------------------------------- + * Project: Fossil Logic + * + * This file is part of the Fossil Logic project, which aims to develop high- + * performance, cross-platform applications and libraries. The code contained + * herein is subject to the terms and conditions defined in the project license. + * + * Author: Michael Gene Brockus (Dreamer) + * Date: 04/05/2014 + * + * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. + * ----------------------------------------------------------------------------- + */ +#include + +// * * * * * * * * * * * * * * * * * * * * * * * * +// * Fossil Logic Test Utilites +// * * * * * * * * * * * * * * * * * * * * * * * * +// Setup steps for things like test fixtures and +// mock objects are set here. +// * * * * * * * * * * * * * * * * * * * * * * * * + +// Define the test suite and add test cases +FOSSIL_SUITE(cpp_sanity_suite); + +// Setup function for the test suite +FOSSIL_SETUP(cpp_sanity_suite) { + // Setup code here +} + +// Teardown function for the test suite +FOSSIL_TEARDOWN(cpp_sanity_suite) { + // Teardown code here +} + +// * * * * * * * * * * * * * * * * * * * * * * * * +// * Fossil Logic Test Cases +// * * * * * * * * * * * * * * * * * * * * * * * * +// The test cases below are provided as samples, inspired +// by the Meson build system's approach of using test cases +// as samples for library usage. +// * * * * * * * * * * * * * * * * * * * * * * * * + +FOSSIL_TEST(cpp_sanity_sys_execute) { + const char *command = "echo Hello, Fossil!"; + int result = FOSSIL_SANITY_SYS_EXECUTE(command); + + // Test cases + ASSUME_ITS_EQUAL_I32(result, 0); // Assuming the command executes successfully +} // end case + +FOSSIL_TEST(cpp_sanity_sys_getpid) { + int pid = FOSSIL_SANITY_SYS_GETPID(); + + // Test cases + ASSUME_NOT_EQUAL_I32(pid, 0); // Process ID should not be 0 +} // end case + +FOSSIL_TEST(cpp_sanity_sys_create_file) { + const char *filename = "test_file.txt"; + int result = FOSSIL_SANITY_SYS_CREATE_FILE(filename); + + // Test cases + ASSUME_ITS_EQUAL_I32(result, 0); // File creation should succeed + ASSUME_ITS_EQUAL_I32(FOSSIL_SANITY_SYS_FILE_EXISTS(filename), 1); // File should exist + + // Cleanup + remove(filename); +} // end case + +FOSSIL_TEST(cpp_sanity_sys_file_exists) { + const char *filename = "test_file_exists.txt"; + + // Ensure file does not exist initially + ASSUME_ITS_EQUAL_I32(FOSSIL_SANITY_SYS_FILE_EXISTS(filename), 0); + + // Create the file + FILE *file = fopen(filename, "w"); + ASSUME_NOT_CNULL(file); + fclose(file); + + // Test cases + ASSUME_ITS_EQUAL_I32(FOSSIL_SANITY_SYS_FILE_EXISTS(filename), 1); + + // Cleanup + remove(filename); +} // end case + + + +// * * * * * * * * * * * * * * * * * * * * * * * * +// * Fossil Logic Test Pool +// * * * * * * * * * * * * * * * * * * * * * * * * +FOSSIL_TEST_GROUP(cpp_sanity_test_cases) { + FOSSIL_TEST_ADD(cpp_sanity_suite, cpp_sanity_sys_execute); + FOSSIL_TEST_ADD(cpp_sanity_suite, cpp_sanity_sys_getpid); + FOSSIL_TEST_ADD(cpp_sanity_suite, cpp_sanity_sys_create_file); + FOSSIL_TEST_ADD(cpp_sanity_suite, cpp_sanity_sys_file_exists); + + FOSSIL_TEST_REGISTER(cpp_sanity_suite); +} // end of group diff --git a/code/tests/cases/test_tdd.c b/code/tests/cases/test_tdd.c index d928eca0..43fd8549 100644 --- a/code/tests/cases/test_tdd.c +++ b/code/tests/cases/test_tdd.c @@ -7,12 +7,12 @@ * herein is subject to the terms and conditions defined in the project license. * * Author: Michael Gene Brockus (Dreamer) - * Date: 07/01/2024 + * Date: 04/05/2014 * - * Copyright (C) 2024 Fossil Logic. All rights reserved. + * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. * ----------------------------------------------------------------------------- */ -#include +#include // * * * * * * * * * * * * * * * * * * * * * * * * // * Fossil Logic Test Utilites @@ -22,7 +22,7 @@ // * * * * * * * * * * * * * * * * * * * * * * * * // Define the test suite and add test cases -FOSSIL_TEST_SUITE(c_tdd_suite); +FOSSIL_SUITE(c_tdd_suite); // Setup function for the test suite FOSSIL_SETUP(c_tdd_suite) { @@ -42,7 +42,7 @@ FOSSIL_TEARDOWN(c_tdd_suite) { // as samples for library usage. // * * * * * * * * * * * * * * * * * * * * * * * * -FOSSIL_TEST_CASE(c_assume_run_of_int) { +FOSSIL_TEST(c_assume_run_of_int) { int x = 42; int y = 20; @@ -54,7 +54,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_int) { FOSSIL_TEST_ASSUME(y <= x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_int8) { +FOSSIL_TEST(c_assume_run_of_int8) { int8_t x = 42; int8_t y = 20; @@ -66,7 +66,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_int8) { FOSSIL_TEST_ASSUME((int8_t)y <= (int8_t)x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_int16) { +FOSSIL_TEST(c_assume_run_of_int16) { int16_t x = 42; int16_t y = 20; @@ -78,7 +78,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_int16) { FOSSIL_TEST_ASSUME((int16_t)y <= (int16_t)x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_int32) { +FOSSIL_TEST(c_assume_run_of_int32) { int32_t x = 42; int32_t y = 20; @@ -90,7 +90,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_int32) { FOSSIL_TEST_ASSUME((int32_t)y <= (int32_t)x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_int64) { +FOSSIL_TEST(c_assume_run_of_int64) { int64_t x = 42; int64_t y = 20; @@ -102,7 +102,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_int64) { FOSSIL_TEST_ASSUME((int64_t)y <= (int64_t)x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_int8_shortcut) { +FOSSIL_TEST(c_assume_run_of_int8_shortcut) { int8_t x = 42; int8_t y = 20; @@ -114,7 +114,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_int8_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_I8((int8_t)y, (int8_t)x); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_int16_shortcut) { +FOSSIL_TEST(c_assume_run_of_int16_shortcut) { int16_t x = 42; int16_t y = 20; @@ -126,7 +126,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_int16_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_I16((int16_t)y, (int16_t)x); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_int32_shortcut) { +FOSSIL_TEST(c_assume_run_of_int32_shortcut) { int32_t x = 42; int32_t y = 20; @@ -138,7 +138,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_int32_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_I32((int32_t)y, (int32_t)x); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_int64_shortcut) { +FOSSIL_TEST(c_assume_run_of_int64_shortcut) { int64_t x = 42; int64_t y = 20; @@ -150,7 +150,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_int64_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_I64((int64_t)y, (int64_t)x); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_uint) { +FOSSIL_TEST(c_assume_run_of_uint) { unsigned int x = 42; unsigned int y = 20; @@ -162,7 +162,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_uint) { FOSSIL_TEST_ASSUME(y <= x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_uint8) { +FOSSIL_TEST(c_assume_run_of_uint8) { uint8_t x = 42; uint8_t y = 20; @@ -174,7 +174,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_uint8) { FOSSIL_TEST_ASSUME((uint8_t)y <= (uint8_t)x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_uint16) { +FOSSIL_TEST(c_assume_run_of_uint16) { uint16_t x = 42; uint16_t y = 20; @@ -186,7 +186,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_uint16) { FOSSIL_TEST_ASSUME((uint16_t)y <= (uint16_t)x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_uint32) { +FOSSIL_TEST(c_assume_run_of_uint32) { uint32_t x = 42; uint32_t y = 20; @@ -198,7 +198,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_uint32) { FOSSIL_TEST_ASSUME((uint32_t)y <= (uint32_t)x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_uint64) { +FOSSIL_TEST(c_assume_run_of_uint64) { uint64_t x = 42; uint64_t y = 20; @@ -210,7 +210,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_uint64) { FOSSIL_TEST_ASSUME((uint64_t)y <= (uint64_t)x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_uint8_shortcut) { +FOSSIL_TEST(c_assume_run_of_uint8_shortcut) { uint8_t x = 42; uint8_t y = 20; @@ -222,7 +222,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_uint8_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_U8((uint8_t)y, (uint8_t)x); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_uint16_shortcut) { +FOSSIL_TEST(c_assume_run_of_uint16_shortcut) { uint16_t x = 42; uint16_t y = 20; @@ -234,7 +234,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_uint16_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_U16((uint16_t)y, (uint16_t)x); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_uint32_shortcut) { +FOSSIL_TEST(c_assume_run_of_uint32_shortcut) { uint32_t x = 42; uint32_t y = 20; @@ -246,7 +246,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_uint32_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_U32((uint32_t)y, (uint32_t)x); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_uint64_shortcut) { +FOSSIL_TEST(c_assume_run_of_uint64_shortcut) { uint64_t x = 42; uint64_t y = 20; @@ -258,7 +258,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_uint64_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_U64((uint64_t)y, (uint64_t)x); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_hex) { +FOSSIL_TEST(c_assume_run_of_hex) { int x = 0x2A; // Hex for 42 int y = 0x14; // Hex for 20 @@ -270,7 +270,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_hex) { FOSSIL_TEST_ASSUME(y <= x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_hex8) { +FOSSIL_TEST(c_assume_run_of_hex8) { int x = 0x2A; // Hex for 42 int y = 0x14; // Hex for 20 @@ -282,7 +282,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_hex8) { FOSSIL_TEST_ASSUME(y <= x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_hex16) { +FOSSIL_TEST(c_assume_run_of_hex16) { int x = 0x2A; // Hex for 42 int y = 0x14; // Hex for 20 @@ -294,7 +294,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_hex16) { FOSSIL_TEST_ASSUME(y <= x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_hex32) { +FOSSIL_TEST(c_assume_run_of_hex32) { int x = 0x2A; // Hex for 42 int y = 0x14; // Hex for 20 @@ -306,7 +306,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_hex32) { FOSSIL_TEST_ASSUME(y <= x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_hex64) { +FOSSIL_TEST(c_assume_run_of_hex64) { int x = 0x2A; // Hex for 42 int y = 0x14; // Hex for 20 @@ -318,7 +318,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_hex64) { FOSSIL_TEST_ASSUME(y <= x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_hex8_shortcut) { +FOSSIL_TEST(c_assume_run_of_hex8_shortcut) { int x = 0x2A; // Hex for 42 int y = 0x14; // Hex for 20 @@ -330,7 +330,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_hex8_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_H8((int)y, (int)x); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_hex16_shortcut) { +FOSSIL_TEST(c_assume_run_of_hex16_shortcut) { int x = 0x2A; // Hex for 42 int y = 0x14; // Hex for 20 @@ -342,7 +342,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_hex16_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_H16((int)y, (int)x); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_hex32_shortcut) { +FOSSIL_TEST(c_assume_run_of_hex32_shortcut) { int x = 0x2A; // Hex for 42 int y = 0x14; // Hex for 20 @@ -354,7 +354,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_hex32_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_H32((int)y, (int)x); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_hex64_shortcut) { +FOSSIL_TEST(c_assume_run_of_hex64_shortcut) { int x = 0x2A; // Hex for 42 int y = 0x14; // Hex for 20 @@ -366,7 +366,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_hex64_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_H64((int)y, (int)x); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_octal8_shortcut) { +FOSSIL_TEST(c_assume_run_of_octal8_shortcut) { int8_t x = 052; // Octal for 42 int8_t y = 024; // Octal for 20 @@ -378,7 +378,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_octal8_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_O8(y, x); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_octal16_shortcut) { +FOSSIL_TEST(c_assume_run_of_octal16_shortcut) { int16_t x = 052; // Octal for 42 int16_t y = 024; // Octal for 20 @@ -390,7 +390,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_octal16_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_O16(y, x); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_octal32_shortcut) { +FOSSIL_TEST(c_assume_run_of_octal32_shortcut) { int32_t x = 052; // Octal for 42 int32_t y = 024; // Octal for 20 @@ -402,7 +402,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_octal32_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_O32(y, x); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_octal64_shortcut) { +FOSSIL_TEST(c_assume_run_of_octal64_shortcut) { int64_t x = 052; // Octal for 42 int64_t y = 024; // Octal for 20 @@ -414,7 +414,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_octal64_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_O64(y, x); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_float32) { +FOSSIL_TEST(c_assume_run_of_float32) { float x = 42.0f; float y = 20.0f; @@ -426,7 +426,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_float32) { ASSUME_ITS_LESS_OR_EQUAL_F32(y, x); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_float64) { +FOSSIL_TEST(c_assume_run_of_float64) { double x = 42.0; double y = 20.0; @@ -438,7 +438,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_float64) { ASSUME_ITS_LESS_OR_EQUAL_F64(y, x); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_boolean_true) { +FOSSIL_TEST(c_assume_run_of_boolean_true) { bool x = true; bool y = false; @@ -449,7 +449,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_boolean_true) { ASSUME_NOT_FALSE(x); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_boolean_false) { +FOSSIL_TEST(c_assume_run_of_boolean_false) { bool x = false; bool y = true; @@ -460,7 +460,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_boolean_false) { ASSUME_NOT_TRUE(x); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_boolean_expression) { +FOSSIL_TEST(c_assume_run_of_boolean_expression) { int a = 5; int b = 10; @@ -471,7 +471,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_boolean_expression) { ASSUME_NOT_FALSE(a < b); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_boolean_expression_negation) { +FOSSIL_TEST(c_assume_run_of_boolean_expression_negation) { int a = 5; int b = 10; @@ -482,7 +482,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_boolean_expression_negation) { ASSUME_NOT_FALSE(!(a > b)); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_boolean_complex_expression) { +FOSSIL_TEST(c_assume_run_of_boolean_complex_expression) { int a = 5; int b = 10; int c = 15; @@ -494,7 +494,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_boolean_complex_expression) { ASSUME_NOT_FALSE((a < b) && (b < c)); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_boolean_complex_expression_negation) { +FOSSIL_TEST(c_assume_run_of_boolean_complex_expression_negation) { int a = 5; int b = 10; int c = 15; @@ -506,7 +506,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_boolean_complex_expression_negation) { ASSUME_NOT_FALSE(!((a > b) || (b > c))); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_null_pointer) { +FOSSIL_TEST(c_assume_run_of_null_pointer) { void *ptr = NULL; // Test cases @@ -514,7 +514,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_null_pointer) { ASSUME_NOT_CNULL((void *)0x1); // Assuming a non-null pointer } // end case -FOSSIL_TEST_CASE(c_assume_run_of_pointer_equality) { +FOSSIL_TEST(c_assume_run_of_pointer_equality) { int a = 42; int *ptr1 = &a; int *ptr2 = &a; @@ -525,7 +525,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_pointer_equality) { ASSUME_NOT_EQUAL_PTR(ptr1, ptr3); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_size_equality) { +FOSSIL_TEST(c_assume_run_of_size_equality) { size_t size1 = 42; size_t size2 = 42; size_t size3 = 20; @@ -535,7 +535,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_size_equality) { ASSUME_NOT_EQUAL_SIZE(size1, size3); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_size_comparison) { +FOSSIL_TEST(c_assume_run_of_size_comparison) { size_t size1 = 42; size_t size2 = 20; @@ -548,7 +548,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_size_comparison) { ASSUME_ITS_MORE_OR_EQUAL_SIZE(size1, size1); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_within_range) { +FOSSIL_TEST(c_assume_run_of_within_range) { int x = 42; int y = 20; @@ -557,7 +557,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_within_range) { ASSUME_NOT_WITHIN_RANGE(y, 21, 30); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_within_range_u8) { +FOSSIL_TEST(c_assume_run_of_within_range_u8) { uint8_t x = 42; uint8_t y = 20; @@ -566,7 +566,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_within_range_u8) { ASSUME_NOT_WITHIN_RANGE_U8(y, 21, 30); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_within_range_u16) { +FOSSIL_TEST(c_assume_run_of_within_range_u16) { uint16_t x = 42; uint16_t y = 20; @@ -575,7 +575,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_within_range_u16) { ASSUME_NOT_WITHIN_RANGE_U16(y, 21, 30); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_within_range_u32) { +FOSSIL_TEST(c_assume_run_of_within_range_u32) { uint32_t x = 42; uint32_t y = 20; @@ -584,7 +584,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_within_range_u32) { ASSUME_NOT_WITHIN_RANGE_U32(y, 21, 30); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_within_range_u64) { +FOSSIL_TEST(c_assume_run_of_within_range_u64) { uint64_t x = 42; uint64_t y = 20; @@ -593,7 +593,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_within_range_u64) { ASSUME_NOT_WITHIN_RANGE_U64(y, 21, 30); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_within_range_i8) { +FOSSIL_TEST(c_assume_run_of_within_range_i8) { int8_t x = 42; int8_t y = 20; @@ -602,7 +602,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_within_range_i8) { ASSUME_NOT_WITHIN_RANGE_I8(y, 21, 30); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_within_range_i16) { +FOSSIL_TEST(c_assume_run_of_within_range_i16) { int16_t x = 42; int16_t y = 20; @@ -611,7 +611,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_within_range_i16) { ASSUME_NOT_WITHIN_RANGE_I16(y, 21, 30); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_within_range_i32) { +FOSSIL_TEST(c_assume_run_of_within_range_i32) { int32_t x = 42; int32_t y = 20; @@ -620,7 +620,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_within_range_i32) { ASSUME_NOT_WITHIN_RANGE_I32(y, 21, 30); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_within_range_i64) { +FOSSIL_TEST(c_assume_run_of_within_range_i64) { int64_t x = 42; int64_t y = 20; @@ -629,7 +629,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_within_range_i64) { ASSUME_NOT_WITHIN_RANGE_I64(y, 21, 30); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_within_range_f32) { +FOSSIL_TEST(c_assume_run_of_within_range_f32) { float x = 42.0f; float y = 20.0f; @@ -638,7 +638,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_within_range_f32) { ASSUME_NOT_WITHIN_RANGE_F32(y, 21.0f, 30.0f); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_within_range_f64) { +FOSSIL_TEST(c_assume_run_of_within_range_f64) { double x = 42.0; double y = 20.0; @@ -647,7 +647,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_within_range_f64) { ASSUME_NOT_WITHIN_RANGE_F64(y, 21.0, 30.0); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_within_range_bchar) { +FOSSIL_TEST(c_assume_run_of_within_range_bchar) { uint8_t x = 42; uint8_t y = 20; @@ -656,7 +656,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_within_range_bchar) { ASSUME_NOT_WITHIN_RANGE_BCHAR(y, 21, 30); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_within_range_cchar) { +FOSSIL_TEST(c_assume_run_of_within_range_cchar) { char x = 'A'; char y = 'B'; @@ -665,27 +665,7 @@ FOSSIL_TEST_CASE(c_assume_run_of_within_range_cchar) { ASSUME_NOT_WITHIN_RANGE_CCHAR(y, 'C', 'Z'); } // end case -FOSSIL_TEST_CASE(c_assume_run_of_within_range_wchar) { - wchar_t x = L'A'; - wchar_t y = L'B'; - - // Test cases - ASSUME_ITS_WITHIN_RANGE_WCHAR(x, L'A', L'Z'); - ASSUME_NOT_WITHIN_RANGE_WCHAR(y, L'C', L'Z'); -} // end case - -FOSSIL_TEST_CASE(c_assume_run_of_wstr) { - wchar_t *str1 = L"Hello"; - wchar_t *str2 = L"Hello"; - wchar_t *str3 = L"World"; - - // Test cases - ASSUME_ITS_EQUAL_WSTR(str1, str2); - ASSUME_NOT_EQUAL_WSTR(str1, str3); - ASSUME_ITS_LENGTH_EQUAL_WSTR(str1, 5); -} // end case - -FOSSIL_TEST_CASE(c_assume_run_of_cstr) { +FOSSIL_TEST(c_assume_run_of_cstr) { const char *str1 = "Hello"; const char *str2 = "Hello"; const char *str3 = "World"; @@ -756,8 +736,6 @@ FOSSIL_TEST_GROUP(c_tdd_test_cases) { FOSSIL_TEST_ADD(c_tdd_suite, c_assume_run_of_within_range_f64); FOSSIL_TEST_ADD(c_tdd_suite, c_assume_run_of_within_range_bchar); FOSSIL_TEST_ADD(c_tdd_suite, c_assume_run_of_within_range_cchar); - FOSSIL_TEST_ADD(c_tdd_suite, c_assume_run_of_within_range_wchar); - FOSSIL_TEST_ADD(c_tdd_suite, c_assume_run_of_wstr); FOSSIL_TEST_ADD(c_tdd_suite, c_assume_run_of_cstr); FOSSIL_TEST_REGISTER(c_tdd_suite); diff --git a/code/tests/cases/test_tdd.cpp b/code/tests/cases/test_tdd.cpp index b3a5bdf4..ee17c3e4 100644 --- a/code/tests/cases/test_tdd.cpp +++ b/code/tests/cases/test_tdd.cpp @@ -7,12 +7,12 @@ * herein is subject to the terms and conditions defined in the project license. * * Author: Michael Gene Brockus (Dreamer) - * Date: 07/01/2024 + * Date: 04/05/2014 * - * Copyright (C) 2024 Fossil Logic. All rights reserved. + * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. * ----------------------------------------------------------------------------- */ -#include +#include #include // * * * * * * * * * * * * * * * * * * * * * * * * @@ -23,7 +23,7 @@ // * * * * * * * * * * * * * * * * * * * * * * * * // Define the test suite and add test cases -FOSSIL_TEST_SUITE(cpp_tdd_suite); +FOSSIL_SUITE(cpp_tdd_suite); // Setup function for the test suite FOSSIL_SETUP(cpp_tdd_suite) { @@ -43,7 +43,7 @@ FOSSIL_TEARDOWN(cpp_tdd_suite) { // as samples for library usage. // * * * * * * * * * * * * * * * * * * * * * * * * -FOSSIL_TEST_CASE(cpp_assume_run_of_int) { +FOSSIL_TEST(cpp_assume_run_of_int) { int x = 42; int y = 20; @@ -55,7 +55,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_int) { FOSSIL_TEST_ASSUME(y <= x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_int8) { +FOSSIL_TEST(cpp_assume_run_of_int8) { int8_t x = 42; int8_t y = 20; @@ -67,7 +67,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_int8) { FOSSIL_TEST_ASSUME((int8_t)y <= (int8_t)x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_int16) { +FOSSIL_TEST(cpp_assume_run_of_int16) { int16_t x = 42; int16_t y = 20; @@ -79,7 +79,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_int16) { FOSSIL_TEST_ASSUME((int16_t)y <= (int16_t)x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_int32) { +FOSSIL_TEST(cpp_assume_run_of_int32) { int32_t x = 42; int32_t y = 20; @@ -91,7 +91,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_int32) { FOSSIL_TEST_ASSUME((int32_t)y <= (int32_t)x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_int64) { +FOSSIL_TEST(cpp_assume_run_of_int64) { int64_t x = 42; int64_t y = 20; @@ -103,7 +103,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_int64) { FOSSIL_TEST_ASSUME((int64_t)y <= (int64_t)x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_int8_shortcut) { +FOSSIL_TEST(cpp_assume_run_of_int8_shortcut) { int8_t x = 42; int8_t y = 20; @@ -115,7 +115,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_int8_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_I8((int8_t)y, (int8_t)x); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_int16_shortcut) { +FOSSIL_TEST(cpp_assume_run_of_int16_shortcut) { int16_t x = 42; int16_t y = 20; @@ -127,7 +127,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_int16_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_I16((int16_t)y, (int16_t)x); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_int32_shortcut) { +FOSSIL_TEST(cpp_assume_run_of_int32_shortcut) { int32_t x = 42; int32_t y = 20; @@ -139,7 +139,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_int32_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_I32((int32_t)y, (int32_t)x); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_int64_shortcut) { +FOSSIL_TEST(cpp_assume_run_of_int64_shortcut) { int64_t x = 42; int64_t y = 20; @@ -151,7 +151,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_int64_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_I64((int64_t)y, (int64_t)x); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_uint) { +FOSSIL_TEST(cpp_assume_run_of_uint) { unsigned int x = 42; unsigned int y = 20; @@ -163,7 +163,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_uint) { FOSSIL_TEST_ASSUME(y <= x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_uint8) { +FOSSIL_TEST(cpp_assume_run_of_uint8) { uint8_t x = 42; uint8_t y = 20; @@ -175,7 +175,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_uint8) { FOSSIL_TEST_ASSUME((uint8_t)y <= (uint8_t)x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_uint16) { +FOSSIL_TEST(cpp_assume_run_of_uint16) { uint16_t x = 42; uint16_t y = 20; @@ -187,7 +187,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_uint16) { FOSSIL_TEST_ASSUME((uint16_t)y <= (uint16_t)x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_uint32) { +FOSSIL_TEST(cpp_assume_run_of_uint32) { uint32_t x = 42; uint32_t y = 20; @@ -199,7 +199,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_uint32) { FOSSIL_TEST_ASSUME((uint32_t)y <= (uint32_t)x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_uint64) { +FOSSIL_TEST(cpp_assume_run_of_uint64) { uint64_t x = 42; uint64_t y = 20; @@ -211,7 +211,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_uint64) { FOSSIL_TEST_ASSUME((uint64_t)y <= (uint64_t)x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_uint8_shortcut) { +FOSSIL_TEST(cpp_assume_run_of_uint8_shortcut) { uint8_t x = 42; uint8_t y = 20; @@ -223,7 +223,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_uint8_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_U8((uint8_t)y, (uint8_t)x); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_uint16_shortcut) { +FOSSIL_TEST(cpp_assume_run_of_uint16_shortcut) { uint16_t x = 42; uint16_t y = 20; @@ -235,7 +235,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_uint16_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_U16((uint16_t)y, (uint16_t)x); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_uint32_shortcut) { +FOSSIL_TEST(cpp_assume_run_of_uint32_shortcut) { uint32_t x = 42; uint32_t y = 20; @@ -247,7 +247,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_uint32_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_U32((uint32_t)y, (uint32_t)x); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_uint64_shortcut) { +FOSSIL_TEST(cpp_assume_run_of_uint64_shortcut) { uint64_t x = 42; uint64_t y = 20; @@ -259,7 +259,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_uint64_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_U64((uint64_t)y, (uint64_t)x); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_hex) { +FOSSIL_TEST(cpp_assume_run_of_hex) { int x = 0x2A; // Hex for 42 int y = 0x14; // Hex for 20 @@ -271,7 +271,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_hex) { FOSSIL_TEST_ASSUME(y <= x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_hex8) { +FOSSIL_TEST(cpp_assume_run_of_hex8) { int x = 0x2A; // Hex for 42 int y = 0x14; // Hex for 20 @@ -283,7 +283,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_hex8) { FOSSIL_TEST_ASSUME(y <= x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_hex16) { +FOSSIL_TEST(cpp_assume_run_of_hex16) { int x = 0x2A; // Hex for 42 int y = 0x14; // Hex for 20 @@ -295,7 +295,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_hex16) { FOSSIL_TEST_ASSUME(y <= x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_hex32) { +FOSSIL_TEST(cpp_assume_run_of_hex32) { int x = 0x2A; // Hex for 42 int y = 0x14; // Hex for 20 @@ -307,7 +307,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_hex32) { FOSSIL_TEST_ASSUME(y <= x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_hex64) { +FOSSIL_TEST(cpp_assume_run_of_hex64) { int x = 0x2A; // Hex for 42 int y = 0x14; // Hex for 20 @@ -319,7 +319,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_hex64) { FOSSIL_TEST_ASSUME(y <= x, "Should have passed the test case"); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_hex8_shortcut) { +FOSSIL_TEST(cpp_assume_run_of_hex8_shortcut) { int x = 0x2A; // Hex for 42 int y = 0x14; // Hex for 20 @@ -331,7 +331,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_hex8_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_H8((int)y, (int)x); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_hex16_shortcut) { +FOSSIL_TEST(cpp_assume_run_of_hex16_shortcut) { int x = 0x2A; // Hex for 42 int y = 0x14; // Hex for 20 @@ -343,7 +343,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_hex16_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_H16((int)y, (int)x); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_hex32_shortcut) { +FOSSIL_TEST(cpp_assume_run_of_hex32_shortcut) { int x = 0x2A; // Hex for 42 int y = 0x14; // Hex for 20 @@ -355,7 +355,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_hex32_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_H32((int)y, (int)x); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_hex64_shortcut) { +FOSSIL_TEST(cpp_assume_run_of_hex64_shortcut) { int x = 0x2A; // Hex for 42 int y = 0x14; // Hex for 20 @@ -367,7 +367,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_hex64_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_H64((int)y, (int)x); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_octal8_shortcut) { +FOSSIL_TEST(cpp_assume_run_of_octal8_shortcut) { int8_t x = 042; // Octal for 42 int8_t y = 020; // Octal for 20 @@ -379,7 +379,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_octal8_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_O8((int8_t)y, (int8_t)x); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_octal16_shortcut) { +FOSSIL_TEST(cpp_assume_run_of_octal16_shortcut) { int16_t x = 042; // Octal for 42 int16_t y = 020; // Octal for 20 @@ -391,7 +391,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_octal16_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_O16((int16_t)y, (int16_t)x); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_octal32_shortcut) { +FOSSIL_TEST(cpp_assume_run_of_octal32_shortcut) { int32_t x = 042; // Octal for 42 int32_t y = 020; // Octal for 20 @@ -403,7 +403,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_octal32_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_O32((int32_t)y, (int32_t)x); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_octal64_shortcut) { +FOSSIL_TEST(cpp_assume_run_of_octal64_shortcut) { int64_t x = 042; // Octal for 42 int64_t y = 020; // Octal for 20 @@ -415,7 +415,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_octal64_shortcut) { ASSUME_ITS_LESS_OR_EQUAL_O64((int64_t)y, (int64_t)x); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_float32) { +FOSSIL_TEST(cpp_assume_run_of_float32) { float x = 42.0f; float y = 20.0f; @@ -427,7 +427,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_float32) { ASSUME_ITS_LESS_OR_EQUAL_F32(y, x); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_float64) { +FOSSIL_TEST(cpp_assume_run_of_float64) { double x = 42.0; double y = 20.0; @@ -439,7 +439,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_float64) { ASSUME_ITS_LESS_OR_EQUAL_F64(y, x); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_boolean_true) { +FOSSIL_TEST(cpp_assume_run_of_boolean_true) { bool x = true; bool y = false; @@ -450,7 +450,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_boolean_true) { ASSUME_NOT_FALSE(x); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_boolean_false) { +FOSSIL_TEST(cpp_assume_run_of_boolean_false) { bool x = false; bool y = true; @@ -461,7 +461,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_boolean_false) { ASSUME_NOT_TRUE(x); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_boolean_expression) { +FOSSIL_TEST(cpp_assume_run_of_boolean_expression) { int a = 5; int b = 10; @@ -472,7 +472,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_boolean_expression) { ASSUME_NOT_FALSE(a < b); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_boolean_expression_negation) { +FOSSIL_TEST(cpp_assume_run_of_boolean_expression_negation) { int a = 5; int b = 10; @@ -483,7 +483,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_boolean_expression_negation) { ASSUME_NOT_FALSE(!(a > b)); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_boolean_complex_expression) { +FOSSIL_TEST(cpp_assume_run_of_boolean_complex_expression) { int a = 5; int b = 10; int c = 15; @@ -495,7 +495,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_boolean_complex_expression) { ASSUME_NOT_FALSE((a < b) && (b < c)); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_boolean_complex_expression_negation) { +FOSSIL_TEST(cpp_assume_run_of_boolean_complex_expression_negation) { int a = 5; int b = 10; int c = 15; @@ -507,7 +507,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_boolean_complex_expression_negation) { ASSUME_NOT_FALSE(!((a > b) || (b > c))); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_null_pointer) { +FOSSIL_TEST(cpp_assume_run_of_null_pointer) { void *ptr = nullptr; // Test cases @@ -515,7 +515,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_null_pointer) { ASSUME_NOT_CNULL((void *)0x1); // Assuming a non-nullptr pointer } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_pointer_equality) { +FOSSIL_TEST(cpp_assume_run_of_pointer_equality) { int a = 42; int *ptr1 = &a; int *ptr2 = &a; @@ -526,7 +526,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_pointer_equality) { ASSUME_NOT_EQUAL_PTR(ptr1, ptr3); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_size_equality) { +FOSSIL_TEST(cpp_assume_run_of_size_equality) { size_t size1 = 42; size_t size2 = 42; size_t size3 = 20; @@ -536,7 +536,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_size_equality) { ASSUME_NOT_EQUAL_SIZE(size1, size3); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_size_comparison) { +FOSSIL_TEST(cpp_assume_run_of_size_comparison) { size_t size1 = 42; size_t size2 = 20; @@ -549,7 +549,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_size_comparison) { ASSUME_ITS_MORE_OR_EQUAL_SIZE(size1, size1); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_within_range) { +FOSSIL_TEST(cpp_assume_run_of_within_range) { int x = 42; int y = 20; @@ -558,7 +558,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_within_range) { ASSUME_NOT_WITHIN_RANGE(y, 21, 30); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_within_range_u8) { +FOSSIL_TEST(cpp_assume_run_of_within_range_u8) { uint8_t x = 42; uint8_t y = 20; @@ -567,7 +567,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_within_range_u8) { ASSUME_NOT_WITHIN_RANGE_U8(y, 21, 30); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_within_range_u16) { +FOSSIL_TEST(cpp_assume_run_of_within_range_u16) { uint16_t x = 42; uint16_t y = 20; @@ -576,7 +576,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_within_range_u16) { ASSUME_NOT_WITHIN_RANGE_U16(y, 21, 30); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_within_range_u32) { +FOSSIL_TEST(cpp_assume_run_of_within_range_u32) { uint32_t x = 42; uint32_t y = 20; @@ -585,7 +585,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_within_range_u32) { ASSUME_NOT_WITHIN_RANGE_U32(y, 21, 30); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_within_range_u64) { +FOSSIL_TEST(cpp_assume_run_of_within_range_u64) { uint64_t x = 42; uint64_t y = 20; @@ -594,7 +594,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_within_range_u64) { ASSUME_NOT_WITHIN_RANGE_U64(y, 21, 30); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_within_range_i8) { +FOSSIL_TEST(cpp_assume_run_of_within_range_i8) { int8_t x = 42; int8_t y = 20; @@ -603,7 +603,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_within_range_i8) { ASSUME_NOT_WITHIN_RANGE_I8(y, 21, 30); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_within_range_i16) { +FOSSIL_TEST(cpp_assume_run_of_within_range_i16) { int16_t x = 42; int16_t y = 20; @@ -612,7 +612,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_within_range_i16) { ASSUME_NOT_WITHIN_RANGE_I16(y, 21, 30); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_within_range_i32) { +FOSSIL_TEST(cpp_assume_run_of_within_range_i32) { int32_t x = 42; int32_t y = 20; @@ -621,7 +621,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_within_range_i32) { ASSUME_NOT_WITHIN_RANGE_I32(y, 21, 30); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_within_range_i64) { +FOSSIL_TEST(cpp_assume_run_of_within_range_i64) { int64_t x = 42; int64_t y = 20; @@ -630,7 +630,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_within_range_i64) { ASSUME_NOT_WITHIN_RANGE_I64(y, 21, 30); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_within_range_f32) { +FOSSIL_TEST(cpp_assume_run_of_within_range_f32) { float x = 42.0f; float y = 20.0f; @@ -639,7 +639,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_within_range_f32) { ASSUME_NOT_WITHIN_RANGE_F32(y, 21.0f, 30.0f); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_within_range_f64) { +FOSSIL_TEST(cpp_assume_run_of_within_range_f64) { double x = 42.0; double y = 20.0; @@ -648,7 +648,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_within_range_f64) { ASSUME_NOT_WITHIN_RANGE_F64(y, 21.0, 30.0); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_within_range_bchar) { +FOSSIL_TEST(cpp_assume_run_of_within_range_bchar) { uint8_t x = 42; uint8_t y = 20; @@ -657,7 +657,7 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_within_range_bchar) { ASSUME_NOT_WITHIN_RANGE_BCHAR(y, 21, 30); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_within_range_cchar) { +FOSSIL_TEST(cpp_assume_run_of_within_range_cchar) { char x = 'A'; char y = 'B'; @@ -666,27 +666,8 @@ FOSSIL_TEST_CASE(cpp_assume_run_of_within_range_cchar) { ASSUME_NOT_WITHIN_RANGE_CCHAR(y, 'C', 'Z'); } // end case -FOSSIL_TEST_CASE(cpp_assume_run_of_within_range_wchar) { - wchar_t x = L'A'; - wchar_t y = L'B'; - // Test cases - ASSUME_ITS_WITHIN_RANGE_WCHAR(x, L'A', L'Z'); - ASSUME_NOT_WITHIN_RANGE_WCHAR(y, L'C', L'Z'); -} // end case - -FOSSIL_TEST_CASE(cpp_assume_run_of_wstr) { - const wchar_t *str1 = L"Hello"; - const wchar_t *str2 = L"Hello"; - const wchar_t *str3 = L"World"; - - // Test cases - ASSUME_ITS_EQUAL_WSTR(str1, str2); - ASSUME_NOT_EQUAL_WSTR(str1, str3); - ASSUME_ITS_LENGTH_EQUAL_WSTR(str1, 5); -} // end case - -FOSSIL_TEST_CASE(cpp_assume_run_of_cstr) { +FOSSIL_TEST(cpp_assume_run_of_cstr) { std::string str1 = "Hello"; std::string str2 = "Hello"; std::string str3 = "World"; @@ -757,8 +738,6 @@ FOSSIL_TEST_GROUP(cpp_tdd_test_cases) { FOSSIL_TEST_ADD(cpp_tdd_suite, cpp_assume_run_of_within_range_f64); FOSSIL_TEST_ADD(cpp_tdd_suite, cpp_assume_run_of_within_range_bchar); FOSSIL_TEST_ADD(cpp_tdd_suite, cpp_assume_run_of_within_range_cchar); - FOSSIL_TEST_ADD(cpp_tdd_suite, cpp_assume_run_of_within_range_wchar); - FOSSIL_TEST_ADD(cpp_tdd_suite, cpp_assume_run_of_wstr); FOSSIL_TEST_ADD(cpp_tdd_suite, cpp_assume_run_of_cstr); FOSSIL_TEST_REGISTER(cpp_tdd_suite); diff --git a/code/tests/meson.build b/code/tests/meson.build index 2cea5d91..b17aabf6 100644 --- a/code/tests/meson.build +++ b/code/tests/meson.build @@ -2,14 +2,14 @@ if get_option('with_test').enabled() run_command(['python3', 'tools' / 'generate-runner.py'], check: true) test_c = ['unit_runner.c'] - test_cases = ['sample', 'bdd', 'tdd', 'ddd', 'mark', 'mock'] + test_cases = ['sample', 'bdd', 'tdd', 'ddd', 'mark', 'mock', 'sanity'] foreach cases : test_cases test_c += ['cases' / 'test_' + cases + '.c'] test_c += ['cases' / 'test_' + cases + '.cpp'] endforeach - pizza_c = executable('testbed-c', test_c, include_directories: dir, dependencies: [fossil_test_dep]) + pizza_c = executable('pizza', test_c, include_directories: dir, dependencies: [fossil_test_dep]) test('fossil testing C', pizza_c) endif \ No newline at end of file diff --git a/code/tests/tools/generate-runner.py b/code/tests/tools/generate-runner.py index 848b24e0..93d50daa 100644 --- a/code/tests/tools/generate-runner.py +++ b/code/tests/tools/generate-runner.py @@ -27,7 +27,7 @@ def generate_c_runner(self, test_groups): # Prepare header content for the test runner header = """ // Generated Fossil Logic Test Runner -#include +#include // * * * * * * * * * * * * * * * * * * * * * * * * // * Fossil Logic Test List @@ -54,9 +54,9 @@ def generate_c_runner(self, test_groups): # Complete with footer footer = """\n - FOSSIL_TEST_RUN(); - FOSSIL_TEST_SUMMARY(); - FOSSIL_TEST_END(); + FOSSIL_RUN_ALL(); + FOSSIL_SUMMARY(); + return FOSSIL_END(); } // end of main """ diff --git a/code/tests/tools/scan-code.py b/code/tests/tools/scan-code.py new file mode 100644 index 00000000..baf83d32 --- /dev/null +++ b/code/tests/tools/scan-code.py @@ -0,0 +1,57 @@ +import os +import re +import argparse + +# ANSI color codes +CYAN = '\033[96m' +BLUE = '\033[94m' +YELLOW = '\033[93m' +RED = '\033[91m' +RESET = '\033[0m' + +def check_format_issues(filepath): + issues = [] + + # Pattern: enforce * next to variable name, e.g., "int* ptr", not "int * ptr" + pointer_pattern = re.compile( + r'\b(?:const\s+|volatile\s+|restrict\s+)?' + r'(?:unsigned\s+|signed\s+)?' + r'(?:int|char|float|double|void|size_t|ptrdiff_t|uint\d+_t|int\d+_t)\s+\*\s+\w+' + ) + + with open(filepath, 'r', encoding='utf-8', errors='ignore') as file: + for lineno, line in enumerate(file, start=1): + if '\t' in line: + issues.append((lineno, 'Tab character found', 'warning')) + if 'goto' in line: + issues.append((lineno, 'Usage of "goto" is not allowed', 'error')) + if pointer_pattern.search(line): + issues.append((lineno, 'Pointer symbol (*) should be adjacent to variable name (e.g., `int* ptr`)', 'error')) + if line.strip().startswith('//') and len(line.strip()) > 80: + issues.append((lineno, 'Comment exceeds 80 characters', 'warning')) + if line.strip().endswith(';') and line.strip() == ';': + issues.append((lineno, 'Empty statement found', 'warning')) + return issues + +def scan_directory_for_issues(directory): + issue_report = {} + for root, _, files in os.walk(directory): + for file in files: + if file.endswith(('.c', '.cpp', '.h', '.hpp')): + full_path = os.path.join(root, file) + issues = check_format_issues(full_path) + if issues: + issue_report[full_path] = issues + return issue_report + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="Check C/C++ files for formatting issues.") + parser.add_argument('directory', help="Directory to scan for source files.") + args = parser.parse_args() + + report = scan_directory_for_issues(args.directory) + for filepath, issues in report.items(): + print(f"\n{BLUE}{filepath}:{RESET}") + for lineno, message, severity in issues: + color = RED if severity == 'error' else YELLOW + print(f" {CYAN}Line {lineno}:{RESET} {color}{message}{RESET}") diff --git a/code/tests/tools/scan-todos.py b/code/tests/tools/scan-todos.py new file mode 100644 index 00000000..14071b8c --- /dev/null +++ b/code/tests/tools/scan-todos.py @@ -0,0 +1,40 @@ +import os +import re +import argparse + +# ANSI escape codes for colors +BLUE = '\033[94m' +CYAN = '\033[96m' +RESET = '\033[0m' + +def find_keywords_in_file(filepath): + keyword_pattern = re.compile(r'//\s*(TODO|FIX|NODE)[:\s]*(.*)', re.IGNORECASE) + items = set() + with open(filepath, 'r', encoding='utf-8', errors='ignore') as file: + for line in file: + match = keyword_pattern.search(line) + if match: + keyword = match.group(1).upper() + text = match.group(2).strip() + if text: + items.add(f"{keyword}: {text}") + return items + +def find_keywords_in_directory(directory): + all_items = set() + for root, _, files in os.walk(directory): + for file in files: + if file.endswith(('.c', '.cpp', '.h', '.hpp')): + full_path = os.path.join(root, file) + all_items.update(find_keywords_in_file(full_path)) + return all_items + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="Extract unique TODOs, FIXes, and NODEs from C/C++ files.") + parser.add_argument('directory', help="Directory to scan for source files.") + args = parser.parse_args() + + items = find_keywords_in_directory(args.directory) + print(f"{BLUE}Extracted Items:{RESET}") + for item in sorted(items): + print(f"{CYAN}- [ ] {item}{RESET}") diff --git a/meson.build b/meson.build index 0c214dad..6a37e0b9 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ -project('Fossil Test', 'c', 'cpp', +project('Pizza Test', 'c', 'cpp', meson_version: '>=1.3.0', license: 'MPL-2.0', - version: '1.2.0', + version: '1.2.1', default_options: ['c_std=c11,c18', 'cpp_std=c++20']) subdir('code')